Skip to content

Commit f8e27f8

Browse files
committed
add an example for mix of Disjoint Pool and L0
1 parent e331e55 commit f8e27f8

File tree

8 files changed

+336
-15
lines changed

8 files changed

+336
-15
lines changed

.github/workflows/gpu.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ jobs:
4141
-DUMF_BUILD_BENCHMARKS=ON
4242
-DUMF_BUILD_TESTS=ON
4343
-DUMF_BUILD_GPU_TESTS=ON
44+
-DUMF_BUILD_GPU_EXAMPLES=ON
4445
-DUMF_FORMAT_CODE_STYLE=ON
4546
-DUMF_DEVELOPER_MODE=ON
4647
-DUMF_BUILD_LIBUMF_POOL_DISJOINT=ON

CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ option(UMF_BUILD_GPU_TESTS "Build UMF GPU tests" OFF)
2222
option(UMF_BUILD_BENCHMARKS "Build UMF benchmarks" OFF)
2323
option(UMF_BUILD_BENCHMARKS_MT "Build UMF multithreaded benchmarks" OFF)
2424
option(UMF_BUILD_EXAMPLES "Build UMF examples" ON)
25+
option(UMF_BUILD_GPU_EXAMPLES "Build UMF GPU examples" OFF)
2526
option(UMF_ENABLE_POOL_TRACKING "Build UMF with pool tracking" ON)
2627
option(UMF_DEVELOPER_MODE "Enable developer checks, treats warnings as errors"
2728
OFF)
@@ -435,7 +436,7 @@ endif()
435436
install(FILES ${CMAKE_SOURCE_DIR}/LICENSE.TXT
436437
DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/doc/${PROJECT_NAME}/")
437438

438-
install(FILES examples/basic/basic.c
439+
install(FILES examples/basic/gpu_shared_memory.c examples/basic/basic.c
439440
DESTINATION "${CMAKE_INSTALL_DOCDIR}/examples")
440441

441442
# Add the include directory and the headers target to the install.

README.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,12 @@ The Unified Memory Framework (UMF) is a library for constructing allocators and
1717

1818
## Usage
1919

20-
For a quick introduction to UMF usage, see [Example usage](https://oneapi-src.github.io/unified-memory-framework/example-usage.html),
21-
which includes the code of the basic [example](https://github.com/oneapi-src/unified-memory-framework/blob/main/examples/basic/basic.c).
20+
For a quick introduction to UMF usage, see
21+
[Example usage](https://oneapi-src.github.io/unified-memory-framework/examples.html),
22+
which includes the code of the basic [example](https://github.com/oneapi-src/unified-memory-framework/blob/main/examples/basic/basic.c)
23+
and the more advanced one that allocates
24+
[USM memory from the GPU device](https://github.com/oneapi-src/unified-memory-framework/blob/main/examples/basic/gpu_shared_memory.c)
25+
using the Level Zero API and UMF Level Zero memory provider.
2226

2327
## Build
2428

@@ -100,6 +104,7 @@ List of options provided by CMake:
100104
| UMF_BUILD_GPU_TESTS | Build UMF GPU tests | ON/OFF | OFF |
101105
| UMF_BUILD_BENCHMARKS | Build UMF benchmarks | ON/OFF | OFF |
102106
| UMF_BUILD_EXAMPLES | Build UMF examples | ON/OFF | ON |
107+
| UMF_BUILD_GPU_EXAMPLES | Build UMF GPU examples | ON/OFF | OFF |
103108
| UMF_ENABLE_POOL_TRACKING | Build UMF with pool tracking | ON/OFF | ON |
104109
| UMF_DEVELOPER_MODE | Treat warnings as errors and enables additional checks | ON/OFF | OFF |
105110
| UMF_FORMAT_CODE_STYLE | Add clang, cmake, and black -format-check and -format-apply targets to make | ON/OFF | OFF |

examples/CMakeLists.txt

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,41 @@ if(UMF_BUILD_LIBUMF_POOL_SCALABLE AND UMF_ENABLE_POOL_TRACKING)
2323

2424
set_tests_properties(${EXAMPLE_NAME} PROPERTIES LABELS "example")
2525

26+
if(WINDOWS)
27+
# append PATH to DLLs
28+
set_property(TEST ${EXAMPLE_NAME} PROPERTY ENVIRONMENT_MODIFICATION
29+
"${DLL_PATH_LIST}")
30+
endif()
31+
else()
32+
message(STATUS "Basic example requires UMF_BUILD_LIBUMF_POOL_SCALABLE and "
33+
"UMF_ENABLE_POOL_TRACKING to be turned ON - skipping")
34+
endif()
35+
36+
if(UMF_BUILD_GPU_EXAMPLES
37+
AND UMF_BUILD_LIBUMF_POOL_DISJOINT
38+
AND UMF_BUILD_LEVEL_ZERO_PROVIDER
39+
AND LINUX)
40+
set(EXAMPLE_NAME umf_example_gpu_shared_memory)
41+
42+
add_umf_executable(
43+
NAME ${EXAMPLE_NAME}
44+
SRCS basic/gpu_shared_memory.c
45+
LIBS umf disjoint_pool ze_loader)
46+
47+
target_include_directories(
48+
${EXAMPLE_NAME}
49+
PRIVATE ${LEVEL_ZERO_INCLUDE_DIRS} ${UMF_CMAKE_SOURCE_DIR}/src/utils
50+
${UMF_CMAKE_SOURCE_DIR}/include)
51+
52+
target_link_directories(${EXAMPLE_NAME} PRIVATE ${LIBHWLOC_LIBRARY_DIRS})
53+
54+
add_test(
55+
NAME ${EXAMPLE_NAME}
56+
COMMAND ${EXAMPLE_NAME}
57+
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
58+
59+
set_tests_properties(${EXAMPLE_NAME} PROPERTIES LABELS "example")
60+
2661
if(WINDOWS)
2762
# append PATH to DLLs
2863
set_property(TEST ${EXAMPLE_NAME} PROPERTY ENVIRONMENT_MODIFICATION
@@ -31,6 +66,7 @@ if(UMF_BUILD_LIBUMF_POOL_SCALABLE AND UMF_ENABLE_POOL_TRACKING)
3166
else()
3267
message(
3368
STATUS
34-
"Basic example requires UMF_BUILD_LIBUMF_POOL_SCALABLE and UMF_ENABLE_POOL_TRACKING
35-
to be turned ON - skipping")
69+
"GPU shared memory example requires UMF_BUILD_GPU_EXAMPLES, "
70+
"UMF_BUILD_LEVEL_ZERO_PROVIDER and UMF_BUILD_LIBUMF_POOL_DISJOINT "
71+
"to be turned ON - skipping")
3672
endif()

examples/README.md

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,28 @@
11
# Examples
22

3-
This directory contains examples of UMF usage. Each example has a brief description below.
3+
This directory contains examples of UMF usage. Each example has a brief
4+
description below.
45

56
## Basic
67

7-
This example covers the basics of UMF API. It walks you through a basic usage of a memory provider and a pool allocator. OS memory provider and Scalable pool are used for this purpose.
8+
This example covers the basics of UMF API. It walks you through a basic usage
9+
of a memory provider and a pool allocator. OS memory provider and Scalable pool
10+
are used for this purpose.
811

912
### Required CMake configuration flags
1013
* UMF_BUILD_LIBUMF_POOL_SCALABLE=ON
1114
* UMF_ENABLE_POOL_TRACKING=ON
15+
16+
## GPU shared memory
17+
18+
This example demonstrates the usage of Intel's Level Zero API for accessing GPU
19+
memory. It initializes the Level Zero driver, discovers all the driver
20+
instances, and creates GPU context for the first found GPU device. Next, it
21+
sets up a combination of UMF Level Zero memory provider and a Disjoint Pool
22+
memory pool to allocate from shared memory. If any step fails, the program
23+
cleans up and exits with an error status.
24+
25+
### Required CMake configuration flags
26+
* UMF_BUILD_GPU_EXAMPLES=ON
27+
* UMF_BUILD_LIBUMF_POOL_DISJOINT=ON
28+
* UMF_BUILD_LEVEL_ZERO_PROVIDER=ON

examples/basic/gpu_shared_memory.c

Lines changed: 244 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,244 @@
1+
/*
2+
*
3+
* Copyright (C) 2024 Intel Corporation
4+
*
5+
* Under the Apache License v2.0 with LLVM Exceptions. See LICENSE.TXT.
6+
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7+
*
8+
*/
9+
10+
#include <assert.h>
11+
#include <stdio.h>
12+
#include <stdlib.h>
13+
#include <ze_api.h>
14+
15+
#include "umf/memory_pool.h"
16+
#include "umf/pools/pool_disjoint.h"
17+
#include "umf/providers/provider_level_zero.h"
18+
19+
// This function initializes the Level Zero driver and creates a context in the
20+
// first GPU device found.
21+
int level_zero_init(ze_driver_handle_t *driver, ze_device_handle_t *device,
22+
ze_context_handle_t *context) {
23+
24+
// Initialize the Level Zero driver
25+
ze_result_t ze_result = zeInit(0);
26+
if (ze_result != ZE_RESULT_SUCCESS) {
27+
fprintf(stderr, "Failed to initialize Level Zero!\n");
28+
return -1;
29+
}
30+
31+
// Discover all the driver instances
32+
uint32_t driverCount = 0;
33+
ze_result = zeDriverGet(&driverCount, NULL);
34+
if (ze_result != ZE_RESULT_SUCCESS) {
35+
fprintf(stderr, "Failed to get number of Level Zero drivers!\n");
36+
return -1;
37+
}
38+
if (driverCount == 0) {
39+
fprintf(stderr, "There are no Level Zero drivers on the system!\n");
40+
return -1;
41+
}
42+
43+
ze_driver_handle_t *all_drivers =
44+
(ze_driver_handle_t *)calloc(driverCount, sizeof(ze_driver_handle_t));
45+
if (all_drivers == NULL) {
46+
fprintf(stderr, "Failed to allocate memory for the list of Level Zero "
47+
"drivers!\n");
48+
return -1;
49+
}
50+
51+
ze_result = zeDriverGet(&driverCount, all_drivers);
52+
if (ze_result != ZE_RESULT_SUCCESS) {
53+
fprintf(stderr, "Failed to get list of Level Zero drivers!\n");
54+
goto drivers_destroy;
55+
}
56+
57+
// Find a driver instance with a GPU device
58+
ze_device_handle_t *all_devices = NULL;
59+
for (uint32_t i = 0; i < driverCount; ++i) {
60+
if (all_drivers[i] == NULL) {
61+
fprintf(stderr, "Error - driver handle can't be NULL!\n");
62+
goto drivers_destroy;
63+
}
64+
65+
// Discover all the devices
66+
uint32_t deviceCount = 0;
67+
ze_result = zeDeviceGet(all_drivers[i], &deviceCount, NULL);
68+
if (ze_result != ZE_RESULT_SUCCESS) {
69+
fprintf(stderr, "Failed to get number of Level Zero devices!\n");
70+
goto drivers_destroy;
71+
}
72+
if (deviceCount == 0) {
73+
fprintf(stderr, "There are no Level Zero devices on the system!\n");
74+
goto drivers_destroy;
75+
}
76+
77+
all_devices = realloc(deviceCount, sizeof(ze_device_handle_t));
78+
if (all_devices == NULL) {
79+
fprintf(stderr, "Failed to allocate memory for the list of Level "
80+
"Zero devices!\n");
81+
goto drivers_destroy;
82+
}
83+
84+
ze_result = zeDeviceGet(all_drivers[i], &deviceCount, all_devices);
85+
if (ze_result != ZE_RESULT_SUCCESS) {
86+
fprintf(stderr, "Failed to get list of Level Zero devices!\n");
87+
goto devices_destroy;
88+
}
89+
90+
// Loop through the devices to find a GPU device
91+
for (uint32_t d = 0; d < deviceCount; ++d) {
92+
if (all_devices[d] == NULL) {
93+
fprintf(stderr, "Error - device handle can't be NULL!\n");
94+
goto devices_destroy;
95+
}
96+
97+
ze_device_properties_t device_properties = {0};
98+
device_properties.stype = ZE_STRUCTURE_TYPE_DEVICE_PROPERTIES;
99+
ze_result =
100+
zeDeviceGetProperties(all_devices[d], &device_properties);
101+
if (ze_result != ZE_RESULT_SUCCESS) {
102+
fprintf(stderr,
103+
"Failed to get Level Zero device properties!\n");
104+
goto devices_destroy;
105+
}
106+
107+
if (ZE_DEVICE_TYPE_GPU == device_properties.type) {
108+
*driver = all_drivers[i];
109+
*device = all_devices[d];
110+
printf("GPU device found: %s\n", device_properties.name);
111+
break;
112+
}
113+
}
114+
115+
// If we found driver and device we could break from the loop
116+
if (NULL != *driver && NULL != *device) {
117+
break;
118+
}
119+
}
120+
121+
// In case no suitable device was found, print an error message and exit
122+
if (NULL == *device) {
123+
fprintf(stderr, "Failed to find a Level Zero device!\n");
124+
goto devices_destroy;
125+
}
126+
127+
// Create context
128+
ze_context_desc_t ctxtDesc = {ZE_STRUCTURE_TYPE_CONTEXT_DESC, NULL, 0};
129+
ze_result = zeContextCreate(*driver, &ctxtDesc, context);
130+
if (ze_result != ZE_RESULT_SUCCESS || context == NULL) {
131+
fprintf(stderr, "Failed to create Level Zero context!\n");
132+
goto devices_destroy;
133+
}
134+
135+
// Free the memory
136+
free(all_devices);
137+
free(all_drivers);
138+
return 0;
139+
140+
// Cleanup after errors
141+
devices_destroy:
142+
free(all_devices);
143+
144+
drivers_destroy:
145+
free(all_drivers);
146+
return -1;
147+
}
148+
149+
int main(void) {
150+
// A result object for storing UMF API result status
151+
umf_result_t res;
152+
153+
// Initialize Level Zero
154+
ze_driver_handle_t hDriver;
155+
ze_device_handle_t hDevice;
156+
ze_context_handle_t hContext;
157+
int ret = level_zero_init(&hDriver, &hDevice, &hContext);
158+
if (ret < 0) {
159+
fprintf(stderr, "Level Zero initialization failed!\n");
160+
return -1;
161+
}
162+
163+
// Setup parameters for the Level Zero memory provider. It will be used for
164+
// allocating memory from Level Zero devices.
165+
level_zero_memory_provider_params_t ze_memory_provider_params;
166+
ze_memory_provider_params.level_zero_context_handle = hContext;
167+
ze_memory_provider_params.level_zero_device_handle = hDevice;
168+
// Set the memory type to shared to allow the memory to be accessed on both
169+
// CPU and GPU.
170+
ze_memory_provider_params.memory_type = UMF_MEMORY_TYPE_SHARED;
171+
172+
// Create Level Zero memory provider
173+
umf_memory_provider_handle_t ze_memory_provider;
174+
res = umfMemoryProviderCreate(umfLevelZeroMemoryProviderOps(),
175+
&ze_memory_provider_params,
176+
&ze_memory_provider);
177+
if (res != UMF_RESULT_SUCCESS) {
178+
fprintf(stderr, "Failed to create a memory provider!\n");
179+
goto level_zero_destroy;
180+
}
181+
182+
printf("Level Zero memory provider created at %p\n",
183+
(void *)ze_memory_provider);
184+
185+
// Setup parameters for the Disjoint Pool. It will be used for managing the
186+
// memory allocated using memory provider.
187+
umf_disjoint_pool_params_t disjoint_memory_pool_params =
188+
umfDisjointPoolParamsDefault();
189+
// Set the Slab Min Size to 64KB - the page size for GPU allocations
190+
disjoint_memory_pool_params.SlabMinSize = 64 * 1024L;
191+
// We would keep only single slab per each allocation bucket
192+
disjoint_memory_pool_params.Capacity = 1;
193+
// Set the maximum poolable size to 64KB - objects with size above this
194+
// limit will not be stored/allocated from the pool.
195+
disjoint_memory_pool_params.MaxPoolableSize = 64 * 1024L;
196+
// Enable tracing
197+
disjoint_memory_pool_params.PoolTrace = 1;
198+
199+
// Create Disjoint Pool memory pool.
200+
umf_memory_pool_handle_t ze_disjoint_memory_pool;
201+
res = umfPoolCreate(umfDisjointPoolOps(), ze_memory_provider,
202+
&disjoint_memory_pool_params, UMF_POOL_CREATE_FLAG_NONE,
203+
&ze_disjoint_memory_pool);
204+
if (res != UMF_RESULT_SUCCESS) {
205+
fprintf(stderr, "Failed to create a memory pool!\n");
206+
goto memory_provider_destroy;
207+
}
208+
209+
printf("Disjoint Pool created at %p\n", (void *)ze_disjoint_memory_pool);
210+
211+
// Allocate some memory from the pool
212+
int *ptr = (int *)umfPoolMalloc(ze_disjoint_memory_pool, sizeof(int));
213+
if (res != UMF_RESULT_SUCCESS) {
214+
fprintf(stderr, "Failed to allocate memory from the memory pool!\n");
215+
goto memory_pool_destroy;
216+
}
217+
218+
// Use allocated memory
219+
*ptr = 1;
220+
221+
// Free allocated memory
222+
res = umfFree(ptr);
223+
if (res != UMF_RESULT_SUCCESS) {
224+
fprintf(stderr, "Failed to free memory to the pool!\n");
225+
goto memory_pool_destroy;
226+
}
227+
printf("Freed memory at %p\n", (void *)ptr);
228+
229+
// Cleanup
230+
umfPoolDestroy(ze_disjoint_memory_pool);
231+
umfMemoryProviderDestroy(ze_memory_provider);
232+
zeContextDestroy(hContext);
233+
return 0;
234+
235+
memory_pool_destroy:
236+
umfPoolDestroy(ze_disjoint_memory_pool);
237+
238+
memory_provider_destroy:
239+
umfMemoryProviderDestroy(ze_memory_provider);
240+
241+
level_zero_destroy:
242+
zeContextDestroy(hContext);
243+
return -1;
244+
}

0 commit comments

Comments
 (0)