www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Do array literals still always allocate?

reply Lewis <musicaljelly gmail.com> writes:
import std.random;
import std.stdio;

int[4] testfunc(int num)  nogc
{
     return [0, 1, num, 3];
}

int main()
{
     int[4] arr = testfunc(uniform(0, 15));
     writeln(arr);
     return 0;
}

I've read a bunch of stuff that seems to indicate that array 
literals are always heap-allocated, even when being used to 
populate a static array. However, testfunc() above compiles as 
 nogc. This would indicate to me that D does the smart thing and 
avoids a heap allocation for an array literal being used to 
populate a static array. Is all the old stuff I was reading just 
out-of-date now?
May 13
next sibling parent Stanislav Blinov <stanislav.blinov gmail.com> writes:
On Saturday, 13 May 2017 at 18:32:16 UTC, Lewis wrote:
 import std.random;
 import std.stdio;

 int[4] testfunc(int num)  nogc
 {
     return [0, 1, num, 3];
 }

 int main()
 {
     int[4] arr = testfunc(uniform(0, 15));
     writeln(arr);
     return 0;
 }

 I've read a bunch of stuff that seems to indicate that array 
 literals are always heap-allocated, even when being used to 
 populate a static array.
On the contrary, when initializing static arrays, allocation is not needed. Note that what's *conceptually* happening in testfunc is this:
int[4] testfunc(int num)  nogc
{
    typeof(return) result = [0, 1, num, 3];
    return result;
}
i.e. the type and size of the storage is known beforehand, all there is to do is copy the elements: 000000000044e528 < nogc int[4] test.testfunc(int)>: 44e528: 55 push %rbp 44e529: 48 8b ec mov %rsp,%rbp 44e52c: 48 83 ec 10 sub $0x10,%rsp 44e530: 48 89 7d f8 mov %rdi,-0x8(%rbp) 44e534: c7 07 00 00 00 00 movl $0x0,(%rdi) # 0 goes at offset 0 44e53a: c7 47 04 01 00 00 00 movl $0x1,0x4(%rdi) # 1 at offset 4 44e541: 89 77 08 mov %esi,0x8(%rdi) # parameter at offset 8 44e544: c7 47 0c 03 00 00 00 movl $0x3,0xc(%rdi) # 3 at offset 12 44e54b: 48 8b 45 f8 mov -0x8(%rbp),%rax 44e54f: c9 leaveq 44e550: c3 retq
 Is all the old stuff I was reading just out-of-date now?
Where exactly did you read that initialization of a static array requires an allocation? That source should be abolished... errrm... corrected!
May 13
prev sibling next sibling parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On Saturday, 13 May 2017 at 18:32:16 UTC, Lewis wrote:
 import std.random;
 import std.stdio;

 int[4] testfunc(int num)  nogc
 {
     return [0, 1, num, 3];
 }

 int main()
 {
     int[4] arr = testfunc(uniform(0, 15));
     writeln(arr);
     return 0;
 }

 I've read a bunch of stuff that seems to indicate that array 
 literals are always heap-allocated, even when being used to 
 populate a static array. However, testfunc() above compiles as 
  nogc. This would indicate to me that D does the smart thing 
 and avoids a heap allocation for an array literal being used to 
 populate a static array. Is all the old stuff I was reading 
 just out-of-date now?
It's just out of date. Can't remember the version, but this did use to allocate. It doesn't any more. But only for this case. In most cases it does allocate. -Steve
May 13
parent reply Lewis <musicaljelly gmail.com> writes:
On Saturday, 13 May 2017 at 19:22:09 UTC, Steven Schveighoffer 
wrote:
 It's just out of date. Can't remember the version, but this did 
 use to allocate. It doesn't any more. But only for this case. 
 In most cases it does allocate.
Okay cool, that's good to hear. For reference the most recent place I remember seeing this was http://stackoverflow.com/questions/6751575/how-to-initialise-static-arrays-in-d-wit out-a-gc-allocation (although I've definitely seen others in the past). I'll add an answer to the SO question to clarify that this is no longer an issue. Thanks all!
May 13
next sibling parent Namespace <rswhite4 gmail.com> writes:
On Sunday, 14 May 2017 at 01:15:03 UTC, Lewis wrote:
 On Saturday, 13 May 2017 at 19:22:09 UTC, Steven Schveighoffer 
 wrote:
 It's just out of date. Can't remember the version, but this 
 did use to allocate. It doesn't any more. But only for this 
 case. In most cases it does allocate.
Okay cool, that's good to hear. For reference the most recent place I remember seeing this was http://stackoverflow.com/questions/6751575/how-to-initialise-static-arrays-in-d-wit out-a-gc-allocation (although I've definitely seen others in the past). I'll add an answer to the SO question to clarify that this is no longer an issue. Thanks all!
You could also use something like this: ---- auto s(T, size_t n)(T[n] arr) { return arr; } auto is = [1, 2, 3].s; // stack allocated ---- I use it whenever I work with D.
May 14
prev sibling parent Nicholas Wilson <iamthewilsonator hotmail.com> writes:
On Sunday, 14 May 2017 at 01:15:03 UTC, Lewis wrote:
 On Saturday, 13 May 2017 at 19:22:09 UTC, Steven Schveighoffer 
 wrote:
 It's just out of date. Can't remember the version, but this 
 did use to allocate. It doesn't any more. But only for this 
 case. In most cases it does allocate.
