4

The documentation for Boost.Context in Boost v1.59 reports the following performance comparison results:

+----------+----------------------+-------------------+-------------------+----------------+
| Platform |      ucontext_t      |    fcontext_t     | execution_context | windows fibers |
+----------+----------------------+-------------------+-------------------+----------------+
| i386     | 708 ns / 754 cycles  | 37 ns / 37 cycles | ns / cycles       | ns / cycles    |
| x86_64   | 547 ns / 1433 cycles | 8 ns / 23 cycles  | 16 ns / 46 cycles | ns / cycles    |
+----------+----------------------+-------------------+-------------------+----------------+

[link]

I believe the source code for these experiments is hosted on GitHub.

My question is, why is the overhead for ucontext 20x higher than the Boost library's implementation? I can't see any obvious reason why there would be such a big difference. Is the Boost implementation using some low-level trick that the ucontext implementers missed, or is something else happening here?

DaoWen
  • 32,589
  • 6
  • 74
  • 101
  • 2
    @Olaf - I added the C tag because `ucontext` is implemented in C, not C++. Why did you remove it? – DaoWen Oct 25 '15 at 16:24
  • There is no code shown which supports this. Site-rules require to provide a self-contained question. Without a statement, one has to assume you are comparing apples with apples (C++ vs C++), not oranges. But after your comment, it looks like the latter (C vs C++ comparison). – too honest for this site Oct 25 '15 at 16:32
  • 1
    @Olaf - There's also no code to support that this is a C++ question—but you can infer that from the fact that I talk about Boost. Similarly, if you knew what ucontext is, then you'd be able to infer that this is also related to C. If you hover over the ucontext flag, you'd see it's a C library (ucontext.h)... – DaoWen Oct 25 '15 at 17:08

1 Answers1

7

The Boost documentation indicates why Boost.context is faster than the deprecated ucontext_t interfaces. In the Rationale section, you'll find this important note:

Note Context switches do not preserve the signal mask on UNIX systems.

and, in the comparison with makecontext in Other APIs:

ucontext_t preserves signal mask between context switches which involves system calls consuming a lot of CPU cycles.

As indicated, swapcontext does preserve the signal mask, which requires a syscall and all the overhead that entails. Since that was precisely the point of the ucontext_t functions, it cannot be described as an oversight. (If you don't want to preserve the signal mask, you can use setjmp and longjmp.)

By the way, the ucontext_t functions were deprecated in Posix edition 6 and removed in edition 7, because (1) the makecontextinterface requires an obsolescent feature of C, which is not available at all in C++; (2) the interfaces are rarely used; and (3) coroutines can be implemented using Posix threads. (See the note in Posix edition 6.) (Clearly, threads are not an ideal mechanism for implementing coroutines, but neither is an interface which relies on an obsolescent feature.)

rici
  • 234,347
  • 28
  • 237
  • 341
  • This is a really good observation (+1)! I was aware that it's technically deprecated from the POSIX standard, but since there's no replacement, it's still implemented on every platform I've checked. (The deprecation is actually why I was looking at Boost.Context as an alternative in the first place. I just wish it were implemented as a standalone C library...) – DaoWen Oct 25 '15 at 22:11
  • The linked note in Posix edition 6 is not saying that coroutines can be implemented using Posix threads, it is suggesting that developers just use Posix threads instead of coroutines entirely. Pretty disappointing that they chose to deprecate the whole interface rather than simply update it to use a `void*` like Posix threads do because "There are very few applications today that use the *context() routines" – Noam Bendelac May 11 '21 at 16:20
  • @noam: If you convert an coroutine-based application to use threads, then surely you've implemented your coroutines with threads, no? But it seems to me a minor semantic point. If it really bothers you, I'll change it. I'm not trying to justify the committee's actions, but I suspect that they removed the interfaces because they could not achieve consensus between implementations on how to fix them. It's tempting to say, "if I were a dictator, this is what I'd impose" :-), but Posix is an uneasy alliance, not a dictatorship, and that constrains its ability to be impositive. – rici May 11 '21 at 16:28
  • I was taking coroutines to mean specifically user-level non-preemptive threading, so 'if you've implemented your application with threads then you've replaced coroutines with threads', but I see how you just took it to mean a different thing. (idk if strictly speaking one of those meanings is more correct). Feel free to leave the answer as is, I was just disappointed because I interpreted your answer to mean there was a way to make user-level non-preemptive threads using Posix threads. – Noam Bendelac May 11 '21 at 16:57