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.