0

I need an array, that is optimized for one-time-initialization at runtime, with a given length. So memory should be allocated at runtime, but I don't need to change its length.

Is there a array-type other than the pure dynamic array? (it seems to be not the optimal choice for this task)

Bonus would be, if the initialized array is indexable via pointer-iteration, so all it's elements are allocated consecutive in memory.

Is this all just a daydream of a non-experienced programmer, or is there a possibility to achieve this?

I could imagine to do this with manual memory allocation, but maybe there's another way.

Edit:

My main concern is the reading and writing speed of the array.

7
  • No. There isn't. It would not be static anymore. Commented Jun 28, 2015 at 10:23
  • 1
    So what exactly prevents me from initializing a static array at runtime? What makes the difference between a compiled hardcoded memory-chunk and a "guaranteed-to-be-in-one-memory-chunk" generated array at runtime? Commented Jun 28, 2015 at 10:27
  • 1
    @Art1st If the initialisation (including allocation) happens in your own code, that must mean that there could be code before the initialisation that can run as well. (For example, if you have two such arrays, then one initialiser necessarily executes before the other.) What happens if that other code accesses the not-yet-initialised array? Whatever your answer is, does it mean the array works more like a static array, or more like a dynamic array? (I strongly suspect the latter.) Commented Jun 28, 2015 at 10:31
  • 2
    It's not clear to me what qualities of a static array you find absent from a dynamic array. You're talking about manual memory allocation, but any solution will be equal to or slower than a dynamic array because that's exactly what a dynamic array does. Once allocated and initialized, the access characteristics of the two array types only differ by the extra level of indirection inherent in dynamic arrays. If you're using pointer iteration, then that difference is nullified. The bottom line is that fixing the length after initialization won't affect access time in the slightest. Commented Jun 29, 2015 at 3:37
  • 1
    @Art1st: I agree with RobKennedy. A dynamic array is allocated at runtime, does not require its length to be changed if you don't want it to be, and it is allocated in a single contiguous block of memory. It satisfies all of your requirements expect one - pointer iteration - but you can easily handle that manually using a simple type-cast to obtain a pointer to the first element and then you can use pointer math on that as needed. Commented Jun 29, 2015 at 19:06

2 Answers 2

4

Just use an external Count: integer variable, and use the dynamic array length as the "capacity" of the array. It would avoid most memory allocation, if the initial capacity of the array is well defined.

In practice, TList<T>, as defined in the System.Generics.Collections unit, is using this scheme: it stores internally an array, but it has its own Count property. I suspect this is what you were looking for.

For a more low-level stuff, with more features (like JSON or binary serialization, or fast lookup via a hash of one or several properties), you may take a look at our TDynArray dynamic array wrapper. Those are just wrappers on existing dynamic arrays, not data holder like TList<T>. And they work from Delphi 5 or older, and also FPC.

Sign up to request clarification or add additional context in comments.

Comments

3

You can encapsulate what you desire in a generic type. Like this:

type
  TFixedLengthArray<T> = record
  strict private
    FItems: TArray<T>;
    FLength: Integer;
    function GetItem(Index: Integer): T; inline;
    procedure SetItem(Index: Integer; const Value: T); inline;
  public
    property Length: Integer read FLength;
    property Items[Index: Integer]: T read GetItem write SetItem; default;
    class function New(const Values: array of T): TFixedLengthArray<T>; static;
  end;

{ TFixedLengthArray<T> }

class function TFixedLengthArray<T>.New(const Values: array of T): TFixedLengthArray<T>;
var
  i: Integer;
begin
  Result.FLength := System.Length(Values);
  SetLength(Result.FItems, Result.FLength);
  for i := 0 to Result.FLength-1 do begin
    Result.FItems[i] := Values[i];
  end;
end;

function TFixedLengthArray<T>.GetItem(Index: Integer): T;
begin
  Result := FItems[Index];
end;

procedure TFixedLengthArray<T>.SetItem(Index: Integer; const Value: T);
begin
  FItems[Index] := Value;
end;

Create a new one like this:

var
  MyArray: TFixedLengthArray<Integer>;
....
MyArray: TFixedLengthArray<Integer>.New([1, 42, 666]);

Access items like this:

for i := 0 to MyArray.Length-1 do
  Writeln(MyArray[i]);

This just wraps a dynamic array. Elements are contiguous. The length of the array is determined once and for all then a new instance is created.

One thing to watch out for here is that the type will behave like a reference type since its data is stored in a reference type. That is, the assignment operator on this type will behave in the same manner as dynamic array assignment.

So if we have two variables of this type, arr1 and arr2 then the following occurs:

arr1 := arr2;
arr1[0] := 42;
Assert(arr2[0] = 42);

If you wanted to make the type behave like a true value then you would implement copy-on-write inside SetItem.

Update

Your edit to the question changes is significantly. It seems that you are in fact concerned more with performance than encapsulation.

The inlining of the item accessor methods in the above type means that the performance characteristics should be close to that of an array. The access will still be O(1), but it is quite plausible that the inliner/optimiser is weak and fails to emit the most optimal code.

Before you decide that you must use arrays to obtain the absolute ultimate performance, do some real world benchmarking. It seems to me to be quite unlikely that the code to read/write from an array is really a bottleneck. Most likely the bottleneck will be what you then do with the values in the array.

11 Comments

Has this any speed benefits compared to just using TArray<T> instead, or this this only to prevent the programmer from changing its length at runtime?
Not likely to be speed differences. If the inliner screws up it will be slower. Reading length might be faster but I doubt it. The point is to stop the programming changing the length, which is what I understood you to be asking.
My main reason for this question was indeed the speed-issue, I hoped there is a type that ensures compact memory, so I can access any item of the array in exactly O(1), but anyway this answer gives a solution to my second concern, changing the length after initialization.
The type in this answer has O(1) random access as do all Delphi array types. If speed and performance was important then I would have expected you to have mentioned that in the question.
@Art1st If array read or write performance is a bottleneck then other strategies may be useful - reading and writing cacheable blocks to a stack buffer, for example. This type of strategy lets you leverage cache on smaller pieces of data at a time as well as writing entire cache or memory lines at once. Would need to be carefully tested and tuned.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.