On Friday, 10 July 2020 at 10:13:23 UTC, wjoe wrote:
So many awesome answers, thank you very much everyone!

Less overhead,
Using/needing it to interface with something else, and
Efficiency are very good points.

However stack memory needs to be allocated at program start. I don't see a huge benefit in allocation speed vs. heap pre-allocation, or is there?

Stack is allocated by the OS for the process when it's started. Reserving space for stack variables, including arrays, is effectively free, since the compiler assigns offsets statically at compile time.

I mean 1 allocation vs 2 isn't going to noticeably improve overall performance.

A GC allocation is way more complex than a mere bump-the-pointer. If your program is trivial enough you may actually find that one extra GC allocation is significant in its runtime. Of course, if you only ever allocate once and your program runs for ages, you won't really notice that allocation.

    a[]
What happens here exactly ?

This:

int[10] a;
int[] slice = a[];
assert(slice.ptr == &a[0]);
assert(slice.length == 10);
assert(a.sizeof == 10 * int.sizeof);    // 40
assert(slice.sizeof == (int[]).sizeof); // 16 on 64 bit

I read the chapters in Ali's book (thank you very much for such a great book, Ali) on arrays and slicing prior to asking this question and I came to the following conclusion:

Because a static array is pre-allocated on the stack,
doesn't have a pointer/length pair,
is addressed via the stack pointer, and
due to the fact that a slice is a pointer/length pair
and because a slice is technically the meta data of a dynamic array, a view into (part) of a dynamic array,

No. A slice is just a pointer/length pair - a contiguous view into *some* memory, regardless of where that memory came from:

void takeASlice(scope void[] data) // can take any slice since any slice converts to void[]
{
    import std.stdio;
    writefln("%x %d", data.ptr, data.length);
}

int[10] a;
takeASlice(a); // a[]
takeASlice(a[1 .. $-1]); // a[1 .. 9]

struct S
{
    float x, y, z;
    float dx, dy, dz;
}

S s;
takeASlice((&s)[0 .. 1]); // Slicing a pointer, not @safe but can be done.
takeASlice(new int[10]); // Array, GC allocation
takeASlice([1, 2, 3, 4]); // Array literal, may or may not be GC-allocated

`takeASlice` has no knowledge of where the memory came from.

Dynamic arrays only ever come into the picture if you try to manipulate the slice itself: resize it, append to it, etc.

that it's not possible to slice a static array because the slice would technically be akin to a dynamic array and hence be incompatible.

Incompatible to what?

int[10] a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
a[0 .. 2] = a[2 .. 4];
assert(a[0] == 3);
assert(a[1] == 4);
int[10] b = void;
b[] = a[];
assert(b == [3, 4, 3, 4, 5, 6, 7, 8, 9, 10]);

struct SuperSpecializedArray(T, size_t S) if (S > 0)
{
   T[S] elements;

   struct SuperSpecializedArrayRange
   {
      typeof(elements) e;

      this(SuperSpecializedArray a)
      {
         e = a.elements; // copies
      }

      // ...
   }
}

Upon creation of a SuperSpecializedArrayRange, the array is copied, but more importantly, data which may not ever be needed is copied and that's supposed to be a big selling point for ranges - only ever touching the data when it's requested - am I wrong ?

Ranges need not be lazy. They can be, and most of them should be indeed, but they need not be. And, as you yourself point out, in your case `e` can just be a slice, and your range becomes lazy.

Reply via email to