April 22, 2015
On 4/22/2015 1:36 PM, John Colvin wrote:
> Is it even possible to contrive a case where
> 1) The default initialisation stores are technically dead and
> 2) Modern compilers can't tell they are dead and elide them and
> 3) Doing the initialisation has a significant performance impact?

And you can still initialize with '= void'.

April 22, 2015
On Wednesday, 22 April 2015 at 22:41:33 UTC, Walter Bright wrote:
> And you can still initialize with '= void'.

This was my tip of the week two TWID's ago!
http://arsdnet.net/this-week-in-d/apr-12.html
April 22, 2015
On 4/22/2015 3:52 PM, Adam D. Ruppe wrote:
> On Wednesday, 22 April 2015 at 22:41:33 UTC, Walter Bright wrote:
>> And you can still initialize with '= void'.
>
> This was my tip of the week two TWID's ago!
> http://arsdnet.net/this-week-in-d/apr-12.html

Yup!
April 23, 2015
On Wednesday, 22 April 2015 at 20:36:12 UTC, John Colvin wrote:
> Is it even possible to contrive a case where
> 1) The default initialisation stores are technically dead and
> 2) Modern compilers can't tell they are dead and elide them and
> 3) Doing the initialisation has a significant performance impact?
>
> The boring example is "extra code causes instruction cache misses".

I'd say it is very unlikely. If the compiler wan't see it, then it means the code is non trivial, and if it is non trivial, it is not an extra store that is going to make any difference.
April 23, 2015
On Thursday, 23 April 2015 at 01:45:14 UTC, deadalnix wrote:
> On Wednesday, 22 April 2015 at 20:36:12 UTC, John Colvin wrote:
>> Is it even possible to contrive a case where
>> 1) The default initialisation stores are technically dead and
>> 2) Modern compilers can't tell they are dead and elide them and
>> 3) Doing the initialisation has a significant performance impact?
>>
>> The boring example is "extra code causes instruction cache misses".
>
> I'd say it is very unlikely. If the compiler wan't see it, then it means the code is non trivial, and if it is non trivial, it is not an extra store that is going to make any difference.

This was my thinking. I guess you could have something like this:

extern(C) void myCheapInitialiser(float*, size_t);
float[256] a;
myCheapInitialiser(a.ptr, a.length);
sort(a);
writeln(a.stride(2).sum());
April 23, 2015
> On Wednesday, 22 April 2015 at 20:29:49 UTC, Walter Bright wrote:
>>
>> On 4/22/2015 12:51 PM, ponce wrote:
>>>
>>> I didn't appreciate how important default initialization was before
>>> having to
>>> fix a non-deterministic, release-only, time-dependent bug in a video
>>> encoder
>>> some months ago. Just because of 2 uninitialized variables (C++ doesn't
>>> require
>>> member initialization in constructor). If one of them was _exactly equal
>>> to 1_
>>> by virtue of randomness, then it would perform from 0 to 2 billions of
>>> motion
>>> estimation steps, which is very slow but not a total halt. A watchdog
>>> mechanism
>>> would detect this and reboot, hence labelling the bug "a deadlock". It
>>> would
>>> disappear in debug mode since variables would be initialized then.
>>
>>
>> The default initialization comes from bitter personal experience, much like yours!
>>
>>
>>> That gives a totally other meaning to "zero cost abstractions" since in
>>> three
>>> weeks of investigation I could have speed-up the program by ~5%, much
>>> more than
>>> the supposed slowdown of variable initialization.
>>
>>
>> Most of the implicit initializations become "dead stores" and are removed anyway by the optimizer.

Right, on simple types (scalars, pointers) which have a trivial initialisation, the compiler is able to track their value until it is read and use DCE to remove previous initialisations up to that point.

(Contrived) Examples:

void dce1()
{
    int dead_int;
    printf("Dead Int\n");
}

void dce2()
{
    int inited_later;
    int dead_int;
    inited_later = 42;
    printf("Initialized Int = %d\n", inited_later);
}

---

gdc -O -fdump-tree-optimized=stderr dce.d

dce1 ()
{
  __builtin_puts (&"Dead Int"[0]);
  return;
}

dce2 ()
{
  __builtin_printf ("Initialized Int = %d\n", 42);
  return;
}

---

As pointed out, there are limitations when it comes to types which have a complex initialiser.

On 22 April 2015 at 22:36, John Colvin via Digitalmars-d <digitalmars-d@puremagic.com> wrote:
>
> Is it even possible to contrive a case where
> 1) The default initialisation stores are technically dead and

int arrayInit()
{
    return 0xdeadbeef;
}

void main()
{
    int[ushort.max] dead_array = arrayInit();
    printf("Dead Array\n");
}

> 2) Modern compilers can't tell they are dead and elide them and

Actually - gdc can DCE the array initialisation at -O3, but I'd like to do better than that in future...

