Thread overview
Struct Constructor Lazy
Jul 12, 2017
Jiyan
Jul 12, 2017
Biotronic
Jul 12, 2017
Jiyan
Jul 12, 2017
Biotronic
Jul 12, 2017
Jiyan
Jul 12, 2017
Biotronic
Jul 12, 2017
Adam D. Ruppe
Jul 12, 2017
Adam D. Ruppe
Jul 12, 2017
Adam D. Ruppe
July 12, 2017
Hey there:)

i want to know whether the following is somehow possible:
structs dont have default constructors, i know so:

struct A
{
int field;
this(int i){field = getDataFromFile("file.txt");}
}

A instance = A(0);

Here comes my issue:
when A(0) is called I would want here optimal performance, so there doesnt even need to be a value pushed on the stack (i=0), what would be like having a constructor with zero arguments (i is never used!).
Im pretty new to D, can somebody tell me how i would do this?
Is this(lazy int i){ ... a solution?


July 12, 2017
On Wednesday, 12 July 2017 at 11:00:54 UTC, Jiyan wrote:
> Hey there:)
>
> i want to know whether the following is somehow possible:
> structs dont have default constructors, i know so:
>
> struct A
> {
> int field;
> this(int i){field = getDataFromFile("file.txt");}
> }
>
> A instance = A(0);
>
> Here comes my issue:
> when A(0) is called I would want here optimal performance, so there doesnt even need to be a value pushed on the stack (i=0), what would be like having a constructor with zero arguments (i is never used!).
> Im pretty new to D, can somebody tell me how i would do this?
> Is this(lazy int i){ ... a solution?

The traditional solution is static opCall:

struct A {
    int field;
    static A opCall() {
        A result;
        result.field = getDataFromFile("file.txt");
        return result;
    }
}

A instance = A();

I believe I've heard this is frowned upon these days, but I don't know of a better solution.

For optimal speed you might also want to skip default initialization of result, by writing A result = void;.

I would be surprised if the optimizer wasn't able to optimize away the useless parameter though - have you looked at the generated assembly?

--
  Biotronic
July 12, 2017
On Wednesday, 12 July 2017 at 11:18:08 UTC, Biotronic wrote:
> On Wednesday, 12 July 2017 at 11:00:54 UTC, Jiyan wrote:
>> [...]
>
> The traditional solution is static opCall:
>
> struct A {
>     int field;
>     static A opCall() {
>         A result;
>         result.field = getDataFromFile("file.txt");
>         return result;
>     }
> }
>
> A instance = A();
>
> I believe I've heard this is frowned upon these days, but I don't know of a better solution.
>
> For optimal speed you might also want to skip default initialization of result, by writing A result = void;.
>
> I would be surprised if the optimizer wasn't able to optimize away the useless parameter though - have you looked at the generated assembly?
>
> --
>   Biotronic

Hey,
yes i did but to be honest i used dmd in debug version.
The thing about the static one, is that it creates a local object A isnt that a performance issue itself - or am i wrong - im confused actually :P?
July 12, 2017
On Wednesday, 12 July 2017 at 11:34:45 UTC, Jiyan wrote:
> Hey,
> yes i did but to be honest i used dmd in debug version.
> The thing about the static one, is that it creates a local object A isnt that a performance issue itself - or am i wrong - im confused actually :P?

Debug = no optimization. Looking at the generated assembly in a debug build is worthless.

You raise a valid point. In a debug build, you're probably right - it will need to copy the temporary to the target. With optimizations enabled, NRVO[0] will populate the target directly, resulting in roughly the equivalent of this code:

struct A {
    int field;
    static void opCall(A* p) {
        p.field = getDataFromFile("file.txt");
    }
}

A a;
A(&a);

If the function is inlined, the whole problem is of course moot. There are probably other optimizations that can interfere with what I've described.

--
  Biotronic

[0]: https://en.wikipedia.org/wiki/Return_value_optimization
July 12, 2017
Thank you, one last question:
If i declare the parameter as ref i, then there shouldnt be any overhead wouldnt it?

Thanks :)

July 12, 2017
On Wednesday, 12 July 2017 at 12:02:37 UTC, Jiyan wrote:
> Thank you, one last question:
> If i declare the parameter as ref i, then there shouldnt be any overhead wouldnt it?
>
> Thanks :)

That would be basically the exact equivalent - instead of passing an int, you'll be passing a pointer.

--
  Biotronic
July 12, 2017
On Wednesday, 12 July 2017 at 11:00:54 UTC, Jiyan wrote:
> when A(0) is called I would want here optimal performance, so there doesnt even need to be a value pushed on the stack (i=0), what would be like having a constructor with zero arguments (i is never used!).

This is so, so irrelevant. Even if it isn't optimized out (which it probably is), pushing a zero to the stack is so extremely cheap that you'd probably not notice it, especially compared to reading something from a file.

> Im pretty new to D, can somebody tell me how i would do this?
> Is this(lazy int i){ ... a solution?

That's actually more expensive than just sending the zero!
July 12, 2017
On Wednesday, 12 July 2017 at 11:57:33 UTC, Biotronic wrote:
> Debug = no optimization.

That's not really true, you can have an optimized debug build.

dmd's -debug, -g, and -O switches are all independent. -debug turns on blocks labeled with the `debug` switch in code. -g adds info for a debugger to read. -O turns on optimizations.

You can use any combination of those.

> Looking at the generated assembly in a debug build is worthless.

I don't agree - looking at the generated assembly would put to rest all these questions.

But before doing that, you should show that the performance is actually bad. I can't imagine sending a zero argument (whether in a register or pushed to the stack) would ever be a serious problem.
July 12, 2017
On Wednesday, 12 July 2017 at 11:18:08 UTC, Biotronic wrote:
> The traditional solution is static opCall:


That's bug city... the dummy argument is better, or a named static factory function.

Foo foo = Foo;
Foo foo = Foo();

those are different with static opCall. It also conflicts with constructors and non-static opCall.