Eliminate static shared this from std/parallelism.d#5470
Eliminate static shared this from std/parallelism.d#5470dlang-bot merged 3 commits intodlang:masterfrom
Conversation
a4b2323 to
8bfa663
Compare
8bfa663 to
7f2a8b0
Compare
|
Thanks for your pull request, @andralex! Bugzilla referencesYour PR doesn't reference any Bugzilla issue. If your PR contains non-trivial changes, please reference a Bugzilla issue or create a manual changelog. |
|
Maybe this is something I don't yet understand about D, but why the cast to |
|
I understand why we're getting rid of module constructors in general, but IMO this seems like one area where they are working as intended. Especially since the code is now more complex, and to get this work without constructors, an unsafe cast has to be used to lie to the user about the purity of the function. @schveiguy I'm curious about your opinion on the purity question, as you had a lot of insight during the |
7f2a8b0 to
d712d81
Compare
|
Updated the code with a reusable function that lazily initializes any atomically-loadable value. I plan to use that elsewhere and perhaps expose it publicly in the future. @JackStouffer module constructors do a fine job but the standard library undergoes extra demands. It's expected of us to wander off the beaten path in the implementation. |
|
cc @WalterBright @MartinNowak @CyberShadow need this reviewed because I can reuse it elsewhere - thx! |
std/parallelism.d
Outdated
| import core.sys.posix.unistd : _SC_NPROCESSORS_ONLN, sysconf; | ||
| totalCPUs = cast(uint) sysconf(_SC_NPROCESSORS_ONLN); | ||
| } | ||
| shared T result = outOfBandValue; |
std/parallelism.d
Outdated
| auto local = atomicLoad(result); | ||
| if (local != outOfBandValue) return local; | ||
| // Long path | ||
| local = (cast(size_t function() @nogc nothrow pure @trusted) &initializer)(); |
There was a problem hiding this comment.
You should check the type of initializer, because it may be delegate, a literal value, or any function pointer type pointing to a function taking one more arguments (which should be statically disallowed).
std/parallelism.d
Outdated
| { | ||
| return (cast(uint function() @nogc nothrow pure) | ||
| &totalCPUsImpl)(); | ||
| } |
There was a problem hiding this comment.
Can't you use lazilyInitializedConstant for totalCPUs?
d712d81 to
9408826
Compare
|
@ZombineDev updated |
| For that reason, no special precautions are taken so `initializer` may be called | ||
| more than one time leading to benign races on the cached value. | ||
|
|
||
| In the quiescent state the cost of the function is an atomic load from a global. |
There was a problem hiding this comment.
Need Params: and Returns: sections.
There was a problem hiding this comment.
function is private, but will do
std/parallelism.d
Outdated
| { | ||
| static shared T result = outOfBandValue; | ||
| // Short path | ||
| auto local = atomicLoad(result); |
std/parallelism.d
Outdated
| auto local = atomicLoad(result); | ||
| if (local != outOfBandValue) return local; | ||
| // Long path | ||
| local = initializer(); |
There was a problem hiding this comment.
Use different variable name here, as this use of local is completely independent of the previous one.
There was a problem hiding this comment.
not getting it... it's the same var
There was a problem hiding this comment.
Consider the trivial case:
int i = 4;
foo(i);
i = 5; // really the start of a different variable
foo(i);
By the idea that each variable should have the tightest scope it can, when a variable has multiple disjoint lifetimes it should be split into multiple independent variables. It also often enables a variable to become const.
There was a problem hiding this comment.
@WalterBright I understood the larger point you made with your first comment, but here local is and stays the local representation of the data meant to be cached throughout. We really are in good shape here.
std/parallelism.d
Outdated
| import core.cpuid : datacache; | ||
| size_t lineSize = 0; | ||
| foreach (cachelevel; datacache) | ||
| foreach (ref cachelevel; datacache) |
std/parallelism.d
Outdated
| uint totalCPUsImpl() @nogc nothrow | ||
| { | ||
| static shared uint result; | ||
| auto localResult = atomicLoad(result); |
std/parallelism.d
Outdated
| auto nameStr = "hw.ncpu\0".ptr; | ||
| } | ||
|
|
||
| localResult = 0; |
There was a problem hiding this comment.
Independent use of localResult should not reuse the name.
There was a problem hiding this comment.
it's in the same arc...
std/parallelism.d
Outdated
| } | ||
|
|
||
| localResult = 0; | ||
| size_t len = uint.sizeof; |
There was a problem hiding this comment.
Use const len instead of size_t len
There was a problem hiding this comment.
doesn't work, see signature: https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man3/sysctlbyname.3.html
std/parallelism.d
Outdated
| @property uint defaultPoolThreads() @trusted | ||
| { | ||
| return atomicLoad(_defaultPoolThreads); | ||
| auto local = atomicLoad(_defaultPoolThreads); |
|
As I recall, there was a correct way to do double checked locking using TLS variables. Perhaps this would be faster than going through |
|
@WalterBright an easy way to use tls for the fast path static nothrow T impl()
{
// as Andrei wrote it, reading/writing to global shared ...
}
static nothrow tlsCache()
{
static T tls = outOfBandValue;
if (tls == outOfBandValue)
tls = impl();
return tls;
} |
cf00ba7 to
f0c986f
Compare
f0c986f to
286930a
Compare
|
I updated the code to use a thread-local cache, starts to feel a bit overcooked. Anyhow it works nicely. |
|
This PR broke dustmite It looks like |
|
Oy what a bug that by definition can't be detected through unittesting :). |
Fix visibility issue for #5470 merged-on-behalf-of: Petar Kirov <ZombineDev@users.noreply.github.com>
|
That's the public alias to private symbol thing people mentioned recently. This will soon be fixed and should pass, atm. we still have the old (buggy) access checker running. |
Wondering how to get rid of the
shared static ~this. cc @dsimcha @wilzbach @quickfur @JackStouffer @MartinNowak