Dynamic Shape Input (Setting the Shape Range)

This section describes the key APIs, API call sequence, and sample code for setting the shape range in the dynamic shape input scenario.

API Call Sequence

If the input shape is dynamic, call aclmdlSetDatasetTensorDesc to set the input tensor description (mainly the shape information) before model execution. After model execution: Call aclmdlGetDatasetTensorDesc to obtain the tensor description information dynamically output by the model, and then call the operation API under aclTensorDesc to obtain the memory size occupied by the output tensor data, tensor format, and tensor dimension information.

The procedure is described as follows:

  1. Build a model.

    In the model inference scenario, when the ATC tool is used to convert the dynamic-shape input data, the input_shape parameter is used to set the input shape range. For details about the parameters, see Command-Line Options > Basic Options > Input Options > --input_shape in ATC.

  2. Load the model.

    For details about the model loading workflow, see Loading a Model. After the model is successfully loaded, the model ID is returned.

  3. Create data of the aclmdlDataset type to describe the input and output of model execution.

    For details about the call sequence, see Preparing Input/Output Data Structure for Model Execution.

    Notes:

    • If the size obtained by calling aclmdlGetInputSizeByIndex is 0, the input shape is dynamic. You can estimate a large input memory based on the actual situation.
    • If the size obtained by calling aclmdlGetOutputSizeByIndex is 0, the output shape is dynamic. You can estimate a large output buffer based on the actual situation, or the system allocates the output buffer of the corresponding index. For details, see aclmdlGetOutputSizeByIndex.
  4. After the model is successfully loaded and before the model is executed, call aclmdlSetDatasetTensorDesc to set the tensor description (mainly the shape information) of the dynamic shape input.

    Set the shape information, including the number of dimensions and dimension size, when creating the tensor description by calling aclCreateTensorDesc. The number of dimensions and dimension size must be within the input shape range set during model building. For details about model building, see Build the model.

  5. (Optional) Create an Allocator descriptor and register an Allocator.
    Note: Currently, external Allocator can be used to manage memory only in the dynamic-shape model inference scenario. The API for registering Allocator must be used together with aclmdlExecuteAsync and must be called before aclmdlExecuteAsync.
    1. Call aclrtAllocatorCreateDesc to create an Allocator descriptor.
    2. Call aclrtAllocatorSetObjToDesc, aclrtAllocatorSetAllocFuncToDesc, aclrtAllocatorSetGetAddrFromBlockFuncToDesc and aclrtAllocatorSetFreeFuncToDesc to set the Allocator object and callback function.
    3. Call aclrtAllocatorRegister to register the Allocator and bind the Allocator to the stream. The same stream must be used during model execution.
    4. After an Allocator is registered, call aclrtAllocatorDestroyDesc to destroy the Allocator descriptor.
  6. Execute the model.
    • Synchronous inference is supported, for example, by calling aclmdlExecute.
    • Asynchronous inference is also supported. For example, the aclmdlExecuteAsync API is called. However, in asynchronous scenarios, the stream synchronization API (for example, aclrtSynchronizeStream) needs to be called to confirm that the asynchronous task is complete.

      In the asynchronous inference scenario, only the aclmdlExecuteAsync API supports the Allocator registered by the user, that is, the Allocator registered in step 5.

  7. Obtain the model execution result.

    Call the aclmdlGetDatasetTensorDesc API to obtain the tensor description of the dynamic shape output, and then use the operation API of the aclTensorDesc data type to obtain the attributes of the tensor description. The following uses the size (space occupied by the tensor data) as an example, then, the data of the corresponding size is read from the memory.

  8. (Optional) If an Allocator is registered in 5, deregister and destroy registered Allocator.

    The Allocator registered by a user is bound to a stream. If the Allocator needs to be released or destroyed, call aclrtAllocatorUnregister to deregister the Allocator before releasing the stream. Then release the stream resources and destroy the Allocator.

Sample Code for Synchronous Inference Scenarios

The following is a code example of key steps of model inference. It is for reference only and cannot be directly copied for compilation and running. After APIs are called, you need to add exception handling branches and record error logs and info logs.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
......

//In this sample, assume that the first input of the model is dynamic with an index of 0, and the first output of the model is dynamic with also an index of 0.

// 1. Load the model. After the model is successfully loaded, set the description of the dynamic input tensor, mainly the shape information.
......

