Re: std.functional.memoize : thread local or __gshared memoization?
On Tuesday, May 30, 2017 19:00:12 Kagamin via Digitalmars-d wrote: > On Saturday, 27 May 2017 at 16:27:46 UTC, Ola Fosheim Grøstad > > wrote: > > If the semantics in C is that everything is typed shared then > > it should also be treated as such when D interfaces with C and > > C like type-semantics. > > Then you would need to laboriously mark everything related to C > as shared, which would be quite different from C workflow. Everything is shared is C, but it's not marked as shared, so even though almost all of it is used as if it were thread-local, it isn't actually guaranteed to be so. This means that if you were being paranoid about it, you'd have to treat every C API as shared, but that is completely impractical and rarely fits what the C API actually does. In the vast majority of cases, if the C code were translated to D code, it would be translated as thread-local and be just fine. Well-behaved C code acts like D code in that it segregates code that operates on data as if it's on one thread and code that operates on data that's shared across threads, even if C doesn't have the helper attributes that D does. So, when dealing with a C API and shared, it's a bit like dealing with @system/@trusted and C APIs. With @system/@trusted, it's up to the programmer to figure out what's safe and what isn't based on what the API does. If it's appropriately memory-safe, then it's okay to mark it as @trusted, and if there's a problem, then you know that you need to dig into the C code to fix it. If it's not memory-safe, then it needs to be marked with @system (or nothing), and the programmer needs to make sure that they use it correctly in order to make the code that uses it memory-safe. With C APIs and shared, the programmer needs to be sure of whether it's thread-safe to treat that API call as if it's operating on thread-local data, or whether it needs to be treated as operating on shared data, and then mutexes need to be used as appropriate. Fortunately, it's usually clear, and in the vast majority of cases, treating the C function as operating on thread-local data is just fine. But it is true that you need to be a bit careful when binding to C APIs to make sure that something which isn't thread-safe is not treated as thread-local. - Jonathan M Davis
Re: std.functional.memoize : thread local or __gshared memoization?
On Saturday, 27 May 2017 at 16:27:46 UTC, Ola Fosheim Grøstad wrote: If the semantics in C is that everything is typed shared then it should also be treated as such when D interfaces with C and C like type-semantics. Then you would need to laboriously mark everything related to C as shared, which would be quite different from C workflow.
Re: std.functional.memoize : thread local or __gshared memoization?
On Saturday, 27 May 2017 at 16:19:06 UTC, Kagamin wrote: On Saturday, 27 May 2017 at 10:29:05 UTC, Ola Fosheim Grøstad wrote: Hm, I would think that using __gshared would not be affected by compiler improvements, since it would turn off optimizations that assume that the variable doesn't change between reads? Like volatile? Volatile doesn't work. Volatile is different. Volatile means there can be side-effects from reads/writes, so the compiler cannot optimize out writes. This is for hardware registers. __gshared has purpose to behave like old good global variable simply to provide a low level feature for a system language, if you need something else, then use the right tool. Doesn't make much sense to me. If the semantics in C is that everything is typed shared then it should also be treated as such when D interfaces with C and C like type-semantics.
Re: std.functional.memoize : thread local or __gshared memoization?
On Saturday, 27 May 2017 at 10:29:05 UTC, Ola Fosheim Grøstad wrote: Hm, I would think that using __gshared would not be affected by compiler improvements, since it would turn off optimizations that assume that the variable doesn't change between reads? Like volatile? Volatile doesn't work. Why would you want that? __gshared has purpose to behave like old good global variable simply to provide a low level feature for a system language, if you need something else, then use the right tool.
Re: std.functional.memoize : thread local or __gshared memoization?
On Saturday, 27 May 2017 at 10:02:35 UTC, Jonathan M Davis wrote: Actually, not so much, because in D, most variables are _not_ shared across threads (unlike in C). If you don't share data, you don't need to worry how to share data. But if you do, D supports C model. The compiler is free to assume that if a variable is not marked as shared, it's thread-local, and it can optimize accordingly. Just like in C. Using __gshared on anything other than C globals is just begging for trouble. Just like in C. As such, I'd strongly argue that using __gshared with anything other than actual C globals is a serious code smell, and it's a practice that should be actively discouraged. You can't just discourage to write code. What to do instead, open a bakery?
Re: std.functional.memoize : thread local or __gshared memoization?
On Saturday, 27 May 2017 at 11:52:04 UTC, John Colvin wrote: Nope. __gshared isn't even part of the type so it can't do that. __gshared int a; void main() { foo(a); But this should fail since foo doesn't take shared? __gshared is a way of declaring a C-like global variable, but D does not - in general - support accessing it in a C-like way. My advice is to cast to shared before use, unless you're totally sure you know what other threads will be doing. Why isn't it always typed as shared?
Re: std.functional.memoize : thread local or __gshared memoization?
On Saturday, 27 May 2017 at 10:29:05 UTC, Ola Fosheim Grøstad wrote: On Saturday, 27 May 2017 at 10:02:35 UTC, Jonathan M Davis wrote: As such, the fact that D programmers frequently decide to use __gshared in order to avoid dealing with the restrictions with shared is actually extremely bad. You can get away with it in some cases, but it's error-prone and is only going to get worse as the compiler improves. Hm, I would think that using __gshared would not be affected by compiler improvements, since it would turn off optimizations that assume that the variable doesn't change between reads? Nope. __gshared isn't even part of the type so it can't do that. __gshared int a; void main() { foo(a); } void foo(ref int b) { // No way of knowing that b actually // references __gshared memory. } __gshared is a way of declaring a C-like global variable, but D does not - in general - support accessing it in a C-like way. My advice is to cast to shared before use, unless you're totally sure you know what other threads will be doing.
Re: std.functional.memoize : thread local or __gshared memoization?
On Saturday, 27 May 2017 at 10:02:35 UTC, Jonathan M Davis wrote: As such, the fact that D programmers frequently decide to use __gshared in order to avoid dealing with the restrictions with shared is actually extremely bad. You can get away with it in some cases, but it's error-prone and is only going to get worse as the compiler improves. Hm, I would think that using __gshared would not be affected by compiler improvements, since it would turn off optimizations that assume that the variable doesn't change between reads?
Re: std.functional.memoize : thread local or __gshared memoization?
On Saturday, May 27, 2017 07:20:10 Kagamin via Digitalmars-d wrote: > On Thursday, 25 May 2017 at 14:56:25 UTC, Jonathan M Davis wrote: > > But we do need to get this ironed out well enough that we can > > definitely tell folks that shared is what it's going to be so > > that they'll stop using __gshared all over the place to try and > > work around shared - or at least if they do so, they won't be > > able to do so with the excuse that shared is not complete. > > People don't have to use shared, D supports old good "anything > can be shared" C model, if people think C model is good enough > for them, they can use it. Actually, not so much, because in D, most variables are _not_ shared across threads (unlike in C). The compiler is free to assume that if a variable is not marked as shared, it's thread-local, and it can optimize accordingly. Using __gshared on anything other than C globals is just begging for trouble. As such, the fact that D programmers frequently decide to use __gshared in order to avoid dealing with the restrictions with shared is actually extremely bad. You can get away with it in some cases, but it's error-prone and is only going to get worse as the compiler improves. As such, I'd strongly argue that using __gshared with anything other than actual C globals is a serious code smell, and it's a practice that should be actively discouraged. - Jonathan M Davis
Re: std.functional.memoize : thread local or __gshared memoization?
On Thursday, 25 May 2017 at 14:56:25 UTC, Jonathan M Davis wrote: But we do need to get this ironed out well enough that we can definitely tell folks that shared is what it's going to be so that they'll stop using __gshared all over the place to try and work around shared - or at least if they do so, they won't be able to do so with the excuse that shared is not complete. People don't have to use shared, D supports old good "anything can be shared" C model, if people think C model is good enough for them, they can use it.
Re: std.functional.memoize : thread local or __gshared memoization?
On Wednesday, 24 May 2017 at 23:56:49 UTC, Timothee Cour wrote: I could look at source to figure it out but others might wonder and I couldn't find it in the docs in https://dlang.org/library/std/functional/memoize.html whether memoize works per thread (thread local) or globally (__gshared) I checked the source, and it's thread-local. (Specifically, it's declared as 'static'.)
Re: std.functional.memoize : thread local or __gshared memoization?
On Thursday, 25 May 2017 at 20:43:36 UTC, Jonathan M Davis wrote: complication to the language. Certainly, from what I know of Rust, it's far more complicated because of that sort of thing, and glancing over that link on Pony, it looks like it's getting a fair bit of complication as well in order to deal with the problem. I think Pony uses a GC (also to collect dead threads/actors). But I have found that trying to understand their model to be a good exercise for thinking about where problems can arise and what it takes to resolve it through a typesystem. stuff with regards to threads to shared, which is great, but dealing with the stuff that involves sharing across threads then requires that the programmer be much more careful than would be the case if the type system were actually helping you beyond preventing you from doing stuff that isn't thread safe to a shared object without casting it to thread-local first. Yes, that transition to/from shared is problematic. There are ways to deal with it, proving concurrency patterns to be correct, but it takes even more machinery than Pony/Rust. I don't know what the right answer is. Rust seems to get both praise and complaints about its approach - as do we for ours. But both approaches are safer than what you get with C/C++. Depends on what you do in C++. If you only share though a ready-made framework, then you probably can do quite well in terms of safety. With a small performance cost. If you want max performance in the general case then I think all these languages will have safety troubles. Because you need proper full-blown verification then...
Re: std.functional.memoize : thread local or __gshared memoization?
On Thursday, 25 May 2017 at 11:15:19 UTC, Stanislav Blinov wrote: Count me const scope. https://www.youtube.com/watch?v=YIp-0V6YKfQ
Re: std.functional.memoize : thread local or __gshared memoization?
On Thursday, May 25, 2017 19:57:17 Ola Fosheim Grøstad via Digitalmars-d wrote: > On Thursday, 25 May 2017 at 14:56:25 UTC, Jonathan M Davis wrote: > > able to do so with the excuse that shared is not complete. But > > the idea that you do almost nothing with an object that is > > shared without casting it first (after protecting it > > appropriately with a mutex of course) except for using atomics > > seems to be too much for many folks, even though aside from the > > cast, the way you actually use a shared object is basically > > what you'd do in C/C++. I'd say that ultimately, shared is more > > for storing the object and protecting it against operations > > that could operate on it incorrectly than it is for actually > > operating on the object. > > You should look at Pony which has a sound type system for > transitioning objects read/write local/shared: > > https://tutorial.ponylang.org/capabilities/reference-capabilities.html > > It is kinda like Rust, but more advanced. Oh, having some sort of ownership model or something similar in the type system which made it possible to know when something was only referenced once on a thread and thus could be passed safely to another and things like that would be very cool. For instance, it would make std.concurrency far easier to use safely with mutable data. The problem is that it seems that in order to for that to be possible, it adds quite a bit of complication to the language. Certainly, from what I know of Rust, it's far more complicated because of that sort of thing, and glancing over that link on Pony, it looks like it's getting a fair bit of complication as well in order to deal with the problem. With D, having shared (and thus having everything else be thread-local) simplifies a lot of stuff, leaving the nasty stuff with regards to threads to shared, which is great, but dealing with the stuff that involves sharing across threads then requires that the programmer be much more careful than would be the case if the type system were actually helping you beyond preventing you from doing stuff that isn't thread safe to a shared object without casting it to thread-local first. So, we're stuck with an annoying area that would be great to clean up, but cleaning it up seems like it requires complicating everything else... I don't know what the right answer is. Rust seems to get both praise and complaints about its approach - as do we for ours. But both approaches are safer than what you get with C/C++. - Jonathan M Davis
Re: std.functional.memoize : thread local or __gshared memoization?
On Thursday, 25 May 2017 at 14:56:25 UTC, Jonathan M Davis wrote: able to do so with the excuse that shared is not complete. But the idea that you do almost nothing with an object that is shared without casting it first (after protecting it appropriately with a mutex of course) except for using atomics seems to be too much for many folks, even though aside from the cast, the way you actually use a shared object is basically what you'd do in C/C++. I'd say that ultimately, shared is more for storing the object and protecting it against operations that could operate on it incorrectly than it is for actually operating on the object. You should look at Pony which has a sound type system for transitioning objects read/write local/shared: https://tutorial.ponylang.org/capabilities/reference-capabilities.html It is kinda like Rust, but more advanced.
Re: std.functional.memoize : thread local or __gshared memoization?
On Thursday, May 25, 2017 10:41:23 Andrei Alexandrescu via Digitalmars-d wrote: > On Thursday, 25 May 2017 at 03:51:05 UTC, Stanislav Blinov wrote: > > On Thursday, 25 May 2017 at 01:17:41 UTC, Timothee Cour wrote: > >> thanks; i think docs for this should still make that clear. > >> > >> How about adding memoizeShared for shared variables? > >> There definitely are use cases for this. > > > > Perhaps we should first actually properly document and > > implement what shared *is*. > > I'm looking into creating a small interest group composed of > experts to lead this effort. We'd put a DIP together that nails > down shared. Who is interested? -- Andrei I would be, though it is one of those topics where you think that you know what you're doing, and then you find out about some compiler optimization or other low level item that you don't know about, and some of what you thought was true wasn't. So, this is one area where I'm not sure that I'd ever be willing to call myself an expert no much how much I knew. But we do need to get this ironed out well enough that we can definitely tell folks that shared is what it's going to be so that they'll stop using __gshared all over the place to try and work around shared - or at least if they do so, they won't be able to do so with the excuse that shared is not complete. But the idea that you do almost nothing with an object that is shared without casting it first (after protecting it appropriately with a mutex of course) except for using atomics seems to be too much for many folks, even though aside from the cast, the way you actually use a shared object is basically what you'd do in C/C++. I'd say that ultimately, shared is more for storing the object and protecting it against operations that could operate on it incorrectly than it is for actually operating on the object. Unfortunately, synchronized classes are the only construct that we've come up with that allows for the locking and casting to to be done safely and automatically rather than requiring an explicit cast, and not only do we not have synchronized classes yet, but even if we did they would only work on the outer layer of shared, so in many cases, you'd be forced to cast anyway. I think that ultimately the big problems that we need to solve are making it clear what shared is and how you use it and ensuring that we have the memory model stuff ironed out well enough that the required casting and other operations are actually guaranteed to do what they're supposed to do. Then it's an issue of educating folks about shared rather than needing to fix anything about it. I don't expect that shared will never be entirely pretty, but ultimately, it's a lot like @safe/@system/@trusted in that it allows you to segregate the code that you need to worry about for a particular class of problem - and ultimately, the biggest benefit of shared is that everything else is thread-local. - Jonathan M Davis
Re: std.functional.memoize : thread local or __gshared memoization?
On Thursday, 25 May 2017 at 10:41:23 UTC, Andrei Alexandrescu wrote: On Thursday, 25 May 2017 at 03:51:05 UTC, Stanislav Blinov wrote: On Thursday, 25 May 2017 at 01:17:41 UTC, Timothee Cour wrote: thanks; i think docs for this should still make that clear. How about adding memoizeShared for shared variables? There definitely are use cases for this. Perhaps we should first actually properly document and implement what shared *is*. I'm looking into creating a small interest group composed of experts to lead this effort. We'd put a DIP together that nails down shared. Who is interested? -- Andrei Count me const scope.
Re: std.functional.memoize : thread local or __gshared memoization?
On Thursday, 25 May 2017 at 03:51:05 UTC, Stanislav Blinov wrote: On Thursday, 25 May 2017 at 01:17:41 UTC, Timothee Cour wrote: thanks; i think docs for this should still make that clear. How about adding memoizeShared for shared variables? There definitely are use cases for this. Perhaps we should first actually properly document and implement what shared *is*. I'm looking into creating a small interest group composed of experts to lead this effort. We'd put a DIP together that nails down shared. Who is interested? -- Andrei
Re: std.functional.memoize : thread local or __gshared memoization?
On Thursday, 25 May 2017 at 01:17:41 UTC, Timothee Cour wrote: thanks; i think docs for this should still make that clear. How about adding memoizeShared for shared variables? There definitely are use cases for this. Perhaps we should first actually properly document and implement what shared *is*.
Re: std.functional.memoize : thread local or __gshared memoization?
thanks; i think docs for this should still make that clear. How about adding memoizeShared for shared variables? There definitely are use cases for this. On Wed, May 24, 2017 at 5:19 PM, Jonathan M Davis via Digitalmars-dwrote: > On Wednesday, May 24, 2017 16:56:49 Timothee Cour via Digitalmars-d wrote: >> I could look at source to figure it out but others might wonder and I >> couldn't find it in the docs in >> https://dlang.org/library/std/functional/memoize.html whether memoize >> works per thread (thread local) or globally (__gshared) > > Definitely thread-local, and there would be problems if it were shared, > since for that to work properly, it would really need to be given either > shared data or data that implicitly converted to shared. And while __gshared > might skirt the compiler yelling at you, it would have the same issues; it's > just that the compiler wouldn't be yelling at you about them, so it would be > harder to catch them and more likely that you'd get weird, > hard-to-track-down bugs. > > - Jonathan M Davis >
Re: std.functional.memoize : thread local or __gshared memoization?
On Wednesday, May 24, 2017 16:56:49 Timothee Cour via Digitalmars-d wrote: > I could look at source to figure it out but others might wonder and I > couldn't find it in the docs in > https://dlang.org/library/std/functional/memoize.html whether memoize > works per thread (thread local) or globally (__gshared) Definitely thread-local, and there would be problems if it were shared, since for that to work properly, it would really need to be given either shared data or data that implicitly converted to shared. And while __gshared might skirt the compiler yelling at you, it would have the same issues; it's just that the compiler wouldn't be yelling at you about them, so it would be harder to catch them and more likely that you'd get weird, hard-to-track-down bugs. - Jonathan M Davis
std.functional.memoize : thread local or __gshared memoization?
I could look at source to figure it out but others might wonder and I couldn't find it in the docs in https://dlang.org/library/std/functional/memoize.html whether memoize works per thread (thread local) or globally (__gshared)