www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Does foreach on array literal allocate?

reply "Bill Baxter" <wbaxter gmail.com> writes:
Does anyone know off the top of their head if code like this allocates
or not with current DMD 1.x compilers?

foreach(x; [1, 2, 3, 4])
{
     // do something non-allocating with x
}

And is the answer different if the values are only known at runtime?  Like here:

void a func(int a1, int a2, int a3)
{
   foreach(x; [a1,a2,a3]) {
         // do something non-allocating with x
   }

}

--bb
Dec 04 2008
next sibling parent Sergey Gromov <snake.scaly gmail.com> writes:
Fri, 5 Dec 2008 03:58:39 +0900, Bill Baxter wrote:

 Does anyone know off the top of their head if code like this allocates
 or not with current DMD 1.x compilers?
 
 foreach(x; [1, 2, 3, 4])
 {
      // do something non-allocating with x
 }
 
 And is the answer different if the values are only known at runtime?  Like
here:
 
 void a func(int a1, int a2, int a3)
 {
    foreach(x; [a1,a2,a3]) {
          // do something non-allocating with x
    }
 
 }

AFAIK array iteration never used delegates. Therefore it shouldn't have been ever allocating, and it shouldn't now.
Dec 04 2008
prev sibling next sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
Bill Baxter wrote:
 Does anyone know off the top of their head if code like this allocates
 or not with current DMD 1.x compilers?
 
 foreach(x; [1, 2, 3, 4])
 {
      // do something non-allocating with x
 }

Here's how to find out. Compile: void test() { foreach(x; [1, 2, 3, 4]) { // do something non-allocating with x } } Obj2asm the output: _D5test44testFZv comdat assume CS:_D5test44testFZv L0: enter 8,0 push EBX push 4 push 3 push 2 push 1 push 4 push offset FLAT:_D12TypeInfo_G4i6__initZ call near ptr __d_arrayliteralT mov -8[EBP],EAX lea EAX,010h[EAX] mov -4[EBP],EAX add ESP,018h L25: mov ECX,-8[EBP] cmp ECX,-4[EBP] jae L38 mov EDX,-8[EBP] mov EBX,[EDX] add dword ptr -8[EBP],4 jmp short L25 L38: pop EBX leave ret _D5test44testFZv ends Look up _d_arrayliteralT in the runtime library: extern (C) void* _d_arrayliteralT(TypeInfo ti, size_t length, ...) { auto sizeelem = ti.next.tsize(); // array element size void* result; debug(PRINTF) printf("_d_arrayliteralT(sizeelem = %d, length = %d)\n", sizeelem, length); if (length == 0 || sizeelem == 0) result = null; else { result = gc_malloc(length * sizeelem, !(ti.next.flags() & 1) ? BlkAttr.NO_SCAN : 0); va_list q; va_start!(size_t)(q, length); size_t stacksize = (sizeelem + int.sizeof - 1) & ~(int.sizeof - 1); if (stacksize == sizeelem) { memcpy(result, q, length * sizeelem); } else { for (size_t i = 0; i < length; i++) { memcpy(result + i * sizeelem, q, sizeelem); q += stacksize; } } va_end(q); } return result; } and note the call to gc_malloc().
Dec 04 2008
next sibling parent Walter Bright <newshound1 digitalmars.com> writes:
Bill Baxter wrote:
 and note the call to gc_malloc().

Bummer.

I agree the compiler could do better there. There is a lot of opportunity for better optimization. I haven't spent time on it because of all the other things that need doing first.
 Does a static-sized array initializer also allocate?
 
 int[4] v = [1,2,3,4];
 foreach(x; v) {
     /// ...
 }
 
 Or maybe this (also) works without allocation?
 
 foreach(x; Tuple!(1,2,3,4))
 {
    ///
 }
 
 This kinda code is useful for the case where you have to do the same
 thing to two or three local variables.  You can write a local function
 to do it, but foreach on a static set of them breaks up the visual
 flow of the code a little less, I think.
 
 
 --bb

Dec 04 2008
prev sibling parent Kagamin <spam here.lot> writes:
Bill Baxter Wrote:

 Bummer.
 
 Does a static-sized array initializer also allocate?
 
 int[4] v = [1,2,3,4];
 foreach(x; v) {
     /// ...
 }

http://d.puremagic.com/issues/show_bug.cgi?id=2356
Dec 05 2008
prev sibling next sibling parent reply "Lionello Lunesu" <lionello lunesu.remove.com> writes:
Short answer: yes, they both allocate memory.

Long answer:

_d_arrayliteralT from gc.d is used to create an array:

import std.stdio;

void main()
{
  foreach(r; [1, 2, 3])
    writefln(r);
  asm { nop; }
  int a,b,c;
  foreach(r; [a, b, c])
    writefln(r);
}

__Dmain comdat
 assume CS:__Dmain
