Jump to page: 1 2
Thread overview
What's the point of static arrays ?
Jul 09, 2020
wjoe
Jul 09, 2020
Simen Kjærås
Jul 09, 2020
psycha0s
Jul 09, 2020
Ali Çehreli
Jul 09, 2020
H. S. Teoh
Jul 09, 2020
Jonathan M Davis
Jul 10, 2020
wjoe
Jul 09, 2020
IGotD-
Jul 09, 2020
H. S. Teoh
Jul 09, 2020
Paul Backus
Jul 09, 2020
IGotD-
Jul 10, 2020
wjoe
Jul 10, 2020
psycha0s
Jul 10, 2020
wjoe
Jul 10, 2020
wjoe
Jul 10, 2020
Stanislav Blinov
Jul 10, 2020
wjoe
Jul 10, 2020
Ali Çehreli
Jul 13, 2020
wjoe
Jul 10, 2020
Simen Kjærås
July 09, 2020
+ The size is known at compile time.

vs.

- Can neither grow nor shrink them
- Can't append elements
- Can't remove elements
- Can't slice them
- Can't range them
- Assignment copies the whole array, as in int[5] a; auto b = a;
- Size is limited by stack
- Stack overflow issues


Some of the cons could be considered a feature.
Also GC but it's possible to make a dynamic array implementation which avoids the GC.

I'm not considering supposed performance benefits/penalties because these need to be profiled.

Considering the many downsides why would I ever want to choose a static over a dynamic array ?

July 09, 2020
On Thursday, 9 July 2020 at 12:12:06 UTC, wjoe wrote:
> I'm not considering supposed performance benefits/penalties because these need to be profiled.
>
> Considering the many downsides why would I ever want to choose a static over a dynamic array ?

Simply put: static arrays are not dynamic arrays, and if you try to use one as the other you're going to be disappointed.

Usually, you use static arrays when you interface with something else that does it - generally a file format or a C library. For the most part you're right, you should probably use a dynamic array instead.

Now, as for your points:

> - Can neither grow nor shrink them
> - Can't append elements
> - Can't remove elements

These are the same as the first point.


> - Can't slice them
> - Can't range them

Sure you can:

unittest {
    import std.stdio;
    import std.algorithm;
    int[10] a = [1,2,3,4,5,6,7,8,9,10];

    // Note I'm slicing the static array to use in range algorithms:
    writeln(a[].map!(b => b+2));

    // Slicing works:
    auto b = a[3..6];
    b[] = 7;
    writeln(a);
}


> - Assignment copies the whole array, as in int[5] a; auto b = a;

This is often a factor in choosing a static array. It's not better or worse, just different. And sometimes it's different in exactly the way you need.


> - Size is limited by stack
> - Stack overflow issues

So allocate your static array on the heap if this is a problem?


> Some of the cons could be considered a feature.
> Also GC but it's possible to make a dynamic array implementation which avoids the GC.

Basically none of the drawbacks you refer to are actual drawbacks, but instead part of what makes static arrays useful. Static arrays are not a poor man's dynamic arrays, they're a different beast, doing different things.

--
  Simen
July 09, 2020
On Thursday, 9 July 2020 at 12:12:06 UTC, wjoe wrote:
> Also GC but it's possible to make a dynamic array implementation which avoids the GC.
It's easy to make a dynamic array without using the GC. Did you mean "implementation which avoids memory management"?

> I'm not considering supposed performance benefits/penalties because these need to be profiled.
>
> Considering the many downsides why would I ever want to choose a static over a dynamic array ?
Sometimes the amount of elements is known at compile time so you don't need to waste CPU cycles for memory allocation/freeing. This can be really important when you work on a performance-critical code.
July 09, 2020
On 7/9/20 5:12 AM, wjoe wrote:

> Considering the many downsides why would I ever want to choose a static over a dynamic array ?
> 

In addition to what others said, dynamic arrays can be more expensive both in space and time.

Time: Dynamic array elements are accessed through an extra pointer compared to static arrays. Depending on the usage pattern of the data, that extra indirection may be slower (e.g. due to the extra load on the CPU's cache).

Space: Every dynamic array is represented by the pair of one pointer (void*) one length (size_t) e.g. 2 * 8 = 16 bytes on a 64-bit system. Assuming the array is just 3 floats, which is a common type used for RGB, 3D coordinates, etc. (3 * 4 = 12 bytes), then those 16 bytes are more than the data itself.

I wrote the following program (as a fun morning exercise, before coffee :o) ) to display bytes used by different kinds of variables:

