Usage Examples

Using the Default Context in a Single Thread

For simple applications, default contexts are sufficient. When the aclrtSetDevice API is called, Runtime automatically creates a default context, which does not need to be explicitly managed.

The following example illustrates the basic process for using the default context in a single thread:

 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
// 1. Initialize Runtime.
aclInit(nullptr);

// 2. Specify a device. The default context and stream are automatically created.
int32_t deviceId = 0;
aclrtSetDevice(deviceId);

// 3. Create an explicit stream (optional; you can also directly use the default stream by passing nullptr).
aclrtStream stream;
aclrtCreateStream(&stream);

// 4. Dispatch tasks to the stream.
aclrtMemcpyAsync(devPtr, size, hostPtr, size, ACL_MEMCPY_HOST_TO_DEVICE, stream);
myKernel<<<8, nullptr, stream>>>(devPtr, size);
aclrtMemcpyAsync(hostPtr, size, devPtr, size, ACL_MEMCPY_DEVICE_TO_HOST, stream);

// 5. Wait for tasks to complete.
aclrtSynchronizeStream(stream);

// 6. Destroy the explicit stream.
aclrtDestroyStream(stream);

// 7. Reset the device to release the default context and stream.
aclrtResetDeviceForce(deviceId);

//8. Perform deinitialization.
aclFinalize();

Multi-threaded Parallel Computing

When multiple threads use the same device, you are advised to create an independent context for each thread.

// Thread function.
void* threadFunc(void* arg) {
    int32_t deviceId = *(int32_t*)arg;

    // Each thread creates its own context.
    aclrtContext ctx;
    aclrtCreateContext(&ctx, deviceId);

    // Create a stream.
    aclrtStream stream;
    aclrtCreateStream(&stream);

    // Execute the task.
    // Call other task APIs.

// Wait for tasks to complete.
    aclrtSynchronizeStream(stream);

    // Destroy resources.
    aclrtDestroyStream(stream);
    aclrtDestroyContext(ctx);

    return nullptr;
}

// Main thread.
int main() {
    aclInit(nullptr);

    int32_t deviceId = 0;
    aclrtSetDevice(deviceId);

    // Create multiple threads.
    pthread_t threads[4];
    for (int i = 0; i < 4; i++) {
        pthread_create(&threads[i], nullptr, threadFunc, &deviceId);
    }

    // Wait until the threads are complete.
    for (int i = 0; i < 4; i++) {
        pthread_join(threads[i], nullptr);
    }

    aclrtResetDeviceForce(deviceId);
    aclFinalize();
    return 0;
}

Context Sharing Among Multiple Streams

When multiple threads share the same context, ensure the execution order of tasks on streams.

aclrtContext g_ctx; // Global context.

void* threadFunc(void* arg) {
    int threadId = *(int*)arg;

    // Switch to the shared context.
    aclrtSetCurrentContext(g_ctx);

    // Create a stream for the thread.
    aclrtStream stream;
    aclrtCreateStream(&stream);

    // Execute the task on the created stream.
    // Call other task APIs.

    aclrtSynchronizeStream(stream);
    aclrtDestroyStream(stream);

    return nullptr;
}

int main() {
    aclInit(nullptr);

    int32_t deviceId = 0;
    aclrtSetDevice(deviceId);

    // Create a context for multiple threads to share.
    aclrtCreateContext(&g_ctx, deviceId);

    pthread_t threads[4];
    int threadIds[4];
    for (int i = 0; i < 4; i++) {
        threadIds[i] = i;
        pthread_create(&threads[i], nullptr, threadFunc, &threadIds[i]);
    }

    for (int i = 0; i < 4; i++) {
        pthread_join(threads[i], nullptr);
    }

    aclrtDestroyContext(g_ctx);
    aclrtResetDeviceForce(deviceId);
    aclFinalize();
    return 0;
}

Context Switching

Use aclrtSetCurrentContext to switch the context of the current thread.

// Create two contexts on device 0.
aclrtSetDevice(0);
aclrtContext ctx1, ctx2;
aclrtCreateContext(&ctx1, 0);
aclrtCreateContext(&ctx2, 0);

// Switch to ctx1.
aclrtSetCurrentContext(ctx1);
aclrtStream stream1;
aclrtCreateStream(&stream1);
// ... Execute tasks on ctx1 ...

// Switch to ctx2.
aclrtSetCurrentContext(ctx2);
aclrtStream stream2;
aclrtCreateStream(&stream2);
//... Execute tasks on ctx2 ...

// Switching to a context on a different device switches the active device.
aclrtSetDevice(1);
aclrtContext ctx3;
aclrtCreateContext(&ctx3, 1);

aclrtSetCurrentContext(ctx3); // Switch the current context to ctx3. The device is switched to device 1.

Context Parameter Configuration

Use aclrtCtxSetSysParamOpt to set context-level parameters.

aclrtContext ctx;
aclrtCreateContext(&ctx, 0);

// Set context parameters (example: memory configuration).
aclrtCtxSetSysParamOpt(ACL_SYS_PARAM_OPT_XXX, value);

// Get context parameters.
int64_t paramValue;
aclrtCtxGetSysParamOpt(ACL_SYS_PARAM_OPT_XXX, &paramValue);

Obtaining the Default Stream

Each context contains a default stream, which can be obtained by calling the following API:

aclrtContext ctx;
aclrtCreateContext(&ctx, 0);

// Get the default stream of the current context.
aclrtStream defaultStream;
aclrtCtxGetCurrentDefaultStream(&defaultStream);

// Use the default stream (or pass nullptr directly).
aclrtMemcpyAsync(dst, size, src, size, ACL_MEMCPY_DEVICE_TO_DEVICE, nullptr);