// 2. Prepare the model description information modelDesc_, model input data input_, and model output data output_.
//Pay attention to the following:
//If the size obtained by using aclmdlGetInputSizeByIndex is 0, the input shape is dynamic. A larger input buffer can be estimated based on the actual situation.
//If the size obtained by using aclmdlGetOutputSizeByIndex is 0, the output shape is dynamic. A larger output buffer can be estimated based on the actual situation.
// ......

// 3. Customize a function to set the description of the dynamic input tensor.
void SetTensorDesc()
{
        // ......
    //Create the tensor description.
    //The shape must be the same as that of the specified input data.
	int64_t shapes[4] = {1, 3, 224, 224};
	aclTensorDesc *inputDesc = aclCreateTensorDesc(ACL_FLOAT, 4, shapes, ACL_FORMAT_NCHW);
	//Set the description of the dynamic input tensor whose index is 0.
	aclError ret = aclmdlSetDatasetTensorDesc(input_, inputDesc, 0);
        // ......
}
// 4. Customize a function to execute the model and obtain the tensor description of the dynamic output.
void ModelExecute()
{
	aclError ret;
	//Call a custom API to set the description of the dynamic input tensor.
	SetTensorDesc();
        //Execute the model.
	ret = aclmdlExecute(modelId, input_, output_);
	//Obtain the description of the dynamic output tensor whose index is 0.
        aclTensorDesc *outputDesc = aclmdlGetDatasetTensorDesc(output_, 0);
	//Use the operation API of the aclTensorDesc type to obtain the outputDesc attribute. You need to obtain size (size occupied by tensor data) as an example: Then, read the data of the corresponding size from the memory.
	string outputFileName = ss.str();
        FILE *outputFile = fopen(outputFileName.c_str(), "wb");
	size_t outputDesc_size = aclGetTensorDescSize(outputDesc);
        aclDataBuffer *dataBuffer = aclmdlGetDatasetBuffer(output_, 0);
        void *data = aclGetDataBufferAddr(dataBuffer);
	void *outHostData = nullptr;
        //Call aclrtGetRunMode to obtain the run mode of the software stack and determine whether to transfer data, based on the run mode.
        aclrtRunMode runMode;
        ret = aclrtGetRunMode(&runMode);
        if (runMode == ACL_HOST) {
            ret = aclrtMallocHost(&outHostData, outputDesc_size);
	   //Since the memory allocated for the dynamic shape is large, the actual data size outputDesc_size is used to copy the memory.
	    ret = aclrtMemcpy(outHostData, outputDesc_size, data, outputDesc_size, ACL_MEMCPY_DEVICE_TO_HOST);
	    fwrite(outHostData, outputDesc_size, sizeof(char), outputFile);
            ret = aclrtFreeHost(outHostData);
	} else {
		// if app is running in host, write model output data into result file
                fwrite(data, outputDesc_size, sizeof(char), outputFile);
	}
	fclose(outputFile);
        // ......
}

// 5. Process the model inference result.
......

Sample Code for Asynchronous Inference Scenarios

This section describes the code logic of model inference. In the example, a user registers an Allocator and calls the asynchronous API aclmdlExecuteAsync to perform inference.

After APIs are called, you need to add exception handling branches and record error logs and info logs. The following is a code snippet of key steps only, which is not ready to be built or run.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
......

//The sample code is for reference only. In actual application scenarios, verify the return value.
//In this sample, assume that the first input of the model is dynamic with an index of 0, and the first output of the model is dynamic with also an index of 0.

// 1. Load the model. After the model is successfully loaded, set the description of the dynamic input tensor, mainly the shape information.
......

// 2. Prepare the model description information modelDesc_, model input data input_, and model output data output_.
//Pay attention to the following:
//If the size obtained by using aclmdlGetInputSizeByIndex is 0, the input shape is dynamic. A larger input buffer can be estimated based on the actual situation.
//If the size obtained by using aclmdlGetOutputSizeByIndex is 0, the output shape is dynamic. A larger output buffer can be estimated based on the actual situation.
// ......

