Examples#

This section will walk you through a basic usageofmemory providerandpool allocator APIs.There are two examples described here: basic and GPU shared.

Basic example uses OS Memory Provider and Scalable Pool,while the GPU shared uses Level Zero Memory Provider and Disjoint Pool.

There are also other memory providers and pools available in the UMF.SeeREADME for more information.

Basic#

You can find the full example code in theexamples/basic/basic.c filein the UMF repository.

Memory provider usage#

First, let’s create a memory provider object for coarse-grained allocations.You have to include theprovider_os_memory.h header withthe OS Memory Provider API:

#include<umf/providers/provider_os_memory.h>

Get a pointer to the OS memory provider operations struct:

umf_memory_provider_ops_t*provider_ops=umfOsMemoryProviderOps();

Get a default OS memory provider parameters. The handle to the parameters objectis returned by theumfOsMemoryProviderParamsCreate function:

umf_os_memory_provider_params_handle_tparams=NULL;res=umfOsMemoryProviderParamsCreate(&params);if(res!=UMF_RESULT_SUCCESS){printf("Failed to create OS memory provider params!\n");return-1;}

The handle to created memoryprovider object is returned as the last argumentofumfMemoryProviderCreate:

umf_memory_provider_handle_tprovider;umfMemoryProviderCreate(provider_ops,&params,&provider);
Theparams object can be destroyed after the provider is created::

umfOsMemoryProviderParamsDestroy(params);

With theprovider handle we can allocate a chunk of memory, callumfMemoryProviderAlloc:

size_talloc_size=5000;size_talignment=0;void*ptr_provider=NULL;umfMemoryProviderAlloc(provider,alloc_size,alignment,&ptr_provider);

To free the memory allocated with aprovider, you have to pass the allocatedsize as the last parameter ofumfMemoryProviderFree:

umfMemoryProviderFree(provider,ptr_provider,alloc_size);

Memory pool usage#

Having created a memoryprovider, you can create a Scalable Memorypoolto be used for fine-grained allocations. You have to includethepool_scalable.h header with the Scalable Memory Pool API:

#include<umf/pools/pool_scalable.h>

Use the default set of operations for the Scalable memory poolby retrieving an address of the default ops struct:

umf_memory_pool_ops_t*pool_ops=umfScalablePoolOps();

Argumentpool_params is not used by the Scalable Pool, set it toNULL:

void*pool_params=NULL;

Here we don’t make use of additionalflags.See thedocumentation for available flags:

umf_pool_create_flags_tflags=0;

Thepool handle is retrieved as the last argument oftheumfPoolCreate function:

umf_memory_pool_handle_tpool;umfPoolCreate(pool_ops,provider,pool_params,flags,&pool);

Thepool has been created, we can allocate some memory nowwith i.e.umfPoolCalloc:

size_tnum=1;alloc_size=128;char*ptr=umfPoolCalloc(pool,num,alloc_size);

With the memory tracking enabled, we can retrieve the pool handle usedfor allocating memory:

umf_memory_pool_handle_tcheck_pool=umfPoolByPtr(ptr);

For any pool, you can retrieve the memory provider’s handlethat was used to create thepool withumfPoolGetMemoryProvider:

umf_memory_provider_handle_tcheck_provider;umfPoolGetMemoryProvider(pool,&check_provider);

Freeing memory is as easy as can be:

umfFree(ptr);umfPoolDestroy(pool);umfMemoryProviderDestroy(provider);

GPU shared memory#

You can find the full example code in theexamples/level_zero_shared_memory/level_zero_shared_memory.c fileorexamples/cuda_shared_memory/cuda_shared_memory.c file in the UMF repository.

TODO

Memspace#

Note

Memspace examples rely on experimental APIs that may change in future releases.

You can find the full examples code in theexamples/memspace directoryin the UMF repository.

TODO

Custom memory provider#

You can find the full examples code in theexamples/custom_file_provider/custom_file_provider.c filein the UMF repository.

TODO

CTL example#

Note

The CTL API is experimental and may change in future releases.

You can find the full example code in theexamples/ctl/ctl.c file in theUMF repository.

The sample configures an OS memory provider and a disjoint pool, reuses theprovider’s canonicalOS selector obtained at runtime, assigns a custom poolname, and then mixesby_handle andby_name selectors to explore CTLstatistics. Wildcard nodes are used to choose provider counters, build afour-segment{}.{} chain for the named pool, reset the peak tracker, anddrill into per-bucket disjoint pool telemetry. The program prints hints onstderr explaining which tracing level is necessary when a statistic isunavailable.

