Programming Objects

A PXL host application is built from four objects: Context, Job, Module, and Function. They form a creation chain — each object is produced from the one above it.

pxl::createContext()  →  Context
        └─ createJob()           →  Job
                  └─ load(module) / buildMap(...)

pxl::createModule(path)  →  Module
        └─ createFunction(name)  →  Function

Map is created from a Job and is covered in Kernel Execution. Stream is covered in Streams.


Context

A Context is an execution session bound to a device. It owns device memory and is the factory for Jobs.

Responsibilities:

  • Create and destroy Jobs.
  • Allocate, free, and transfer device memory.
  • Query memory information of a pointer.
auto ctx = pxl::createContext();              // first available device
auto ctx0 = pxl::createContext(deviceId);     // specific device

// Device memory
auto ptr = ctx->memAlloc(size);
ctx->copyToDevice(ptr, hostBuf, size);
ctx->copyFromDevice(hostBuf, ptr, size);
ctx->memFree(ptr);

// Job creation
auto job = ctx->createJob(numSub);

pxl::destroyContext(ctx);

Reuse a single Context for the lifetime of your application. Allocating device memory once at startup and reusing it across kernel launches avoids re-mapping device addresses on every launch, which would otherwise add latency to each call.


Job

A Job is a reservation of one or more Sub units. It loads a kernel binary and creates Map objects that run on those Subs.

Responsibilities:

  • Hold a set of Subs (allocate more or release some at runtime).
  • Load a Module so the kernel binary lives on the Job’s Subs.
  • Build Map objects that target functions in the loaded module.
  • Own a private default Stream used by every Map that does not bind its own.
auto job = ctx->createJob(4);          // 4 Subs
job->load("mu_kernel.mubin");          // load by path
// or: job->load(module);              // load a pre-created Module

job->subAlloc(2);                      // request 2 more Subs
auto subIds = job->subIdList();        // inspect what was assigned

auto map = job->buildMap("my_kernel", taskCount);

ctx->destroyJob(job);

Destroying a Job synchronously drains every Map built from it, so it is safe to call even when asynchronous executions are still in flight.


Module and Function

A Module is a compiled MU kernel binary. A Function is a single host-callable entry point inside that module.

Module and Function

auto module = pxl::createModule("mu_kernel.mubin");

auto sortFunc   = module->createFunction("bubbleSort");
auto searchFunc = module->createFunction("binarySearch");

// Inspect the module
auto names = module->getMuFunctionList();
auto count = module->getNumMuFunctions();

pxl::destroyModule(module);

A Module can be loaded into a Job with job->load(module). The Job’s Subs then hold the binary, so buildMap(funcName, ...) can resolve function names against it.


Lifecycle Summary

Object Created by Destroyed by Owns
Context pxl::createContext() pxl::destroyContext() device memory, Jobs
Job Context::createJob() Context::destroyJob() Subs, default Stream, Maps
Module pxl::createModule() pxl::destroyModule() binary image, Functions
Function Module::createFunction() Module::destroyFunction() (handle only)
Map Job::buildMap() Job::destroyMap() argument bindings, stream binding

Destroying a parent object releases its children, so you typically only need to destroy the top-level objects (Context, Module). Explicit child destruction is supported for cases where you want to free resources earlier.


→ Related: Kernel Execution, Streams