L0:  push EBP
  mov EBP,ESP
  sub ESP,028h
  push 3
  push 2
  push 1
  push 3
  push offset FLAT:_D12TypeInfo_G3i6__initZ
  call near ptr __d_arrayliteralT
  mov -024h[EBP],EAX
  mov EAX,-024h[EBP]
  lea EAX,0Ch[EAX]
  mov -020h[EBP],EAX
  add ESP,014h
L27:  mov EAX,-024h[EBP]
  cmp EAX,-020h[EBP]
  jae L4D
  mov EAX,-024h[EBP]
  mov EAX,[EAX]
  mov -01Ch[EBP],EAX
  push dword ptr -01Ch[EBP]
  push offset FLAT:_D12TypeInfo_B1i6__initZ
  call near ptr _D3std5stdio8writeflnFYv
  add dword ptr -024h[EBP],4
  add ESP,8
  jmp short L27
L4D:  nop
  xor EAX,EAX
  mov -018h[EBP],EAX
  mov -014h[EBP],EAX
  mov -010h[EBP],EAX
  push dword ptr -010h[EBP]
  push dword ptr -014h[EBP]
  push dword ptr -018h[EBP]
  push 3
  push offset FLAT:_D12TypeInfo_G3i6__initZ
  call near ptr __d_arrayliteralT
  mov -0Ch[EBP],EAX
  mov EAX,-0Ch[EBP]
  lea EAX,0Ch[EAX]
  mov -8[EBP],EAX
  add ESP,014h
L7D:  mov EAX,-0Ch[EBP]
  cmp EAX,-8[EBP]
  jae LA3
  mov EAX,-0Ch[EBP]
  mov EAX,[EAX]
  mov -4[EBP],EAX
  push dword ptr -4[EBP]
  push offset FLAT:_D12TypeInfo_B1i6__initZ
  call near ptr _D3std5stdio8writeflnFYv
  add dword ptr -0Ch[EBP],4
  add ESP,8
  jmp short L7D
LA3:  mov ESP,EBP
  pop EBP
  ret

"Bill Baxter" <wbaxter gmail.com> wrote in message 
news:mailman.92.1228417124.22690.digitalmars-d puremagic.com...
 Does anyone know off the top of their head if code like this allocates
 or not with current DMD 1.x compilers?

 foreach(x; [1, 2, 3, 4])
 {
     // do something non-allocating with x
 }

 And is the answer different if the values are only known at runtime?  Like 
 here:

 void a func(int a1, int a2, int a3)
 {
   foreach(x; [a1,a2,a3]) {
         // do something non-allocating with x
   }

 }

 --bb 

Dec 04 2008
parent "Lionello Lunesu" <lionello lunesu.remove.com> writes:
Walter beat me to it! Oh well, it was a nice exercise :)

L.
Dec 04 2008
prev sibling next sibling parent "Bill Baxter" <wbaxter gmail.com> writes:
On Fri, Dec 5, 2008 at 10:31 AM, Sergey Gromov <snake.scaly gmail.com> wrote:
 Fri, 5 Dec 2008 03:58:39 +0900, Bill Baxter wrote:

 Does anyone know off the top of their head if code like this allocates
 or not with current DMD 1.x compilers?

 foreach(x; [1, 2, 3, 4])
 {
      // do something non-allocating with x
 }

 And is the answer different if the values are only known at runtime?  Like
here:

 void a func(int a1, int a2, int a3)
 {
    foreach(x; [a1,a2,a3]) {
          // do something non-allocating with x
    }

 }

AFAIK array iteration never used delegates. Therefore it shouldn't have been ever allocating, and it shouldn't now.

Walter's answer is more what I was after. I wasn't worried about the D2 delegate allocation stuff, but rather the little array literal would be put on the stack or not. --bb
Dec 04 2008
prev sibling parent "Bill Baxter" <wbaxter gmail.com> writes:
On Fri, Dec 5, 2008 at 10:37 AM, Walter Bright
<newshound1 digitalmars.com> wrote:
 Bill Baxter wrote:
 Does anyone know off the top of their head if code like this allocates
 or not with current DMD 1.x compilers?

 foreach(x; [1, 2, 3, 4])
 {
     // do something non-allocating with x
 }

Here's how to find out. Compile:

Thanks for the explanation. Though Obj2Asm is I think part of the EUP, right? Maybe I'll get that after all.
 and note the call to gc_malloc().

Bummer. Does a static-sized array initializer also allocate? int[4] v = [1,2,3,4]; foreach(x; v) { /// ... } Or maybe this (also) works without allocation? foreach(x; Tuple!(1,2,3,4)) { /// } This kinda code is useful for the case where you have to do the same thing to two or three local variables. You can write a local function to do it, but foreach on a static set of them breaks up the visual flow of the code a little less, I think. --bb
Dec 04 2008