Okay cool, that's good to hear. For reference the most recent place I remember seeing this was http://stackoverflow.com/questions/6751575/how-to-initialise-static-arrays-in-d-wit out-a-gc-allocation (although I've definitely seen others in the past). I'll add an answer to the SO question to clarify that this is no longer an issue. Thanks all!
It may allocate if it is returned. I think it elided for one caller because it constructs into the callers stack frame but only if the length of all return path are the same length. For 2D or higher it always allocates, i think.
May 14
prev sibling parent reply Nicholas Wilson <iamthewilsonator hotmail.com> writes:
On Saturday, 13 May 2017 at 18:32:16 UTC, Lewis wrote:
 import std.random;
 import std.stdio;

 int[4] testfunc(int num)  nogc
 {
     return [0, 1, num, 3];
 }

 int main()
 {
     int[4] arr = testfunc(uniform(0, 15));
     writeln(arr);
     return 0;
 }

 I've read a bunch of stuff that seems to indicate that array 
 literals are always heap-allocated, even when being used to 
 populate a static array. However, testfunc() above compiles as 
  nogc. This would indicate to me that D does the smart thing 
 and avoids a heap allocation for an array literal being used to 
 populate a static array. Is all the old stuff I was reading 
 just out-of-date now?
1D arrays it doesn't, 2D or higher it does.
May 13
parent reply ag0aep6g <anonymous example.com> writes:
On 05/14/2017 01:57 AM, Nicholas Wilson wrote:
 1D arrays it doesn't, 2D or higher it does.
What do you mean? This works just fine as well: ---- import std.random; import std.stdio; int[2][2] testfunc(int num) nogc { return [[0, 1], [num, 3]]; } int main() { int[2][2] arr = testfunc(uniform(0, 15)); writeln(arr); return 0; } ----
May 14
parent reply Nicholas Wilson <iamthewilsonator hotmail.com> writes:
On Sunday, 14 May 2017 at 10:18:40 UTC, ag0aep6g wrote:
 On 05/14/2017 01:57 AM, Nicholas Wilson wrote:
 1D arrays it doesn't, 2D or higher it does.
What do you mean? This works just fine as well: ---- import std.random; import std.stdio; int[2][2] testfunc(int num) nogc { return [[0, 1], [num, 3]]; } int main() { int[2][2] arr = testfunc(uniform(0, 15)); writeln(arr); return 0; } ----
dynamic array literals is what I meant.
May 14
next sibling parent reply ag0aep6g <anonymous example.com> writes:
On 05/14/2017 01:40 PM, Nicholas Wilson wrote:
 dynamic array literals is what I meant.
I don't follow. Can you give an example in code?
May 14
parent reply Eugene Wissner <belka caraus.de> writes:
On Sunday, 14 May 2017 at 11:45:12 UTC, ag0aep6g wrote:
 On 05/14/2017 01:40 PM, Nicholas Wilson wrote:
 dynamic array literals is what I meant.
I don't follow. Can you give an example in code?
void main() { ubyte[] arr = [ 1, 2, 3, 4, 5 ]; assert(arr == [ 1, 2, 3, 4, 5 ]); } Both, assignment and comparison, allocate.
May 14
next sibling parent ag0aep6g <anonymous example.com> writes:
On 05/14/2017 02:02 PM, Eugene Wissner wrote:
 void main()
 {
     ubyte[] arr = [ 1, 2, 3, 4, 5 ];

     assert(arr == [ 1, 2, 3, 4, 5 ]);
 }

 Both, assignment and comparison, allocate.
Sure, but Nicholas said 1D arrays behave different from 2D arrays.
May 14
prev sibling parent Nicholas Wilson <iamthewilsonator hotmail.com> writes:
On Sunday, 14 May 2017 at 12:02:03 UTC, Eugene Wissner wrote:
 On Sunday, 14 May 2017 at 11:45:12 UTC, ag0aep6g wrote:
 On 05/14/2017 01:40 PM, Nicholas Wilson wrote:
 dynamic array literals is what I meant.
I don't follow. Can you give an example in code?
void main() { ubyte[] arr = [ 1, 2, 3, 4, 5 ]; assert(arr == [ 1, 2, 3, 4, 5 ]); } Both, assignment and comparison, allocate.
LDC is able to promote those to alloca, not sure about DMD or its interaction with nogc. see https://github.com/ldc-developers/ldc/blob/master/gen/passes/GarbageCo lect2Stack.cpp#L832 for the details.
May 14
prev sibling parent Steven Schveighoffer <schveiguy yahoo.com> writes:
On 5/14/17 7:40 AM, Nicholas Wilson wrote:
 On Sunday, 14 May 2017 at 10:18:40 UTC, ag0aep6g wrote:
 On 05/14/2017 01:57 AM, Nicholas Wilson wrote:
 1D arrays it doesn't, 2D or higher it does.
What do you mean? This works just fine as well: ---- import std.random; import std.stdio; int[2][2] testfunc(int num) nogc { return [[0, 1], [num, 3]]; } int main() { int[2][2] arr = testfunc(uniform(0, 15)); writeln(arr); return 0; } ----
dynamic array literals is what I meant.
dynamic array literals allocate most of the time. The one time they don't is the specialized case of initializing a static array, I think of any dimension. However, to the OP's point, they *did* allocate up until a certain version, even when initializing a static array. That is: int[2] x = [1, 2]; used to allocate a dynamic array on the heap, and then copy the data into x (and leave the allocated data for the GC). Now it just populates x without allocating. LDC might be smarter, and take more valid shortcuts, so that is good. But in general you can expect array literals to allocate when not used as static array initializers. -Steve
May 15