March 27, 2023
On Sunday, 26 March 2023 at 18:07:03 UTC, ryuukk_ wrote:
>
> It should be the opposite
>
> Slow code ugly
> Fast code beautiful
>

What's fast today may not be fast tomorrow but the language might still be relevant.

e.g.: It used to be faster to ...

- pre-calculate sin/cos tables, now the memory look up cost more cycles than the calculation itself

- use fixed point integer math, now every CPU has what used to be a floating point co-processor integrated

- only redraw the parts of the screen that changed, now the branching is slower than to  redraw everything
another example is sorting - Alexei wrote a blog post about how a stupid and slow sorting algorithm now performs better in multi threading. Maybe someone remembers the title/url of the post ?

And finally, beauty is in the eye of the beholder - meaning it's purely subjective.
March 27, 2023

On Sunday, 26 March 2023 at 18:07:03 UTC, ryuukk_ wrote:

>

shared is even more ugly since everything must be shared afterwards

The limitations of shared can be bypassed with a "function" that removes type qualifiers. return *cast(Unqual!T*) &foo(example, doesn't work as is for arrays.)

This way shared symbols can be used with functions that cannot be made compatible with both shared and non-shared.(by cannot the exact reasons seem obscure, it may be that a called function would support shared parameters but calls a function with the parameter that does not, ime this is painfully apparent with structs' member functions)

Naturally there's the risk of concurrency issues because this isn't the language documentation-recommended way of doing things, but D's library has these problems covered.

March 27, 2023

On Sunday, 26 March 2023 at 18:07:03 UTC, ryuukk_ wrote:

>

shared is even more ugly since everything must be shared afterwards

The limitations of shared can be bypassed with a "function" that removes type qualifiers. return *cast(Unqual!T*) &foo(example, doesn't work as is for arrays.)

This way shared symbols can be used with functions that cannot be made compatible with both shared and non-shared.(by cannot the exact reasons seem obscure, it may be that a called function would support shared parameters but calls a function with the parameter that does not, ime this is painfully apparent with structs' member functions)

Naturally there's the risk of concurrency issues because this isn't the language documentation-recommended way of doing things, but D's library has these problems covered.

March 27, 2023

On Sunday, 26 March 2023 at 18:07:03 UTC, ryuukk_ wrote:

>

shared is even more ugly since everything must be shared afterwards

The limitations of shared can be bypassed with a "function" that removes type qualifiers. return *cast(Unqual!T*) &foo(example, doesn't work as is for arrays.)

This way shared symbols can be used with functions that cannot be made compatible with both shared and non-shared.(by cannot the exact reasons seem obscure, it may be that a called function would support shared parameters but calls a function with the parameter that does not, ime this is painfully apparent with structs' member functions)

Naturally there's the risk of concurrency issues because this isn't the language documentation-recommended way of doing things, but D's library has these problems covered.

March 27, 2023

On Sunday, 26 March 2023 at 18:07:03 UTC, ryuukk_ wrote:

>

What i do not understand is the reasoning behind choosing tls global by default in D

Because the language maintainers decided that they want to emphasize the actor model with no default shared state in the language.

This is quite beneficial, BTW, as multiprocessor programming is by no means an easy problem, so the language just pushes you towards the error-free code by default.

As for __gshared, this is intended only for interfacing with C and other languages where global variables is the default way of doing things.

This may seem ugly, but if you write your program completely in D and want to avoid TLS, why don't you avoid globals at all?

Just allocate some state on the stack and pass it to your functions as ref.

This way it will actually be much cleaner and easy to understand and test, because global variables are tempting to be mutated, which makes the program control flow much less transparent.

March 27, 2023

On Sunday, 26 March 2023 at 18:07:03 UTC, ryuukk_ wrote:

>

Even C does it better: https://gcc.gnu.org/onlinedocs/gcc/Thread-Local.html

Honestly I find TLS-by-default to be a bad idea, it has become a trap to be avoided, and TLS does occasionally speed up things but it should be opt-in.

March 27, 2023

On 3/26/23 4:41 PM, ryuukk_ wrote:

>

On Sunday, 26 March 2023 at 19:08:32 UTC, Steven Schveighoffer wrote:

>

On 3/26/23 2:07 PM, ryuukk_ wrote:

>

Hi,

It's common knowledge that accessing tls global is slow http://david-grs.github.io/tls_performance_overhead_cost_linux/

What i do not understand is the reasoning behind choosing tls global by default in D

If you know a variable is not shared, then you know it can only be accessed from the current thread. This has huge implications for thread access.

However, one problem that D has no good solution is passing thread-local data to another thread (to be owned by the new thread, and no access in the current thread). That's typically the most common use case for "shared data".

I haven't explored this use case, that must explain why i find it surprising, however, i read about other languages too, and they seem to be explicit whenever they make use of TLS

C, C++, Rust, Zig, Go doesn't do TLS by default for example

If you want to have the type system tell you when a variable is readable from other threads or not, you need to have something to distinguish them. 99% of all variables are not readable from other threads, so making that the default seems the right call to me.

I don't know if you have thought through the implications. Start with the requirement that I need to know by examining the type whether the value is viewable from multiple threads. Is there a better mechanism with better defaults? C/C++ is precisely the wrong answer -- just let the programmer fend for himself. I haven't used the others to know what their story is.

One possibility is to REQUIRE you to mark module-level and static variables as one of __gshared, shared, immutable, or @tls. I would be OK with such a change.

I've run into a situation where I want to make a thread local pointer to shared data, and it's not easy. The only solution I could come up with is to put it inside a dummy struct with a shared member. Having a @tls storage class would help solve that.

-Steve

March 31, 2023

On Sunday, 26 March 2023 at 20:39:21 UTC, ryuukk_ wrote:

>

if my code doesn't do threads, why should i put my variable into TLS?

I don't think writing __gshared is a huge burden. You can use -vtls to print out all variables that are TLS, and add that to an automated test to check you don't have any accidentally. I have thought before that a --no-threads compiler switch that does not link the key thread functions but makes __gshared the default might be a good option for your use case. Then if you accidentally call some code that uses std.parallelism internally you would get a link error.

>

If i want fast code, why should i make use of ugly syntax?

By default D supports threads and accidental data races are far worse than ugly syntax.

March 31, 2023

On Sunday, 26 March 2023 at 20:39:21 UTC, ryuukk_ wrote:

>

if my code doesn't do threads, why should i put my variable into TLS?

I don't think writing __gshared is much of a burden. You can use -vtls to print out all variables that are TLS, and add that to an automated test to check you don't have any accidentally. I have thought before that a --no-threads compiler switch that does not link the key thread functions but makes __gshared the default might be a good enhancement for your use case. Then if you accidentally call some code that uses std.parallelism internally you would get a link error.

>

If i want fast code, why should i make use of ugly syntax?

  1. If you want fast code, why aren't you using threads?
  2. By default D supports threads and accidental data races are far worse than ugly syntax.
March 31, 2023
On Sunday, 26 March 2023 at 20:36:37 UTC, ryuukk_ wrote:
> Golang doesn't even have thread local storage, yet they do very well

Go doesn't have a solution to preventing data races at compile time, they just say don't share memory. But what if you accidentally share memory? That is *very* easy to do in Go. You and your users are out of luck. All you can do is run the race detector and pray that you happen to test all the code paths with it that might have data races:

> The race detector only finds races that happen at runtime, so it can't find races in code paths that are not executed

https://go.dev/doc/articles/race_detector