Creating arrays on device

Jan 18, 2012 at 9:06 PM
Edited Jan 20, 2012 at 10:49 AM

Currently to do this you would have to create a shared array using thread.AllocateShared() but the size can't be dynamic. I really just wanted a local array anyway for messing around so I started the implementation of CUDAOutputVisitor.VisitArrayCreateExpression() since it was currently not supported.

I didn't do anything to handle reallocation or reassignment. If you try and reassign this new array it will create a memory leak because it won't check for that. But it should automatically free the memory for the pointer otherwise.

This code appears to be working fine for me, as long as you call Cudafy() with eArachitecture set to sm_20 (which is not the default)

public object VisitArrayCreateExpression(ArrayCreateExpression arrayCreateExpression, object data)
{
    // TODO: detect architecture
    //if (CUDALanguage.Architecture != eArchitecture.sm_20)
    //    throw new CudafyLanguageException(CudafyLanguageException.csX_ARE_NOT_SUPPORTED, "Array create expressions when CUDA Arch is not set to sm_20");
    if (!(arrayCreateExpression.Parent is VariableInitializer))
        throw new CudafyLanguageException(CudafyLanguageException.csX_ARE_NOT_SUPPORTED, "Array create expressions when it's not being used with assignment (usage: int[,] array = new int[width,height];)");

    string variableName = (arrayCreateExpression.Parent as VariableInitializer).Name;
    StartNode(arrayCreateExpression);

    // Cast void* to the correct type
    LPar();
    arrayCreateExpression.Type.AcceptVisitor(this, data);
    WriteToken(BinaryOperatorExpression.GetOperatorSymbol(BinaryOperatorType.Multiply), BinaryOperatorExpression.OperatorRole);
    RPar();
            
    // Dynamically allocate the memory for the array: malloc(x * y * z * sizeof(type))
    WriteKeyword("malloc");
    LPar();
	bool isFirst = true;
    List<string> dims = new List<string>();
    foreach (var arg in arrayCreateExpression.Arguments)
    {
        if (isFirst) {
			isFirst = false;
		}
        else {
            Space();
            WriteToken(BinaryOperatorExpression.GetOperatorSymbol(BinaryOperatorType.Multiply), BinaryOperatorExpression.OperatorRole);
            Space();
        }
        dims.Add(arg.ToString());
        arg.AcceptVisitor(this, data);
    }
    Space();
    WriteToken(BinaryOperatorExpression.GetOperatorSymbol(BinaryOperatorType.Multiply), BinaryOperatorExpression.OperatorRole);
    Space();
    WriteKeyword("sizeof");
    LPar();
    arrayCreateExpression.Type.AcceptVisitor(this, data);
    RPar();
    RPar();
    Semicolon();

    // Declare a length variable for every dimension in the array
    for (int d = 0; d < dims.Count; d++)
    {
        formatter.WriteIdentifier(string.Format("int {0}Len{1} = {2}", variableName, d, dims[d]));
        // Don't add the semicolon to the last line, because EndNode() will add it
        if (d + 1 < dims.Count)
            Semicolon();
    }

    // TODO: Verify or Keep Assuming there won't be any return statements decending from the parent block statement

    // Inject code at the end of the parent block statement to free the allocated array
    BlockStatement block = arrayCreateExpression.Ancestors.First(n => n is BlockStatement) as BlockStatement;
    block.Add(new InvocationExpression(
        new IdentifierExpression("free"),
        new IdentifierExpression(variableName)
        ));
            
    return EndNode(arrayCreateExpression);
}

Jan 20, 2012 at 10:54 AM

I just updated this code, I noticed a bug that it was throwing a null reference exception when attempting to pass an array to a function like this: test(new int[] { 1, 2, 3, 4 }); which is not supported by this code right now, so I inserted a check for that...

if (!(arrayCreateExpression.Parent is VariableInitializer))
    throw new CudafyLanguageException(CudafyLanguageException.csX_ARE_NOT_SUPPORTED, "Array create expressions when it's not being used with assignment (usage: int[,] array = new int[width,height];)");

If you are attempting to pass a new array to a function, you should instead write your code like this...

int[] arrayParam = new int[4];
arrayParam[0] = 1;
arrayParam[1] = 2;
arrayParam[2] = 3;
arrayParam[3] = 4;
test(arrayParam);

 

Coordinator
Jan 23, 2012 at 7:09 AM

This also looks good.  Can you post some examples of code using this?  Again like the printf functionality you added we need to check the target architecture.  There are still many many pre-Fermi GPUs out there.  We've hit trouble even assuming 1.3, since users with even 1.0 are out there.

Apr 16, 2012 at 3:12 AM

xer21, yes it could be cool to have few examples of your array creation on device .... as using the default  "thread.AllocateShared()" it's not the best way, when we want to create temporary arrays for local works on device.

Thx

Coordinator
Apr 19, 2012 at 11:13 PM

Yeah not looked into this yet.  If xer21 or someone else can come up with some suitable example code that would be much appreciated!