Performance issue in struct initialization
Basile B. via Digitalmars-d
digitalmars-d at puremagic.com
Mon Jun 20 13:34:12 PDT 2016
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:
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 ?
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