Related to my DConf 2022 lightning talk, I am noticing that D runtime's in-place array extension optimization is available only for array data that are at certain memory alignments.

I used the following program to test it. In addition to repeatedly adding an element to an array,

- 'version = neither' does not drop any element (this is "good" as well)

- 'version = bad' case drops the front element (a moving window of 1)

- 'version = good' case drops elements only when element data will hit a certain alignment value.

Is this expected?

import std.stdio;
import std.range;

// PLEASE UNCOMMENT ONLY ONE:
// version = bad;        // No in-place extension
// version = good;       // In-place extension happens
// version = neither;    // In-place extension happens

mixin assertSingleVersionOf!("bad", "good", "neither");

void main() {
  struct S {
    ubyte[4] data;  // Try different reasonable sizes.
                    // For example, 5 will not work even
                    // for the "good" case.
  }

  S[] arr;

  foreach (i; 0 .. 100_000) {
    const oldCap = arr.capacity;
    const oldPtr = arr.ptr;

    arr ~= S.init;

    if (arr.capacity != oldCap) {
      // The array needed to be extended...
      if (arr.ptr == oldPtr) {
        // ... but the pointer did not change
writefln!"In-place extension -- element: %,s capacity: %,s -> %,s ptr: %s"(
          i, oldCap, arr.capacity, arr.ptr);
      }
    }

    version (neither) {
      // Do not remove any element; just extend
    }

    version (bad) {
      // Dropping 1 element inhibits in-place extension
      // (Many values other than 1 will inhibit as well.)
      arr = arr[1..$];

      // Even this does not help
      arr.assumeSafeAppend();
    }

    version (good) {
      // Dropping front elements equaling 2048 bytes works.
      // (Likely a GC magic constant.)

      enum magic = 2048;
      enum elementsPerPage = magic / S.sizeof;

      if (arr.length == elementsPerPage) {
        arr = arr[elementsPerPage..$];
      }
    }
  }
}

// A useful template that has nothing to do with this problem.
mixin template assertSingleVersionOf(args...) {
  import std.format : format;

  static assert (1 >= {
    size_t count = 0;
    static foreach (arg; args) {
      static assert (is (typeof(arg) == string));
      mixin (format!q{
        version (%s) {
          ++count;
        }
      }(arg));
    }
    return count;
  }(), format!"Please pick only one or none of %(%s, %)"([args]));
}

Ali

Reply via email to