gdc -O3 -fdump-tree-optimized=stderr dce.d
D main ()
{
  unsigned int ivtmp.20;
  int dead_array[65535];
  unsigned int _1;
  void * _13;

  <bb 2>:
  # DEBUG D.3569 => &dead_array
  # DEBUG D.3570 => 65535
  ivtmp.20_12 = (unsigned int) &dead_array;
  _1 = (unsigned int) &MEM[(void *)&dead_array + 262128B];

  <bb 3>:
  # ivtmp.20_16 = PHI <ivtmp.20_3(3), ivtmp.20_12(2)>
  _13 = (void *) ivtmp.20_16;
  MEM[base: _13, offset: 0B] = { -559038737, -559038737, -559038737,
-559038737 };
  ivtmp.20_3 = ivtmp.20_16 + 16;
  if (_1 == ivtmp.20_3)
    goto <bb 4>;
  else
    goto <bb 3>;

  <bb 4>:
  __builtin_puts (&"Dead Array"[0]);
  dead_array ={v} {CLOBBER};
  return 0;

}

> 3) Doing the initialisation has a significant performance impact?

Short answer, no.

time ./a.out
Dead Array

real    0m0.001s
user    0m0.000s
sys    0m0.001s


Worded answer, static array overflow analysis means that you can't get an array much higher than 1M, and the compiler can quite happily vectorised the initialisation process for you so there's fewer loops to go round.  However if there was a 'lazy + impure' initialiser, that would be a different story.  :-)

Iain.
April 23, 2015
Walter Bright:

> On 4/22/2015 2:58 PM, bearophile wrote:
>> D is less stack-friendly than Ada (and probably Rust too),
>
> ??

In Ada standard library you have safe fixed-size stack-allocated associative arrays. In D you can't even allocate safely a dynamically-sized 1D array on the stack, and forget about doing it for 2D. Enough said.

Bye,
bearophile
April 23, 2015
On Wednesday, 22 April 2015 at 22:26:45 UTC, John Colvin wrote:
> On Wednesday, 22 April 2015 at 21:59:48 UTC, Ola Fosheim Grøstad wrote:
>> On Wednesday, 22 April 2015 at 20:36:12 UTC, John Colvin wrote:
>>> Is it even possible to contrive a case where
>>> 1) The default initialisation stores are technically dead and
>>> 2) Modern compilers can't tell they are dead and elide them and
>>> 3) Doing the initialisation has a significant performance impact?
>>>
>>> The boring example is "extra code causes instruction cache misses".
>>
>> Allocation of large arrays.
>
> That doesn't really answer the question without some more context.

I think it does.

Compilers cannot tell what goes on because they cannot figure out nontrivial loop invariants without guidance. You need something like a theorem prover (coq?)...

Compilers may not be about to tell arrays won't be touched because of memory barriers when calling external functions, so they have to complete initialization before that. Presumably a Rust compiler could do better...

>Can you give a specific example where all 3 points are
> satisfied?

Not sure why you would need it, plenty of cases where compilers will fail. E.g. queues between threads (like real time threads) where you allocate in one thread and fill out data in another thread.

Any preallocation done on large data structures or frequently reinitialized data structures may perform better without explicit initialization.

For a system level language I think it would be better to verify that you don't use noninitialized memory using a theorem prover (sanitizer) or to use guards (like NaN). Automatic initialization is also a source of bugs.

April 23, 2015
On Thursday, 23 April 2015 at 14:29:01 UTC, Ola Fosheim Grøstad wrote:
>>Can you give a specific example where all 3 points are
>> satisfied?
>
> Not sure why you would need it, plenty of cases where compilers will fail. E.g. queues between threads (like real time threads) where you allocate in one thread and fill out data in another thread.
>
> Any preallocation done on large data structures or frequently reinitialized data structures may perform better without explicit initialization.

Yes, there are times the compiler can't optimise the dead stores away. Obviously these dead stores are not free. What I don't see is a good example of when that cost matters.

There are cases where you might really need to grab an extra 1-5%, at which point you are hand optimising and = void is a reasonable tool.
April 23, 2015
On 23 April 2015 at 16:28, via Digitalmars-d <digitalmars-d@puremagic.com> wrote:
> On Wednesday, 22 April 2015 at 22:26:45 UTC, John Colvin wrote:
>>
>> On Wednesday, 22 April 2015 at 21:59:48 UTC, Ola Fosheim Grøstad wrote:
>>>
>>> On Wednesday, 22 April 2015 at 20:36:12 UTC, John Colvin wrote:
>>>>
>>>> Is it even possible to contrive a case where
>>>> 1) The default initialisation stores are technically dead and
>>>> 2) Modern compilers can't tell they are dead and elide them and
>>>> 3) Doing the initialisation has a significant performance impact?
>>>>
>>>> The boring example is "extra code causes instruction cache misses".
>>>
>>>
>>> Allocation of large arrays.
>>
>>
>> That doesn't really answer the question without some more context.
>
>
> I think it does.
>
> Compilers cannot tell what goes on because they cannot figure out nontrivial loop invariants without guidance. You need something like a theorem prover (coq?)...
>

There are two states each local variable can be assigned.
1. Used
2. Read

int a = 1;  // a = Used
return a;  // a = Read
printf("%d\n", a);  // a = Read
int b = a;  // b = Used, a = Read
int c = void; // c = Unused

If a variable is unused, it's a dead variable.  If a variable is used but not read, it's a dead variable. Simple. :-)