Struggling to implement parallel foreach...

Manu turkeyman at gmail.com
Tue Jun 18 00:30:00 UTC 2019


On Tue, Jun 18, 2019 at 10:00 AM Timon Gehr via Digitalmars-d
<digitalmars-d at puremagic.com> wrote:
>
> On 18.06.19 00:42, Manu wrote:
> > On Tue, Jun 18, 2019 at 5:40 AM Timon Gehr via Digitalmars-d
> > <digitalmars-d at puremagic.com> wrote:
> >>
> >> On 17.06.19 12:18, Nicholas Wilson wrote:
> >>>
> >>> That is all moot if mutable promotes to shared under "New Shared"™,
> >>
> >> I was discussing the current language. And even then, with old shared as
> >> well as new shared, my interpretation is the correct one.
> >>
> >> What "New Shared" it is actually trying to do is to introduce a new
> >> `threadsafe` qualifier. This doesn't conflict with `shared`. Roughly
> >> speaking, `threadsafe` is to unshared and `shared` what `const` is to
> >> mutable and `immutable`.
> >>
> >> threadsafe(int) <- inaccessible
> >> threadsafe(shared(int)) <- same as shared(int), accessible
> >
> > If this threadsafe concept is something that exists, I am not a party
> > to any such conversation.
> > ...
>
> I think this is what you wanted the `shared`-qualifier to mean. I think
> `threadsafe` is the canonical descriptor for the concept.

Okay. To be clear, I'm not talking about something else, I'm talking
about shared exclusively here.

> > There was some discussion about promotion possibility (but that never
> > talked about 'threadsafe'), and that was universally rejected.
> > ...
>
> A large part of the rejection was probably because `shared` is an
> existing qualifier and you argued as if it didn't already have a
> meaning. It's much easier to argue that a new primitive is useful if you
> don't need to argue at the same time that an existing primitive should
> be removed (people feel loss more strongly than gain ;)).
>
> I think it is more productive to keep the qualifiers separate for the
> time being and then at some point in the discussion decide that perhaps
> yes, we can drop `shared` as a qualifier and move a restricted version
> of it into druntime.
>
> > Making shared inhibit read/write needs to happen regardless of
> > anything else to work according to current rules.
> >
> >> The "New Shared" vision is to remove type qualifier support for `shared`
> >> and to instead move `shared` into druntime in a restricted form. (Last
> >> time I discussed this with Manu, I believe he was adamant that the
> >> language shouldn't distinguish between shared and unshared data at all,
> >> so we had a long unproductive debate.)
> >> Then `shared` is repurposed to mean something completely different.
> >
> > I don't know what you're talking about.
> > Shared needs to have read/write access removed... I feel like that was
> > universally agreed.
>
> threadsafe(unshared) needs to have read/write access removed
> (completely, UB if you access with casts).
>
> shared needs to have unsynchronized read/write access removed. There are
> multiple ways to go about this:
>
> 1. shared data cannot be accessed directly, all accesses need to go
> through special druntime functions.
>
> 2. direct accesses to shared data are atomic/sequentially consistent,
> other kinds of consistency guarantees are supported with special
> druntime functions.
>
> 3. direct accesses to shared data have low consistency guarantees (some
> sort of lowest-common-denominator of what's provided by weak memory
> models), if you want more consistency guarantees, you need to use
> special druntime functions.

1: Simplest and least opinionated approach.
2: Is only possible for exactly one type; `int`, and breaks down if
there are 2 int's. Anything else is #1 anyway. Just let the user call
atomicInc() for clarity rather than pretending `++` is safe.
3: What does that mean in practise? Any implementation where you can
access data members directly is no better than our naming conventions
in C++.

> Why is 1 better than 2 or 3? (I think Walter is actually leaning towards
> 2 at the moment.)

Start with 1, reach for 2 as an expansion. 1 is subset of 2.

> >>From there, I can get to work.
> >
> >>> I don't think anybody disagrees that qualified local functions should
> >>> work.
> >>
> >> Yes, they do. Manu said you shouldn't be able to call an
> >> `immutable`-qualified local function! This is extremely weird, because
> >> it is always valid to drop a context qualifier!
> >>
> >>> It would be useful to decouple this from the the current
> >>> discussion to avoid derailing.
> >>
> >> I think it's related to why Manu is so confused.
> >
> > I don't think I'm as confused as you'd like to think.
>
> I'd have liked to think you are exactly as confused as someone who
> argues that useful closure capturing behavior is a blocker for a
> parallel `foreach` implementation, but you have been slowly backing down
> on that claim, so it is indeed likely that you are less confused now
> than you seemed to be when I wrote that post. :)

I haven't backed down. Large elements of my suggestion have been
misunderstood (or were omitted from my suggestion).
I know I can't convince you, and I don't want to try. You have your
own ideas, I'll see where it goes.

> > I understand I'm proposing to obliterate a thing. It's a complex and surprising thing,
> > and as far as I can tell, it's useless, and only a point of friction
> > and bugs. Can you show how it's useful?
> > ...
>
> The language needs a way to tell what kind of semantics are expected for
> variable accesses. The usefulness is that you (and the compiler
> implementer) know which code is guaranteed to keep working across
> compiler versions and which code is not guaranteed to work and you just
> got lucky and it might break on the next compiler update, leaving you
> with some weird concurrency bug that only happens on Tuesdays in release
> builds.

I don't know what you're saying here.

> You absolutely need some notion of shared variable. Unshared is
> optional, but why pessimize thread-local code, after having gone through
> the effort to make globals thread-local by default? It is possible that
> a shared _qualifier_ is ultimately too expensive and it should be
> replaced by some _special_ druntime constructs (which also has
> non-trivial implications, and needs to be designed properly). The
> language will however in each case need know about shared and unshared
> memory locations.

Again, I have no idea what you're talking about here.

> > It took a mammoth effort to arrive at agreement that shared can't
> > read/write. That's uncontroversial, trivial to implement, and won't
> > break anything that's not already critically broken.
> > Can we start there?
> > ...
>
> See my last post. I don't think we are there yet. Walter is actually
> leaning towards allowing default access on `shared` variables with
> sequential consistency guarantees.

No such thing exists, unless there is exactly one integer.

> I don't think he is on board with the redefinition of `shared` to
> `threadsafe`. For `threadsafe`, default access on unshared data must
> obviously be disallowed. For `shared` this is less obvious if you don't
> already assume it has the new meaning.

We can drop this 'threadsafe' thing you have going on. I yielded on
that discussion months ago, we need shared to not be broken first.



More information about the Digitalmars-d mailing list