Build and run the example with:

cmake-Bbuildcmake--buildbuild./build/examples/umf_example_ctl_statistics

Detailed disjoint pool counters are disabled unless tracing is configuredbefore pool creation. Enable them through the environment:

UMF_CONF="umf.pool.default.disjoint.params.pool_trace=2"./build/examples/umf_example_ctl_statistics

Tracing level1 enables slab usage counters, level2 adds allocationand free statistics, and level3 additionally emits verbose log messagesfrom the pool implementation.

Custom CTL example#

You can find the full example code in theexamples/ctl/custom_ctl.c file inthe UMF repository. The program implements a minimal memory provider with CTLhooks that accept configuration values, execute runnables, and expose providerstate through the experimental API. It highlights converting wildcard segmentstoprintf-style format strings and reading integers supplied viaconfiguration defaults.

Build and run the example with:

cmake-Bbuildcmake--buildbuild./build/examples/umf_example_ctl

Optionally supply a modulus via configuration defaults:

UMF_CONF="umf.provider.default.ctl.m=10"./build/examples/umf_example_ctl

IPC example with Level Zero Memory Provider#

The full code of the example is in theexamples/ipc_level_zero/ipc_level_zero.c file in the UMF repository.The example demonstrates how to use UMFIPC API. For demonstration purpose the example usesLevel Zero memory provider to instantiate a pool. But the same flow will work with any memory provider thatsupports IPC capabilities.

Here we omit describing how memory pools are created as its orthogonal to the IPC API usage. For more informationon how to create memory pools refer to the previous examples. Also for simplification, our example is single processwhileIPC API targeted for interprocess communication when IPC handle is created by one processto be used in another process.

To useIPC API theumf/ipc.h header should be included.

#include<umf/ipc.h>

To get IPC handle for the memory allocated by UMF theumfGetIPCHandle function should be used.

umf_ipc_handle_tipc_handle=NULL;size_thandle_size=0;umf_result_tumf_result=umfGetIPCHandle(initial_buf,&ipc_handle,&handle_size);

TheumfGetIPCHandle function requires only the memory pointer as an input parameter and internally determinesthe memory pool to which the memory region belongs. While in our example theumfPoolMalloc function is calleda few lines before theumfGetIPCHandle function is called, in a real application, memory might be allocated evenby a different library and the caller of theumfGetIPCHandle function may not know the corresponding memory pool.

TheumfGetIPCHandle function returns the IPC handle and its size. The IPC handle is a byte-copyable opaquedata structure. Theumf_ipc_handle_t type is defined as a pointer to a byte array. The size of the handlemight be different for different memory provider types. The code snippet below demonstrates how the IPC handle canbe serialized for marshaling purposes.

// Serialize IPC handlevoid*serialized_ipc_handle=malloc(handle_size);memcpy(serialized_ipc_handle,(void*)ipc_handle,handle_size);

Note

The method of sending the IPC handle between processes is not defined by the UMF.

When the IPC handle is transferredto another process it can be opened by theumfOpenIPCHandle function.

umf_ipc_handler_handle_tipc_handler=0;umf_result=umfPoolGetIPCHandler(consumer_pool,&ipc_handler);void*mapped_buf=NULL;umf_result=umfOpenIPCHandle(ipc_handler,ipc_handle,&mapped_buf);

TheumfOpenIPCHandle function requires the IPC handler and the IPC handle as input parameters. The IPC handler mapsthe handle to the current process address space and returns the pointer to the same memory region that was allocatedin the producer process. To retrieve the IPC handler, theumfPoolGetIPCHandler function is used.

Note

The virtual addresses of the memory region referred to by the IPC handle may not be the same in the producer and consumer processes.

To release IPC handle on the producer side theumfPutIPCHandle function should be used.

umf_result=umfPutIPCHandle(ipc_handle);

To close IPC handle on the consumer side theumfCloseIPCHandle function should be used.

umf_result=umfCloseIPCHandle(mapped_buf);

TheumfPutIPCHandle function on the producer side might be called even before theumfCloseIPCHandlefunction is called on the consumer side. The memory mappings on the consumer side remains valid untiltheumfCloseIPCHandle function is called.