import std.stdio;
import std.range;
import std.algorithm;
import std.traits;

size_t bytesUsed(T)(T var) {
  static if (isDynamicArray!T) {
    enum dynamicArrayOverhead = (void[]).sizeof;
    // Double check:
    static assert (dynamicArrayOverhead == size_t.sizeof + (void*).sizeof);

    return dynamicArrayOverhead + var.map!(element => bytesUsed(element)).sum;

  } else static if (isAssociativeArray!T) {
    static assert (false, "I don't know the implementation of AAs.");

  } else static if (is (T == struct)) {
    // BUG: Ignores alignment
    size_t total;
    foreach (member; var.tupleof) {
      total += bytesUsed(member);
    }
    return total;

  } else static if (is (T == class)) {
    // BUG: Ignores alignment
    size_t total;
    foreach (member; var.tupleof) {
      total += bytesUsed(member);
    }
    enum classOverhead = (void*).sizeof * 2;
    return classOverhead + total;

  } else {
    return var.sizeof;
  }

// BUG: union?
}

unittest {
  struct S {
    int[] arr;
    void* ptr;
  }
  assert(bytesUsed(S([1, 2, 3])) == size_t.sizeof + (void*).sizeof + 3 * int.sizeof + 8);
}

void info(alias var)() {
  writefln!"'%s' is %s and uses %s bytes."(var.stringof, typeof(var).stringof, bytesUsed(var));
}

void main() {
  // This is an efficient data structure:
  alias Color = float[3]; // red, green, blue
  alias DayColors = Color[7];

  // Comparing it to the dynamic array equivalent:
  DayColors a;
  auto b = makeDayColors();
  info!a;
  info!b;
}

float[] makeColor() {
  // Syntax confusion alert: Return type is *not* a static array. :/
  return new float[3];
}

float[][] makeDayColors() {
  float[][] result = new float[][7];
  foreach (ref e; result) {
    e = makeColor();
  }
  return result;
}

Ali
July 09, 2020
On Thu, Jul 09, 2020 at 12:12:06PM +0000, wjoe via Digitalmars-d-learn wrote:
> + The size is known at compile time.
> 
> vs.
> 
> - Can neither grow nor shrink them
> - Can't append elements
> - Can't remove elements

Consider a 3D game in which you represent vectors as static arrays of 4 elements (homogenous representation).  In this case, it's a plus to not be able to append/remove elements, because the rest of the code expects vectors that are exactly 4 elements long.


> - Can't slice them
> - Can't range them

Sure you can.


> - Assignment copies the whole array, as in int[5] a; auto b = a;

