Getting the const-correctness of Object sorted once and for all
Alex Rønne Petersen
xtzgzorex at gmail.com
Mon May 14 02:28:38 PDT 2012
On 14-05-2012 08:37, Walter Bright wrote:
> On 5/13/2012 10:34 PM, Alex Rønne Petersen wrote:
>> I have yet to see any compiler make sensible use of the information
>> provided by
>> both C++'s const and D's const.
>
> D's const is part of purity, which is optimizable.
A function can still be weakly pure and operating on const arguments,
meaning you *still* have no opportunity for optimization. Strongly pure
functions are easily optimizable - because they operate on immutable
data (or, at least, data with shared indirection).
>
>> const in particular is completely useless to an optimizer because it
>> does not
>> give it any information that it can use for anything. The kind of
>> information
>> that an optimization pass, in general, wants to see is whether
>> something is
>> guaranteed to *never* change. const does not provide this information.
>> const
>> simply guarantees that the code working on the const data cannot alter
>> it (but
>> at the same time allows *other* code to alter it), which, as said, is
>> useless to
>> the optimizer.
>>
>> immutable is a different story. immutable actually opens the door to many
>> optimization opportunities exactly because the optimizer knows that
>> the data
>> will not be altered, ever. This allows it to (almost) arbitrarily
>> reorder code,
>> fold many computations at compile time, do conditional constant
>> propagation,
>> dead code elimination, ...
>
> You cannot have immutable without also having const. Or, at least, it
> would be impractical.
I agree entirely. I'm just saying that the way it is in the language
right now doesn't make it easy for the compiler to optimize for
immutable at all, due to how programmers tend to program against it.
>
>> This seems reasonable. But now consider that the majority of functions
>> *are
>> written for const, not immutable*. Thereby, you're throwing away the
>> immutable
>> guarantee, which is what the *compiler* (not the *programmer*) cares
>> about.
>> immutable is an excellent idea in theory, but in practice, it doesn't
>> help the
>> compiler because you'd have to either
>>
>> a) templatize all functions operating on const/immutable data so the
>> compiler
>> can retain the immutable guarantee when the input is such, or
>> b) explicitly duplicate code for the const and the immutable case.
>
> strings are immutable, not just const. It's been very successful.
And yet, the majority of functions operate on const(char)[], not
immutable(char)[], thereby removing the guarantee that string was
supposed to give about immutability.
>
>
>> Both approaches clearly suck. Templates don't play nice with
>> polymorphism, and
>> code duplication is...well...duplication. So, most of druntime and
>> phobos is
>> written for const because const is the bridge between the mutable and
>> immutable
>> world, and writing code against that rather than explicitly against
>> mutable/immutable data is just simpler. But this completely ruins any
>> opportunity the compiler has to optimize!
>
> That isn't true when it comes to purity.
I don't follow. Can you elaborate?
>
>
>> (An interesting fact is that even the compiler engineers working on
>> compilers
>> for strictly pure functional languages have yet to take full advantage
>> of the
>> potential that a pure, immutable world offers. If *they* haven't done
>> it yet, I
>> don't think we're going to do it for a long time to come.)
>
> It isn't just what the compiler can do, purity and immutability offer a
> means to prove things about code.
Absolutely, and I think that has significant value. Keep in mind that I
am only contesting the usefulness of const in terms of optimizations in
a normal compiler.
>
>> Now, you might argue that the compiler could simply say "okay, this
>> data is
>> const, which means it cannot be changed in this particular piece of
>> code and
>> thus nowhere else, since it is not explicitly shared, and therefore
>> not touched
>> by any other threads". This would be great if shared wasn't a complete
>> design
>> fallacy. Unfortunately, in most real world code, shared just doesn't
>> cut it, and
>> data is often shared between threads without using the shared qualifier
>> (__gshared is one example).
>
> Yes, if you're thinking like a C programmer!
Or if you're doing low-level thread programming (in my case, for a
virtual machine).
>
>
>> shared is another can of worms entirely. I can list a few initial
>> reasons why
>> it's unrealistic and impractical:
>>
>> 1) It is extremely x86-biased; implementing it on other architectures
>> is going
>> to be...interesting (read: on many architectures, impossible at ISA
>> level).
>
> I don't see why.
Some architectures with weak memory models just plain don't have fence
instructions.
>
>> 2) There is no bridge between shared and unshared like there is for
>> mutable and
>> immutable. This means that all code operating on shared data has to be
>> templatized (no, casts will not suffice; the compiler can't insert memory
>> barriers then) or code has to be explicitly duplicated for the shared and
>> unshared case. Funnily, the exact same issue mentioned above for const
>> and
>> immutable!
>
> Frankly, you're doing it wrong if you're doing more than trivial things
> with shared types. Running an algorithm on a shared type is just a bad
> idea.
So you're saying that casting shared away when dealing with
message-passing is the right thing to do? (Using immutable is not always
the answer...)
>
>> 3) It only provides documentation value. The low-level atomicity that
>> it is
>> supposed to provide (but doesn't yet...) is of extremely questionable
>> value. In
>> my experience, I never actually access shared data from multiple threads
>> simultaneously, but rather, transfer the data from one thread to
>> another and use
>> it exclusively in the other thread (i.e. handing over the ownership).
>> In such
>> scenarios, shared just adds overhead (memory barriers are Bad (TM) for
>> performance).
>
> Transferring data between threads should be done either using value
> types, which are copied, or references which are typed as shared only
> transitorially.
>
But with references come the issue that they are practically unusable
with shared, forcing one to cast it away. This should be a clear sign
that the feature is incomplete.
--
- Alex
More information about the Digitalmars-d
mailing list