Performance issue in struct initialization
Johannes Pfau via Digitalmars-d
digitalmars-d at puremagic.com
Mon Jun 20 22:46:34 PDT 2016
Am Mon, 20 Jun 2016 20:34:12 +0000
schrieb Basile B. <b2.temp at gmx.com>:
> On Monday, 20 June 2016 at 11:45:28 UTC, Johannes Pfau wrote:
> > Am Sun, 19 Jun 2016 20:52:52 +0000
> > schrieb deadalnix <deadalnix at gmail.com>:
> >
> >> On Sunday, 19 June 2016 at 11:11:18 UTC, Basile B. wrote:
> >> > On Saturday, 23 April 2016 at 13:37:31 UTC, Andrei
> >> > Alexandrescu wrote:
> >> >> https://issues.dlang.org/show_bug.cgi?id=15951. I showed a
> >> >> few obvious cases, but finding the best code in general is
> >> >> tricky. Ideas? -- Andrei
> >> >
> >> > A new "@noinit" attribute could solve this issue and other
> >> > cases where the initializer is a handicap:
> >> >
> >> > The runtime would skip the copy of the initializer when
> >> > 1- @noinit is an attribute of an aggregate.
> >> > 2- a ctor that takes at least one parameter is present.
> >> > 3- the default ctor is disabled (only a condition for the
> >> > structs or the new free form unions)
> >> >
> >> > // OK
> >> > @noinit struct Foo
> >> > {
> >> > uint a;
> >> > @disable this();
> >> > this(uint a){}
> >> > }
> >> >
> >> > // not accepted because a ctor with parameters misses
> >> > @noinit struct Foo
> >> > {
> >> > @disable this();
> >> > }
> >> >
> >> > // Ok but a warning will be emitted...
> >> > @noinit struct Foo
> >> > {
> >> > uint a = 1; // ...because this value is ignored
> >> > @disable this();
> >> > this(uint a){}
> >> > }
> >> >
> >> > // not accepted because there's a default ctor
> >> > @noinit struct Foo
> >> > {
> >> > this(){}
> >> > }
> >> >
> >> > The rationale is that when there's a constructor that takes
> >> > parameters it's really suposed to initialize the aggregate.
> >> > At least that would be the contract, the "postulate', put by
> >> > the usage of @noinit.
> >>
> >> No new attribute please. Just enable the damn thing where
> >> there is an argumentless constructor and be done with it.
> >
> > Can somebody explain how exactly are constructors related to
> > the problem?
>
> The initializer is copied to the chunk that represents the new
> aggregate instance. The idea here is to explicitly disable this
> copy to get a faster instantiation, under certain conditions. For
> example in allocator.make() this would mean "skip the call to
> emplace() and call directly __ctor() on the new chunk".
>
> > If I've got this:
> > struct Foo
> > {
> > int a = 42;
> > int b = void;
> >
> > @disable this();
> > this(int b)
> > {this.b = b;}
> > }
> > auto foo = Foo(41);
> >
> > I'd still expect a to be initialized to 42.
>
> That's exactly why with @noinit you would get a warning
>
> > Note that this does _not_ require a initializer symbol or
> > memcpy.
>
> I'be verified again and the initializer is copied. For example
> with a gap in the static initial values:
I meant this does not have to use a symbol. Right now it does, but
that's an implementation issue.
>
>
> struct Foo
> {
> int a = 7;
> int gap = void;
> int c = 8;
> @disable this();
> this(int a)
> {this.a = a;}
> }
> auto fun(){ auto foo = Foo(41); return foo.a;}
>
>
> I get (-O -release -inline) for fun():
>
> 0000000000457D58h sub rsp, 18h
> 0000000000457D5Ch mov esi, 004C9390h //
> typid(Foo).initializer.ptr
> 0000000000457D61h lea rdi, qword ptr [rsp+08h]
> 0000000000457D66h movsq //copy 8, note that the gap is not
> handled at all
> 0000000000457D68h movsb //copy 1
> 0000000000457D69h movsb //copy 1
> 0000000000457D6Ah movsb //copy 1
> 0000000000457D6Bh movsb //copy 1
> 0000000000457D6Ch mov eax, 00000029h //inlined __ctor
> 0000000000457D71h mov dword ptr [rsp+08h], eax
> 0000000000457D75h add rsp, 18h
> 0000000000457D79h ret
>
> But that was obvious. How would you expect a = 7 and c = 8 to be
> generated otherwise ?
Instead of doing foo = Foo.init(symbol) you could do foo = {a:7,
c:8}(literal) (in the compiler). Then you don't need memory to memory
moves at all if your architecure allows hardcoding constants in
instructions. Additionally this allows the optimizer to see if you're
writing to a default initialized variable and remove the
useless initialization. If you don't have any default initializers / all
are =void, foo = {} is a no-op. With @noinit using default initializers
causes a warning, with my idea using default initializers works fine
and if you don't want any, just set all fields to =void. (This does not
produce optimal code right now, but considering the =void fields when
initializing is a simple change in GDC)
See https://issues.dlang.org/show_bug.cgi?id=15951#c4 for details.
>
> with @noinit you would get
>
> sub rsp, 18h
> mov eax, 00000029h //inlined __ctor
> mov dword ptr [rsp+08h], eax
> add rsp, 18h
> ret
>
> That's a really simple and pragmatic idea. But I guess that if
> you manage to get the compiler to generate a smarter initializer
> copy then the problem is fixed. At least I'll experiment this
> noinit stuff in my user library.
More information about the Digitalmars-d
mailing list