One of the rules related to the new ILNumerics memory management, which potentially causes issues, is not to use the var
keyword of C#. In Visual Basic, similar functionality is available by ommiting the explicit type declarations for array types.
ILArray<double> A = rand(5,4,3); // explicit array type declaration required var B = rand(5,4,3); // NOT ALLOWED!
Lets take a look at the reasons, why this – otherwise convenient – language feature is not allowed in conjunction with array declarations. In order to make this clear, a little survey into the internals of the ILNumerics memory management is needed.
In ILNumerics, local arrays are of one of the types ILArray<T>, ILLogicalArray, ILCell
. Those array types are one building block of the memory managemt in ILNumerics. By using one of those array types in a function, one can be sure, to keep the array alive and available as long as the function is not left. On the other hand – as soon as the function was left, the array will be recycled immediately.
Other array types exist in ILNumerics. They serve different purposes regarding their lifetime and mutability. Input arrays like ILInArray<T>
f.e., make sure, arrays given as function parameters are unable to get altered.
Another important type is the return type of any function. Every function, property or operator in ILNumerics returns arrays of either ILRetArray<T>, ILRetLogical
or ILRetCell
. Return arrays are volatile or temporary arrays. Their use is restricted to exactly one time. After the first use, return arrays are disposed immediately. For expressions like the following, this behavior drastically increases memory efficiency:
abs(pow(cos(A*pi/2+t),2)
Assuming A to be a (rather large) array, 7 temporary memory storages of the size of A would be necessary in order to evaluate the whole expression. But if we take the above assumption regarding the lifetime of return arrays into account, that number is reduced to at most 2 temporary arrays. The reason: A * pi needs one storage for the result: Result1. It is than used to compute [Result1] / 2. Here, another storage is needed for the new result: Result2. At the end of the division operation, [Result1] has already been used for the first time. Since it is a Return Type, it is released and its storage recycled. For the next calculation [Result2] + t, the storage from [Result1] is already found in the memory pool of ILNumerics. Therefore, no new storage is needed and both temporary storages are alternatingly used for the subsequent evaluations.
Lets assume, the expression above does only make sense, if we can retrieve the result and use it in subsequent expressions inside our function. The most common case would be to assign the result to a local variable. Now, we get close to the interesting part: If we would allow the var
keyword to be used, C# would generate a local variable B of type ILRetArray<double>
:
// DO NOT DO THIS!! var B = abs(pow(cos(A*pi/2+t),2); // now B is of type ILRetArray<double> ! Console.Out.Write(B.Length); Console.Out.Write(B.ToString()); //<-- fails!
Besides the fact, that this would conflict with the function rules of ILNumerics (local array types must be of ILArray<T>
or similar), B could only be used for exactly one time! In the example, B.Length
does execute normaly. After that statement, B gets disposed. Therefore, the statement B.ToString()
will already fail. This is, why var
is not permitted.
By explicitely declaring the local array type, the compiler will use implicit type conversions in order to convert a return array to a local array, which is than available for the rest of the function block:
// this code is correct ILArray<double> B = abs(pow(cos(A*pi/2+t),2); // now B is of type ILArray<double> ! Console.Out.Write(B.Length); Console.Out.Write(B.ToString()); // works as expected
See: General Rules for ILNumerics, Function Rules