ILNumerics  >  Support  >  Documentation  >  General Rules  >  Function Rules
 

Writing efficient Functions in ILNumerics

1. Array Types for Function Declarations

ILNumerics provides distinct array types - regarding the content and regarding the lifetime. Usually, the user only handles common local arrays, like ILArray<T>, ILCell and ILLogical. All array variables declared in the function body are of one of those types.

However, in the need for an efficient memory management we added certain array types which are used only in the parameter list of function declarations:

  • "Input arrays" are used as general, readonly, input parameter to a function.
  • "Output arrays" serve as additional, optional, output parameter of a function.
  • "Return arrays" are always used as return value of a function.

All array types are easily distinguishable by name. The general naming scheme is as follows:

IL[|In|Out|Ret][Array<T>|Cell|Logical]

Lets assume, a function is to receive two dense matrices as input parameter and returns a cell array. The function signature may looks as follows:

C# Code
1
static ILRetCell MyFunc(ILInArray<double> inA, ILInArray<double> inB) { ...

The following table lists all array types existing in ILNumerics and describes their lifetimes:

Content Local Array Input Parameter Output Parameter Return Value
Dense Array ILArray<T> ILInArray<T> ILOutArray<T> ILRetArray<T>
Cell Array ILCell ILInCell ILOutCell ILRetCell
Logical Array ILLogical ILInLogical ILOutLogical ILRetLogical
Lifetimes
Used in function .. body head head head
Disposed automatically ... when current scope was left when function scope was left when calling scope was left after the first use
Mutability full mutable immutable full mutable immutable
Functionality
Assignments to variables
of that type:
A = ... inA = ... [1] outA.a = ... (no variables)[2]

[1] Not recommended for best performance, see: Optimizing Performance for details. [2] Return values are volatile and converted to other array types or immediately disposed after first use.

All those array types share the same set of functionality. They provide the same set of member functions for read access and can arbitrarily be given to any ILNumerics function as parameter. ILNumerics handles implicit conversions between compatible types automatically. The only exception: Input arrays and return arrays are immutable. This means, they lag of any member function or setter property which could be used to alter the content of the array. Also, these types cannot be converted to output parameters.

2. using() blocks as artificial scopes

Function bodies of any computational function must be enclosed with a construct according to the following scheme:

C# Code
1
2
3
4
using(ILScope.Enter(in1,in2,...)) {
// function body...
 
}

In Visual Basic, a corresponding Using ... End Using block is to be used.

The ILNumerics.ILScope.Enter() function creates an artificial scope with the following functionality:

  • All arrays created inside the scope are disposed once the block was left.
  • Input parameters are disposed after the block was left.

Therefore, the ILScope.Enter() function receives all input parameters (i.e. all parameters of type ILInArray<T>, ILInCell or ILInLogical) which may appear in the parameter list.

3. Output parameters

Output parameters are used whenever a function must return more than one array. In ILNumerics, output parameters act like a reference to another (local) array in the calling scope. If a function receives a valid object reference for an output parameter (i.e. an empty array or anything but null), this signals the function to actually do all computations needed to provide the parameter. Otherwise null was given and the function can choose another, more efficient way of computation without providing the output parameter.

Modifications and assignments to output parameters immediately alter the underlying source array. In order to make this work, output parameters can be modified in one of the following ways only. Consider an output variable named frequencies from the example below:

  • frequencies[0,full] = ... - use the setter properties to alter parts of the array. All subarray access rules are permitted. Alternatively, the SetRange() method may be used if the language used does not support those properties.
  • frequencies.a = ... - reassign a new array to the output parameter. It is important not to forget to use the .a setter property. For languages which do not support such properties (f.e. Visual Basic .NET) the frequencies.Assign() method of an array is to be used as alternative.

By using output parameters in ILNumerics, neither the out nor the ref keyword (C#) are required. The user easily decides, which parameter to request from a function, by simply giving any local array or null or even nothing as parameter. There is no need to declare any dummy variables if an specific output parameter is not needed.

Example of a computational function in ILNumerics

Note, that in the body only local array types are declared - all other types go in the function head. Note also, the parameter list may just as well contain any parameter type useful for the function. In the example, a parameter of system type double is used next to our array types.

Best Practices for Writing ILNumerics Functions

Next to the obligatory rules, the following hints are optional. The user may decide to use them in order to create more readable and convenient code.

  • All parameters - where applicable - should be defined as optional parameters (see: C# language reference) at the end of the parameter list. Default value for all array parameters should be null. This allows the user to pick only those output parameters, which are really needed in long parameter lists.

    C# Code
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    
    public static YourFunc(ILInArray<double> inA, 
                           ILOutArray<double> outB = null) { 
        // ...
    }
     
    // ... allowed use of the function: 
    ILArray<double> I = zeros(5,4); 
    ILArray<double> O = empty(); 
    // request the additional output value: 
    YourFunc(I,O); 
    // do not request the additional output value: 
    YourFunc(I);

  • Prepend parameter names with corresponding prefixes 'in' and 'out'. This makes parameters easily distinguishable from local variables.
  • Input paramters are immutable - it is impossible to change their content. In the context of input parameter checking, the following scheme has proved to be efficient:
    • For every input parameter, a corresponding local parameter should be declared.
    • The check() function from the ILMath class is convenient to check against any parameter being null and to define constraints for them. Input parameters are therefore transformed into their corresponding local variables, including all neccessary parameter checks.
    • Optimally, the check function would be the only place to reference any input parameter in the function body.

    Example of efficient parameter checking in ILNumerics:

    C# Code
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    
    public static void MyFunc(ILInArray<double> inA, 
                              ILInArray<double> inB, 
                              ILInArray<double> inC) {
      using (ILScope.Enter(inA, inB, inC)) {
        // declare local arrays for all input parameters
        ILArray<double> A,B,C; 
        // parameter checking via "check" function: 
        A = check(inA);            // checks on null
        B = check(inB, (b) => {    // checks on column vector also
            if (!b.IsVector) 
                throw new ILArgumentException("inB must be a vector!"); 
            return (b.IsRowVector) ? b.C : b.T;  
        });  
        // parameter checking the classical way:
        if (isnull(inC) || inC.IsEmpty 
        || !inC.IsColumnVector || inC.Length != inB.D[0])
            throw new ILArgumentException("invalid parameter inC provided");
        // proceed with local variables A,B and C...
        // ...
      }
    }

    By following these hints, assignments to input parameters are avoided completely. By doing so, ILNumerics is able to optimize memory usage furthermore. See Optimizing Performance for details.