// 3. Customize a function to set the description of the dynamic input tensor.
void SetTensorDesc()
{
        // ......
    //Create the tensor description.
    //The shape must be the same as that of the specified input data.
	int64_t shapes = {1, 3, 224, 224};
	aclTensorDesc *inputDesc = aclCreateTensorDesc(ACL_FLOAT, 4, shapes, ACL_FORMAT_NCHW);
	//Set the description of the dynamic input tensor whose index is 0.
	aclError ret = aclmdlSetDatasetTensorDesc(input_, inputDesc, 0);
        // ......
}

// 4. Create an Allocator descriptor and register an Allocator.
//Assume that the allocator is the Allocator object that the user wants to register.
void RegisterCustomAllocator(aclrtAllocator allocator, aclrtStream stream) {
    //6.1 Create AllocatorDesc.
    aclrtAllocatorDesc allocatorDesc = aclrtAllocatorCreateDesc();
    //6.2 Initialize AllocatorDesc and set callback functions related to Allocator memory allocation and deallocation. CustomMallocFunc, CustomFreeFunc, CustomMallocAdviseFunc, and CustomGetBlockAddrFunc are callback functions defined in the C style and are transferred to AllocatorDesc as function pointers.
    aclrtAllocatorSetObjToDesc(allocatorDesc, allocator);
    aclrtAllocatorSetAllocFuncToDesc(allocatorDesc, CustomMallocFunc);
    aclrtAllocatorSetFreeFuncToDesc(allocatorDesc, CustomFreeFunc);
    aclrtAllocatorSetAllocAdviseFuncToDesc(allocatorDesc, CustomMallocAdviseFunc);
    aclrtAllocatorSetGetAddrFromBlockFuncToDesc(allocatorDesc, CustomGetBlockAddrFunc);
    //Register the Allocator and bind it to the stream. The API creates the Allocator based on AllocatorDesc.
    aclrtAllocatorRegister(stream, allocatorDesc);
    //After the Allocator descriptor is used, it can be destroyed.
    aclrtAllocatorDestroyDesc(allocatorDesc);
}


// 5. Customize a function to execute the model and obtain the tensor description of the dynamic output.
void ModelExecute(aclrtStream stream)
{
	aclError ret;
	//Call a custom API to set the description of the dynamic input tensor.
	SetTensorDesc();
       //Execute the model. Only asynchronous APIs support the Allocator registered by the user, and the execution stream must be the same as the stream bound to the registered Allocator.
	ret = aclmdlExecuteAsync(modelId, input_, output_, stream);
       //If the asynchronous API is successfully called, the task is successfully delivered. Before obtaining the result, call the call synchronization API to ensure that the task has been executed.
        aclrtSynchronizeStream(stream);
       //Obtain the description of the dynamic output tensor whose index is 0.
        aclTensorDesc *outputDesc = aclmdlGetDatasetTensorDesc(output_, 0);
       //Use the operation API of the aclTensorDesc type to obtain the outputDesc attribute. You need to obtain size (size occupied by tensor data) as an example: Then, read the data of the corresponding size from the memory.
	string outputFileName = ss.str();
        FILE *outputFile = fopen(outputFileName.c_str(), "wb");
	size_t outputDesc_size = aclGetTensorDescSize(outputDesc);
        aclDataBuffer *dataBuffer = aclmdlGetDatasetBuffer(output_, 0);
        void *data = aclGetDataBufferAddr(dataBuffer);
	void *outHostData = nullptr;
       //Call aclrtGetRunMode to obtain the run mode of the software stack and determine whether to transfer data, based on the run mode.
        aclrtRunMode runMode;
        ret = aclrtGetRunMode(&runMode);
        if (runMode == ACL_HOST) {
            ret = aclrtMallocHost(&outHostData, outputDesc_size);
           //Since the memory allocated for the dynamic shape is large, the actual data size outputDesc_size is used to copy the memory.
	    ret = aclrtMemcpy(outHostData, outputDesc_size, data, outputDesc_size, ACL_MEMCPY_DEVICE_TO_HOST);
	    fwrite(outHostData, outputDesc_size, sizeof(char), outputFile);
            ret = aclrtFreeHost(outHostData);
	    } else {
		// if app is running in host, write model output data into result file
                fwrite(data, outputDesc_size, sizeof(char), outputFile);
	    }
	fclose(outputFile);
        // ......
}

// 6. Process the model inference result.
......

// 7. Deregister and destroy the registered Allocator.
void UnregisterCustomAllocator(aclrtStream stream) {
     aclrtAllocatorUnregister(stream);
    //Destroy the customized Allocator.
}

......