Honorable (?) mention goes to (client-side) JavaScript - it's harder to have threading issues if you only have a single thread to work with!
"Linus Torvalds, though, argued that none of this effort was worth it. Speeding up random-number generation, beyond a point, is not the kernel's job, he said; instead, that should be left to the C library."
https://lwn.net/Articles/919008/
I've not been following the mailing lists so I don't know if that ever changed since then. The dev (Jason Donenfeld) seemed to think there was a way to get it working without the issues that blocked the improvement from getting merged last time though.
Also, if you feel that strongly, please don't roll your own crypto and use a CSPRNG.
you could pull different set lengths of pseudorandom numbers from the different seeds
https://hackage.haskell.org/package/MonadRandom-0.1.3/docs/C...
This is the rule you need to follow, but I don't understand what you're saying about global state.
The broken version has global state.
Non-broken versions can have global state or not have global state, depending on implementation choice. And that choice is separate from whether they have good performance.
"Give me a random number" feels like it shouldn't require all this overhead and hidden complexity under the hood. But unless you've read a lot about implementation details, or have been bitten by that exact problem before, how would you know? You wouldn't, of course.
If you call a function that calls a function that has side effects, or that closes the file descriptor you're using, or could panic, or that needs global shared state, or spins up a thread, or allocates, we don't have a good way to deal with that. There are some partial and underutilised solutions like monadic IO and linear/affine types and, uh, not having panics. I have some (bad) ideas but I think this is a space that's worth playing in.
Imagine if you could look at a function, and know, undeniably - this, and anything it can call, absolutely cannot alter the file system directly or make a network call or spawn a process. Maybe instead of just types, you need to supply capabilities (much like interfaces on classes). Sounds like it could be a real pain. Would make auditing easier though.
The libc was designed with heavy use of global variables in mind. Modules in C are designed as compilation units with static global variables and exposed functions that operate on them.
There's a reason many big projects (and programmers) use their own version of the standard library instead of libc.
It doesn't. The libc is the real problem. The libc and its global state. Even something as simple as errno creates global state problems just like this one. There are buffers all over the place.
Freestanding C is a better language just by virtue of not linking against libc.
Programmers now have done the impossible: written code so consistently bad that there are no hotspots, because the whole codebase is non-performant trash.
"Programmers waste enormous amounts of time thinking about, or worrying about, the speed of noncritical parts of their programs, and these attempts at efficiency actually have a strong negative impact when debugging and maintenance are considered. We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil. Yet we should not pass up our opportunities in that critical 3%." - Donald Knuth
(In practice, there is no way for a program to distinguish this from a single RNG that was initialized with a truly random seed that you can't recover, so it doesn't break backwards compatibility.)
Unfortunately, the C standard prescribes that failing to call `srand` must be equivalent to calling `srand(1)`, so I don't think glibc can get away with this trick.
Mentioned here: https://go.dev/blog/randv2
I think it could. Just supply two implementations of rand(): a strictly conforming one without this optimisation, and a less than strictly conforming one with it. The second is implemented in a function with a name like `__gnu_rand_optimized`, and then if you `#define __GNU_RAND_OPTIMIZED` before `#include <stdlib.h>`, then that header does `#define rand __gnu_rand_optimized`.
All the C standard requires is that you provide a mode of operation which strictly conforms to it. A platform is allowed to provide configuration options which cause it to be violated in various ways. Strictly conforming code won't turn on those options and so will work as the standard prescribes. If you choose to turn on such an option, that's a non-portable extension, and how it works is between you and the platform, the C standard isn't involved.
Or add a new function that provides behavior different from the existing one.
The latter is simpler. And safer. And more flexible. And faster, because you don't have a condition to check.
For a language that talks so much about simplicity, it puts a lot of effort into inventing clever wheels, with mixed results. I recently realized that SQL connections are thread safe and automagically pooled, I would have preferred more manual control. Don't get me started on date/time-formats.
My opinion is that the standard libc should be used much more as a fallback than a default.
And this is especially true about random number generation, there are much better generators out there, and some of them are only a few lines of code.
If the program was properly written there wouldn't be any issues with multithreading.
The title of the article gives the false impression that multithreading is slowing programs in the general case. In fact poorly written programs are slow and that is true for both multithreaded and singlethreaded programs.
If your program runs slow but you don't need it to run fast, don't bother.
If your program runs slow and you need it to run fast, go see what optimizations can be done.
From my experience, in the general case programs are poorly written, so yeah, multithreading makes them 1) more complex, more poorly written, and 2) slower in many cases.
So, me, being brand new to OS/2, immediately wanted to try out the new multi-threading features. Without knowing anything about locks and spins or deadlocks or shared state, I plunged into it just using the OS/2 documentation and a programming book I bought. Keep in mind, this was a single CPU machine, probably an Intel 486dx or something like that.
I spent the next couple of weeks debugging all sorts of things that should not have been happening. There were crashes, lock up, slow downs, missing variables, etc... that I couldn't resolve.
After a while, I gave up and did what the OP did: just put everything in a loop and it solved everything.
Now try a multi-thread program compute bound program with more threads than CPUs that does Rust vector "push" operations. Push causes an array to grow, locks get set, other threads hit locks, threads go into spinlock mode, threads inside spinlocks context switch, lose control, and performance drops by two orders of magnitude. Most CPU time is going into spinlocks.
I'm not convinced that user-space spinlocks are a good idea. If you're never compute-bound, they can work, but if you run out of CPU time and a context switch occurs inside a spinlock, it's all downhill from there.
Windows itself doesn't do this; it has a different locking strategy.
Make your program slower with threads - https://news.ycombinator.com/item?id=8711162 - Dec 2014 (47 comments)
"The random() function should not be used in multithreaded programs where reproducible behavior is required. Use random_r(3) for that purpose."
Perhaps the notes should be updated to state that the performance of multi-threaded programs is also affected due to the shared global resource?
However, the point of the article still stands.
I remember in the early 2000s doubling the performance of a CPU-bound C++ program by using two threads, and my boss being shocked that I was able to do it. He had written it off as a naive idea. I was too young and inexperienced to understand why he was surprised; to me it just seemed like he didn't understand what I was doing. In retrospect, now I feel like maybe he had the right default expectation, and I got lucky that it worked out so easily.