DotCompute - Working Reference Example
This document shows the correct API patterns for using DotCompute v0.4.1-rc2.
✅ Correct Pattern: Device Discovery and Enumeration
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using DotCompute.Abstractions.Factories;
using DotCompute.Runtime.Configuration;
using DotCompute.Runtime.Factories;
// 1. Setup Dependency Injection
var services = new ServiceCollection();
// Add logging (optional but recommended)
services.AddLogging(builder =>
{
builder.AddConsole();
builder.SetMinimumLevel(LogLevel.Information);
});
// Configure DotCompute runtime options
services.Configure<DotComputeRuntimeOptions>(options =>
{
options.ValidateCapabilities = false; // Set to true for strict validation
options.AcceleratorLifetime = DotCompute.Runtime.Configuration.ServiceLifetime.Transient;
});
// Register the accelerator factory
services.AddSingleton<IUnifiedAcceleratorFactory, DefaultAcceleratorFactory>();
var serviceProvider = services.BuildServiceProvider();
// 2. Get the factory from DI
var factory = serviceProvider.GetRequiredService<IUnifiedAcceleratorFactory>();
// 3. Enumerate available devices
var devices = await factory.GetAvailableDevicesAsync();
Console.WriteLine($"Found {devices.Count} device(s):");
foreach (var device in devices)
{
Console.WriteLine($" - {device.Name} ({device.DeviceType})");
Console.WriteLine($" Memory: {device.TotalMemory / (1024.0 * 1024 * 1024):F2} GB");
Console.WriteLine($" Compute Units: {device.MaxComputeUnits}");
}
✅ CORRECT PATTERN: Using AddDotComputeRuntime()
FIXED IN LATEST BUILD: The namespace conflict has been resolved. There is now ONE unified AddDotComputeRuntime() method that registers ALL necessary services.
The recommended approach is simple and clean:
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using DotCompute.Abstractions.Factories;
using DotCompute.Abstractions.Interfaces;
using DotCompute.Runtime; // ✅ ONLY namespace needed now!
// Using Host builder
var host = Host.CreateApplicationBuilder(args);
// Add logging (optional but recommended)
host.Services.AddLogging(builder =>
{
builder.AddConsole();
builder.SetMinimumLevel(LogLevel.Information);
});
// ✅ Single unified method registers ALL services!
host.Services.AddDotComputeRuntime();
var app = host.Build();
// Get factory - now properly registered!
var factory = app.Services.GetRequiredService<IUnifiedAcceleratorFactory>();
var devices = await factory.GetAvailableDevicesAsync();
// Get orchestrator - also registered!
var orchestrator = app.Services.GetRequiredService<IComputeOrchestrator>();
✅ Correct Pattern: Creating Specific Accelerators
// After enumerating devices, create an accelerator for a specific device
var devices = await factory.GetAvailableDevicesAsync();
// Find CUDA device
var cudaDevice = devices.FirstOrDefault(d => d.DeviceType == "CUDA");
if (cudaDevice != null)
{
// Create accelerator for this specific device
var accelerator = await factory.CreateAsync(cudaDevice);
// Use the accelerator...
// Dispose when done
accelerator.Dispose();
}
✅ Correct Pattern: Kernel Execution with IComputeOrchestrator
IMPORTANT: IComputeOrchestrator is now automatically registered by AddDotComputeRuntime(). No additional setup needed!
using Microsoft.Extensions.DependencyInjection;
using DotCompute.Abstractions.Interfaces;
using DotCompute.Runtime;
// Get orchestrator - automatically registered!
var orchestrator = app.Services.GetRequiredService<IComputeOrchestrator>();
// Prepare data
var a = new float[] { 1, 2, 3, 4, 5 };
var b = new float[] { 10, 20, 30, 40, 50 };
var result = new float[5];
// Execute kernel with automatic backend selection
await orchestrator.ExecuteKernelAsync(
kernelName: "VectorAdd",
args: new object[] { a, b, result }
);
For direct accelerator usage:
using DotCompute.Abstractions.Factories;
// Get factory from DI
var factory = app.Services.GetRequiredService<IUnifiedAcceleratorFactory>();
// Get devices and create accelerator
var devices = await factory.GetAvailableDevicesAsync();
var device = devices.FirstOrDefault(d => d.DeviceType == "CUDA") ?? devices.First();
using var accelerator = await factory.CreateAsync(device);
// Now use the accelerator for computations
// (See backend-specific documentation for kernel compilation and execution)
❌ INCORRECT Patterns (Do Not Use)
❌ Wrong: Manual Service Registration (Deprecated Pattern)
// DON'T DO THIS - use AddDotComputeRuntime() instead!
host.Services.AddSingleton<IUnifiedAcceleratorFactory, DefaultAcceleratorFactory>(); // ❌ Incomplete
host.Services.Configure<DotComputeRuntimeOptions>(options => { }); // ❌ Manual
// ✅ CORRECT: Use the unified method
host.Services.AddDotComputeRuntime(); // Registers EVERYTHING!
❌ Wrong: Direct Instantiation
// DON'T DO THIS - bypasses DI and configuration
using var accelerator = new CudaAccelerator(); // ❌ WRONG
using var accelerator = new CpuAccelerator(); // ❌ WRONG
❌ Wrong: ComputeOrchestrator.CreateDefault()
// DON'T DO THIS - this method doesn't exist
var orchestrator = ComputeOrchestrator.CreateDefault(); // ❌ WRONG
❌ Wrong: Using UnifiedBuffer Directly Without Factory
// DON'T DO THIS - requires proper accelerator from factory
var buffer = new UnifiedBuffer<float>(data, accelerator); // ❌ May not work correctly
✅ Complete Working Example (RECOMMENDED)
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using DotCompute.Abstractions.Factories;
using DotCompute.Abstractions.Interfaces;
using DotCompute.Runtime;
class Program
{
static async Task Main(string[] args)
{
// 1. Build host with DotCompute services
var host = Host.CreateApplicationBuilder(args);
host.Services.AddLogging(builder =>
{
builder.AddConsole();
builder.SetMinimumLevel(LogLevel.Information);
});
// ✅ Single unified method registers ALL services!
host.Services.AddDotComputeRuntime();
var app = host.Build();
// 2. Get factory and enumerate devices
var factory = app.Services.GetRequiredService<IUnifiedAcceleratorFactory>();
var devices = await factory.GetAvailableDevicesAsync();
Console.WriteLine($"\\n=== Available Devices ===");
Console.WriteLine($"Found {devices.Count} device(s):\\n");
foreach (var device in devices)
{
Console.WriteLine($"📱 {device.Name}");
Console.WriteLine($" Type: {device.DeviceType}");
Console.WriteLine($" Memory: {device.TotalMemory / (1024.0 * 1024 * 1024):F2} GB");
Console.WriteLine($" Compute Units: {device.MaxComputeUnits}");
Console.WriteLine();
}
// 3. Create accelerator for specific device
var cudaDevice = devices.FirstOrDefault(d => d.DeviceType == "CUDA");
if (cudaDevice != null)
{
using var accelerator = await factory.CreateAsync(cudaDevice);
Console.WriteLine($"Using accelerator: {cudaDevice.Name}");
// Now use accelerator for kernel compilation and execution
// (See backend-specific documentation for details)
}
}
}
// Kernel definition
public static class MyKernels
{
[Kernel]
public static void VectorAdd(
ReadOnlySpan<float> a,
ReadOnlySpan<float> b,
Span<float> result)
{
int idx = Kernel.ThreadId.X;
if (idx < result.Length)
{
result[idx] = a[idx] + b[idx];
}
}
}
Package Installation
# Core packages (required)
dotnet add package DotCompute.Core --version 0.4.1-rc2
dotnet add package DotCompute.Abstractions --version 0.4.1-rc2
dotnet add package DotCompute.Runtime --version 0.4.1-rc2
dotnet add package DotCompute.Memory --version 0.4.1-rc2
# Backend packages (install what you need)
dotnet add package DotCompute.Backends.CPU --version 0.4.1-rc2 # Always recommended
dotnet add package DotCompute.Backends.CUDA --version 0.4.1-rc2 # NVIDIA GPUs
dotnet add package DotCompute.Backends.OpenCL --version 0.4.1-rc2 # Cross-platform GPU
dotnet add package DotCompute.Backends.Metal --version 0.4.1-rc2 # Apple Silicon
# Source generators (required for [Kernel] attribute)
dotnet add package DotCompute.Generators --version 0.4.1-rc2
Key Takeaways
- Always use Dependency Injection - Don't instantiate accelerators directly
- Call
AddDotComputeRuntime()- Single method registers ALL services (factory, orchestrator, memory, kernels) - Use
IUnifiedAcceleratorFactory- For device discovery and accelerator creation - Use
IComputeOrchestrator- For kernel execution with automatic backend selection - Dispose properly - Accelerators implement
IDisposable - Check device capabilities - Not all devices support all operations
- No manual registration needed - Everything is configured automatically