Implementation:Dotnet Machinelearning MklFftProxy
| Knowledge Sources | |
|---|---|
| Domains | Signal Processing, Native Interop, Time Series Analysis |
| Last Updated | 2026-02-09 12:00 GMT |
Overview
Proxy wrapper library that adapts Intel MKL's variadic DFTI (Discrete Fourier Transform Interface) API into fixed-argument C functions callable from C# via P/Invoke, enabling FFT-based time series analysis in ML.NET.
Description
MklProxyNative.cpp solves a specific interop challenge: Intel MKL's DFTI functions use C variadic arguments (...) in several key functions, particularly DftiSetValue and DftiCreateDescriptor. The .NET P/Invoke marshaller cannot reliably call variadic native functions because the calling convention and argument promotion rules differ from fixed-argument functions on many platforms.
This proxy library wraps each variadic MKL function with a fixed-argument equivalent:
- MKLDftiSetValue wraps DftiSetValue -- accepts the configuration parameter enum and value as explicit typed arguments instead of variadic arguments
- MKLDftiCreateDescriptor wraps DftiCreateDescriptor -- accepts precision, domain, dimensionality, and sizes as explicit arguments
- MKLDftiComputeForward wraps DftiComputeForward -- accepts separate real and imaginary input/output buffers for split-complex format
- MKLDftiComputeBackward wraps DftiComputeBackward -- accepts separate real and imaginary input/output buffers for the inverse transform
The library also defines C-compatible enumerations (ConfigParam and ConfigValue) that mirror MKL's DFTI enums, providing a stable ABI that does not depend on MKL header versioning.
Usage
This proxy is used internally by ML.NET's time series analysis components, specifically:
- Singular Spectrum Analysis (SSA) forecasting, which uses FFT to efficiently compute the Hankel matrix eigendecomposition
- Fourier-based seasonality detection, which identifies dominant frequency components in time series data
- Any ML.NET transform or trainer that requires frequency-domain signal processing
Code Reference
Source Location
- Repository: Dotnet_Machinelearning
- File: src/Native/MklProxyNative/MklProxyNative.cpp
- Lines: 1-177
Signature
// Configuration parameter enumeration
enum ConfigParam {
ForwardDomain = 0,
Dimension = 1,
Precision = 2,
ForwardScale = 4,
BackwardScale = 5,
NumberOfTransforms = 7,
NumberOfUserThreads = 8,
InputDistance = 12,
OutputDistance = 13,
Placement = 11,
ComplexStorage = 14,
RealStorage = 15,
ConjugateEvenStorage = 16,
PackedFormat = 21,
Workspace = 26,
CommitStatus = 22
};
// Configuration value enumeration
enum ConfigValue {
Committed = 0,
Uncommitted = 1,
Complex = 32,
Real = 33,
Single = 35,
Double = 36,
ComplexComplex = 39,
RealReal = 42,
Inplace = 43,
NotInplace = 44,
Allow = 51,
Avoid = 52,
None = 53
};
// Exported proxy functions
EXPORT_API(int) MKLDftiSetValue(
DFTI_DESCRIPTOR_HANDLE handle,
int config_param,
int config_val
);
EXPORT_API(int) MKLDftiCreateDescriptor(
DFTI_DESCRIPTOR_HANDLE* handle,
int precision,
int domain,
int dim,
int* sizes
);
EXPORT_API(int) MKLDftiComputeForward(
DFTI_DESCRIPTOR_HANDLE handle,
float* inputRe,
float* inputIm,
float* outputRe,
float* outputIm
);
EXPORT_API(int) MKLDftiComputeBackward(
DFTI_DESCRIPTOR_HANDLE handle,
float* inputRe,
float* inputIm,
float* outputRe,
float* outputIm
);
Import
// P/Invoke declarations (managed side)
[DllImport("MklProxyNative")]
internal static extern int MKLDftiCreateDescriptor(
out IntPtr handle, int precision, int domain,
int dim, int[] sizes);
[DllImport("MklProxyNative")]
internal static extern int MKLDftiSetValue(
IntPtr handle, int configParam, int configVal);
[DllImport("MklProxyNative")]
internal static extern int MKLDftiComputeForward(
IntPtr handle,
float[] inputRe, float[] inputIm,
float[] outputRe, float[] outputIm);
[DllImport("MklProxyNative")]
internal static extern int MKLDftiComputeBackward(
IntPtr handle,
float[] inputRe, float[] inputIm,
float[] outputRe, float[] outputIm);
// MKL also requires explicit commit and free calls
[DllImport("MklProxyNative")]
internal static extern int DftiCommitDescriptor(IntPtr handle);
[DllImport("MklProxyNative")]
internal static extern int DftiFreeDescriptor(ref IntPtr handle);
I/O Contract
Inputs
| Name | Type | Required | Description |
|---|---|---|---|
| handle | DFTI_DESCRIPTOR_HANDLE | Yes | Opaque handle to an MKL FFT descriptor (created by MKLDftiCreateDescriptor) |
| config_param | int (ConfigParam enum) | Yes (SetValue) | Which configuration property to set (e.g., Placement, ForwardScale) |
| config_val | int (ConfigValue enum) | Yes (SetValue) | The value to assign to the configuration property |
| precision | int | Yes (Create) | Floating-point precision: Single (35) or Double (36) |
| domain | int | Yes (Create) | Transform domain: Complex (32) or Real (33) |
| dim | int | Yes (Create) | Dimensionality of the transform (1 for 1-D FFT) |
| sizes | int* | Yes (Create) | Array of dimension sizes; for 1-D, a single element specifying transform length |
| inputRe | float* | Yes (Compute) | Real part of the input signal |
| inputIm | float* | Yes (Compute) | Imaginary part of the input signal (zeros for real-valued input) |
| outputRe | float* | Yes (Compute) | Buffer for the real part of the output |
| outputIm | float* | Yes (Compute) | Buffer for the imaginary part of the output |
Outputs
| Name | Type | Description |
|---|---|---|
| return (all functions) | int | MKL status code: 0 indicates success; non-zero indicates an error (see MKL DFTI error codes) |
| handle (Create) | DFTI_DESCRIPTOR_HANDLE* | Output parameter: pointer to the created descriptor handle |
| outputRe (Compute) | float* | Real part of the transformed signal |
| outputIm (Compute) | float* | Imaginary part of the transformed signal |
FFT Workflow
The typical usage sequence for performing an FFT through this proxy is:
- Create a descriptor with MKLDftiCreateDescriptor (specify precision, domain, and transform size)
- Configure the descriptor with one or more calls to MKLDftiSetValue (e.g., set placement to not-in-place, set storage format)
- Commit the descriptor with DftiCommitDescriptor (finalizes internal plans and allocations)
- Compute the forward or backward transform with MKLDftiComputeForward or MKLDftiComputeBackward
- Free the descriptor with DftiFreeDescriptor when no longer needed
Usage Examples
// Performing a 1-D forward FFT using the MKL proxy
IntPtr handle;
int[] sizes = new int[] { signalLength };
// Step 1: Create descriptor for single-precision, real domain, 1-D
MKLDftiCreateDescriptor(out handle, 35 /* Single */, 33 /* Real */, 1, sizes);
// Step 2: Configure for not-in-place computation
MKLDftiSetValue(handle, 11 /* Placement */, 44 /* NotInplace */);
// Step 3: Commit the descriptor
DftiCommitDescriptor(handle);
// Step 4: Compute forward FFT
float[] inputRe = signal;
float[] inputIm = new float[signalLength]; // zeros
float[] outputRe = new float[signalLength];
float[] outputIm = new float[signalLength];
MKLDftiComputeForward(handle, inputRe, inputIm, outputRe, outputIm);
// outputRe and outputIm now contain the frequency-domain representation
// Step 5: Free descriptor
DftiFreeDescriptor(ref handle);
// Used internally by ML.NET SSA forecasting
var pipeline = mlContext.Forecasting.ForecastBySsa(
outputColumnName: "Forecast",
inputColumnName: "Value",
windowSize: 12,
seriesLength: 36,
trainSize: 120,
horizon: 6);
// The SSA trainer internally uses MKL FFT via this proxy
// to compute eigenvalues of the trajectory matrix
var model = pipeline.Fit(trainingData);