Sometimes this is desirable.  Consider the 3D game example.  Suppose you're given a vector and need to perform some computation on it. If it were a dynamic array, you'd need to allocate a new array on the heap in order to work on it without changing the original vector. With a static array, it's passed by value to your function, so you just do what you need to do with it, and when you're done, either discard it (== no work because it's allocated on the stack) or return it (== return on the stack, no allocations).


> - Size is limited by stack
> - Stack overflow issues

You *can* allocate static arrays on the heap, in which case they become closer to dynamic arrays.  Generally, the cases for which static arrays are useful are when the size isn't huge; for huge arrays it makes more sense to just use dynamic arrays.

You can also have classes that contain large static arrays: in this case, the usefulness comes from having the array embedded in the class object itself, rather than being a separate heap allocation + another level of indirection.


[...]
> Considering the many downsides why would I ever want to choose a static over a dynamic array ?

It depends on your needs.  If you know you're always going to be trading in vectors of a fixed size, like 4-element vectors in aforementioned 3D game, then there's no need to put extra GC (or whatever allocator) pressure on your code, just trade in T[4].  You also skip bounds checks in many cases, since the length is known at compile-time.  You get by-value semantics, which is generally much easier to reason about than by-reference semantics.  You avoid an extra level of indirection, which can matter in hotspots.

If your arrays are going to change in length, then static arrays aren't what you need; just use dynamic arrays. They serve different purposes.


T

-- 
All men are mortal. Socrates is mortal. Therefore all men are Socrates.
July 09, 2020
On Thursday, July 9, 2020 10:21:41 AM MDT H. S. Teoh via Digitalmars-d-learn wrote:
> > - Assignment copies the whole array, as in int[5] a; auto b = a;
>
> Sometimes this is desirable.  Consider the 3D game example.  Suppose you're given a vector and need to perform some computation on it. If it were a dynamic array, you'd need to allocate a new array on the heap in order to work on it without changing the original vector. With a static array, it's passed by value to your function, so you just do what you need to do with it, and when you're done, either discard it (== no work because it's allocated on the stack) or return it (== return on the stack, no allocations).

I recall that at one point, I wrote brute-force sudoku solver, and initially, I'd used dynamic arrays to represent the board. When I switched them to static arrays, it was _way_ faster - presumably, because all of those heap allocations were gone. And of course, since the sudoku board is always the same size, the ability to resize the array was unnecessary.

In most programs that I've written, it hasn't made sense to use static arrays anywhere, but sometimes, they're exactly what you need.

- Jonathan M Davis



July 09, 2020
On Thursday, 9 July 2020 at 12:12:06 UTC, wjoe wrote:
> ...

Static arrays are great because as already mentioned, they are allocated on the stack (unless it is global variable something, then it ends up in the data segment or TLS area).

As C/C++ now allows dynamically sized static arrays (for stack only), shouldn't D allow this as well.

Now you have to do.

import core.stdc.stdlib;

void myFunc(size_t arraySize)
{
    void *ptr = alloca(arraySize);
    ubyte[] arr = cast(ubyte[])ptr[0..arraySize];

}

it would be nice if we could just write

ubyte[arraySize] just like in C.

Now this operation is not safe, but could be allowed in @system code.

Is this the reason that ubyte[arraySize] doesn't create a dynamic array with size arraySize?

Now you need to do.

ubyte[] arr;
arr.length = arraySize;

Like ubyte[arraySize] is reserved for the same usage as in C.



July 09, 2020
On Thu, Jul 09, 2020 at 06:02:02PM +0000, IGotD- via Digitalmars-d-learn wrote: [...]
> Is this the reason that ubyte[arraySize] doesn't create a dynamic array with size arraySize?
> 
> Now you need to do.
> 
> ubyte[] arr;
> arr.length = arraySize;

Nah, just do this:

	arr = new ubyte[arraySize];

Mission accomplished. ;-)


T

-- 
I am Ohm of Borg. Resistance is voltage over current.
July 09, 2020
On Thursday, 9 July 2020 at 18:02:02 UTC, IGotD- wrote:
>
> Static arrays are great because as already mentioned, they are allocated on the stack (unless it is global variable something, then it ends up in the data segment or TLS area).
>
> As C/C++ now allows dynamically sized static arrays (for stack only), shouldn't D allow this as well.
>
> Now you have to do.
>
> import core.stdc.stdlib;
>
> void myFunc(size_t arraySize)
> {
>     void *ptr = alloca(arraySize);
>     ubyte[] arr = cast(ubyte[])ptr[0..arraySize];
>
> }
>
> it would be nice if we could just write
>
> ubyte[arraySize] just like in C.

Note that using VLAs in C is widely considered to be bad practice, and that they were made optional in the C11 standard.

If you want to allocate an array on the stack, the best way is to use a static array for size below a predetermined limit, and fall back to heap allocation if that limit is exceeded. An easy way to do this in D is with `std.experimental.allocator.showcase.StackFront`.
July 09, 2020
On Thursday, 9 July 2020 at 18:51:47 UTC, Paul Backus wrote:
>
> Note that using VLAs in C is widely considered to be bad practice, and that they were made optional in the C11 standard.
>
> If you want to allocate an array on the stack, the best way is to use a static array for size below a predetermined limit, and fall back to heap allocation if that limit is exceeded. An easy way to do this in D is with `std.experimental.allocator.showcase.StackFront`.

I know but I at least really like them because they solve a few obstacles for me. If you have a maximum allowed size, then they should be alright but opinions differ here.

I do not recommend allocating a 'max' static size if you don't use just a fraction of it. The reason is that will possibly break out more stack pages than necessary leading to unnecessary memory consumption, especially if you initialize the entire array. This is another reason I like VLAs.
« First   ‹ Prev
1 2