Skip to content

Commit 97a3d3d

Browse files
committed
add an example for mix of Disjoint Pool and L0
1 parent af5050f commit 97a3d3d

File tree

8 files changed

+283
-16
lines changed

8 files changed

+283
-16
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)
@@ -398,7 +399,7 @@ endif()
398399
install(FILES ${CMAKE_SOURCE_DIR}/LICENSE.TXT
399400
DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/doc/${PROJECT_NAME}/")
400401

401-
install(FILES examples/basic/basic.c
402+
install(FILES examples/advanced/disjoint_level_zero.c examples/basic/basic.c
402403
DESTINATION "${CMAKE_INSTALL_DOCDIR}/examples")
403404

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

README.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,11 @@ 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 that allocates [USM memory from the GPU device](https://github.com/oneapi-src/unified-memory-framework/blob/main/examples/basic/gpu_shared.c) using the
24+
Level Zero API and UMF Level Zero provider.
2225

2326
## Build
2427

@@ -100,6 +103,7 @@ List of options provided by CMake:
100103
| UMF_BUILD_GPU_TESTS | Build UMF GPU tests | ON/OFF | OFF |
101104
| UMF_BUILD_BENCHMARKS | Build UMF benchmarks | ON/OFF | OFF |
102105
| UMF_BUILD_EXAMPLES | Build UMF examples | ON/OFF | ON |
106+
| UMF_BUILD_GPU_EXAMPLES | Build UMF GPU examples | ON/OFF | OFF |
103107
| UMF_ENABLE_POOL_TRACKING | Build UMF with pool tracking | ON/OFF | ON |
104108
| UMF_DEVELOPER_MODE | Treat warnings as errors and enables additional checks | ON/OFF | OFF |
105109
| UMF_FORMAT_CODE_STYLE | Add clang and cmake -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 "
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: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,27 @@
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

examples/basic/gpu_shared.c

Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
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+
assert(ze_result == ZE_RESULT_SUCCESS);
27+
28+
// Discover all the driver instances
29+
uint32_t driverCount = 0;
30+
ze_result = zeDriverGet(&driverCount, NULL);
31+
assert(ze_result == ZE_RESULT_SUCCESS);
32+
assert(driverCount > 0);
33+
34+
ze_driver_handle_t *all_drivers =
35+
(ze_driver_handle_t *)calloc(driverCount, sizeof(ze_driver_handle_t));
36+
ze_result = zeDriverGet(&driverCount, all_drivers);
37+
assert(ze_result == ZE_RESULT_SUCCESS);
38+
39+
// Find a driver instance with a GPU device
40+
for (uint32_t i = 0; i < driverCount; ++i) {
41+
assert(all_drivers[i] != NULL);
42+
43+
// Discover all the devices
44+
uint32_t deviceCount = 0;
45+
ze_result = zeDeviceGet(all_drivers[i], &deviceCount, NULL);
46+
assert(ze_result == ZE_RESULT_SUCCESS);
47+
assert(deviceCount > 0);
48+
49+
ze_device_handle_t *all_devices = (ze_device_handle_t *)calloc(
50+
deviceCount, sizeof(ze_device_handle_t));
51+
ze_result = zeDeviceGet(all_drivers[i], &deviceCount, all_devices);
52+
assert(ze_result == ZE_RESULT_SUCCESS);
53+
54+
// Loop through the devices to find a GPU device
55+
for (uint32_t d = 0; d < deviceCount; ++d) {
56+
assert(all_devices[d] != NULL);
57+
58+
ze_device_properties_t device_properties = {0};
59+
device_properties.stype = ZE_STRUCTURE_TYPE_DEVICE_PROPERTIES;
60+
ze_result =
61+
zeDeviceGetProperties(all_devices[d], &device_properties);
62+
assert(ze_result == ZE_RESULT_SUCCESS);
63+
64+
if (ZE_DEVICE_TYPE_GPU == device_properties.type) {
65+
*driver = all_drivers[i];
66+
*device = all_devices[d];
67+
printf("GPU device found: %p\n", device);
68+
break;
69+
}
70+
}
71+
72+
free(all_devices);
73+
if (NULL != driver) {
74+
break;
75+
}
76+
}
77+
78+
// In case no suitable device was found, print an error message and exit
79+
if (NULL == device) {
80+
printf("Failed to find GPU device!");
81+
return -1;
82+
}
83+
84+
// Create context
85+
ze_context_desc_t ctxtDesc = {ZE_STRUCTURE_TYPE_CONTEXT_DESC, NULL, 0};
86+
ze_result = zeContextCreate(*driver, &ctxtDesc, context);
87+
assert(ze_result == ZE_RESULT_SUCCESS);
88+
assert(*context != NULL);
89+
90+
// Free the memory
91+
free(all_drivers);
92+
return 0;
93+
}
94+
95+
int main(void) {
96+
97+
// A result object for storing UMF API result status
98+
umf_result_t res;
99+
100+
// Initialize Level Zero
101+
ze_driver_handle_t hDriver;
102+
ze_device_handle_t hDevice;
103+
ze_context_handle_t hContext;
104+
int ret = level_zero_init(&hDriver, &hDevice, &hContext);
105+
if (ret < 0) {
106+
// Initialization failed or no GPU devices was found
107+
return -1;
108+
}
109+
110+
// Setup parameters for the Level Zero memory provider. It will be used for
111+
// allocating memory from Level Zero devices.
112+
level_zero_memory_provider_params_t ze_memory_provider_params;
113+
ze_memory_provider_params.level_zero_context_handle = hContext;
114+
ze_memory_provider_params.level_zero_device_handle = hDevice;
115+
// Set the memory type to shared to allow the memory to be accessed on both
116+
// CPU and GPU.
117+
ze_memory_provider_params.memory_type = UMF_MEMORY_TYPE_SHARED;
118+
119+
// Create Level Zero memory provider
120+
umf_memory_provider_handle_t ze_memory_provider;
121+
res = umfMemoryProviderCreate(umfLevelZeroMemoryProviderOps(),
122+
&ze_memory_provider_params,
123+
&ze_memory_provider);
124+
if (res != UMF_RESULT_SUCCESS) {
125+
printf("Failed to create a memory provider!");
126+
goto level_zero_destroy;
127+
}
128+
129+
printf("Level Zero memory provider created at %p\n",
130+
(void *)ze_memory_provider);
131+
132+
// Setup parameters for the Disjoint Pool. It will be used for managing the
133+
// memory allocated using memory provider.
134+
umf_disjoint_pool_params_t disjoint_memory_pool_params =
135+
umfDisjointPoolParamsDefault();
136+
// Set the Slab Min Size to 64KB - the page size for GPU allocations
137+
disjoint_memory_pool_params.SlabMinSize = 64 * 1024L;
138+
// We would keep only single slab per each allocation bucket
139+
disjoint_memory_pool_params.Capacity = 1;
140+
// Set the maximum poolable size to 64KB - objects with size above this
141+
// limit will not be stored/allocated from the pool.
142+
disjoint_memory_pool_params.MaxPoolableSize = 64 * 1024L;
143+
// Enable tracing
144+
disjoint_memory_pool_params.PoolTrace = 1;
145+
146+
// Create Disjoint Pool memory pool.
147+
umf_memory_pool_handle_t ze_disjoint_memory_pool;
148+
res = umfPoolCreate(umfDisjointPoolOps(), ze_memory_provider,
149+
&disjoint_memory_pool_params, UMF_POOL_CREATE_FLAG_NONE,
150+
&ze_disjoint_memory_pool);
151+
if (res != UMF_RESULT_SUCCESS) {
152+
printf("Failed to create a memory pool!");
153+
goto memory_provider_destroy;
154+
}
155+
156+
printf("Disjoint Pool created at %p\n", (void *)ze_disjoint_memory_pool);
157+
158+
// Allocate some memory from the pool
159+
void *ptr = umfPoolMalloc(ze_disjoint_memory_pool, sizeof(int));
160+
if (res != UMF_RESULT_SUCCESS) {
161+
printf("Failed to allocate memory from the memory pool!");
162+
goto memory_pool_destroy;
163+
}
164+
165+
// Use allocated memory
166+
*(int *)ptr = 1;
167+
168+
// Free allocated memory
169+
res = umfFree(ptr);
170+
if (res != UMF_RESULT_SUCCESS) {
171+
printf("Failed to free memory to the pool!");
172+
goto memory_pool_destroy;
173+
}
174+
printf("Freed memory at %p\n", ptr);
175+
176+
// Cleanup
177+
umfPoolDestroy(ze_disjoint_memory_pool);
178+
umfMemoryProviderDestroy(ze_memory_provider);
179+
zeContextDestroy(hContext);
180+
return 0;
181+
182+
memory_pool_destroy:
183+
umfPoolDestroy(ze_disjoint_memory_pool);
184+
185+
memory_provider_destroy:
186+
umfMemoryProviderDestroy(ze_memory_provider);
187+
188+
level_zero_destroy:
189+
zeContextDestroy(hContext);
190+
return -1;
191+
}

scripts/docs_config/example-usage.rst

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,27 @@
22
:linenothreshold: 10
33

44
==============================================================================
5-
Example usage
5+
Examples
66
==============================================================================
77

88
This section will walk you through a basic usage
9-
of :ref:`memory provider <glossary-memory-provider>`
10-
and :ref:`pool allocator <glossary-pool-allocator>`. OS Memory Provider
11-
and Scalable Pool will be used for this purpose.
9+
of :ref:`memory provider <glossary-memory-provider>` API
10+
and :ref:`pool allocator <glossary-pool-allocator>` API.
11+
There are two examples described here: basic and GPU shared.
1212

13-
There are also other memory pools available in the UMF. See `README`_ for
14-
more information.
13+
Basic example uses OS Memory Provider and Scalable Pool,
14+
while the GPU shared uses Level Zero Memory Provider and Disjoint Pool .
1515

16-
There are some API functions that are supported only when the UMF is built
17-
with the memory tracking enabled (UMF_ENABLE_POOL_TRACKING=ON). These functions
18-
are explicitly described in this tutorial as requiring memory tracking.
16+
There are also other memory providers and pools available in the UMF.
17+
See `README`_ for more information.
18+
19+
.. note::
20+
There are some API functions that are supported only when the UMF is built
21+
with the memory tracking enabled (UMF_ENABLE_POOL_TRACKING=ON). These functions
22+
are explicitly described in this tutorial as requiring memory tracking.
23+
24+
Basic
25+
==============================================================================
1926

2027
You can find the full example code in the `examples/basic/basic.c`_ file
2128
in the UMF repository.
@@ -112,7 +119,16 @@ Freeing memory is as easy as can be::
112119
If the memory tracking is disabled, you should call a different function:
113120
:any:`umfPoolFree`.
114121

122+
GPU shared memory
123+
==============================================================================
124+
125+
You can find the full example code in the `examples/basic/gpu_shared.c`_ file
126+
in the UMF repository.
127+
128+
TODO
129+
115130
.. _examples/basic/basic.c: https://github.com/oneapi-src/unified-memory-framework/blob/main/examples/basic/basic.c
131+
.. _examples/basic/gpu_shared.c: https://github.com/oneapi-src/unified-memory-framework/blob/main/examples/basic/gpu_shared.c
116132
.. _README: https://github.com/oneapi-src/unified-memory-framework/blob/main/README.md#memory-pool-managers
117133
.. _provider_os_memory.h: https://github.com/oneapi-src/unified-memory-framework/blob/main/include/umf/providers/provider_os_memory.h
118134
.. _pool_scalable.h: https://github.com/oneapi-src/unified-memory-framework/blob/main/include/umf/pools/pool_scalable.h

test/test_installation.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,8 @@ def _create_match_list(self) -> List[str]:
113113
for example_dir in examples_dirs
114114
for file_path in example_dir.iterdir()
115115
]
116+
# TODO - why we do not preserve example folders?
117+
examples = sorted(examples)
116118
examples.insert(0, "share/doc/umf/examples")
117119
share.extend(examples)
118120
share.append("share/doc/umf/LICENSE.TXT")

0 commit comments

Comments
 (0)