Repository navigation
Expand file tree
/
Copy pathNDIterKernels.cs
More file actions
263 lines (226 loc) · 8.92 KB
/
Copy pathNDIterKernels.cs
File metadata and controls
263 lines (226 loc) · 8.92 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
using System;
using System.Runtime.CompilerServices;
using System.Runtime.Intrinsics.X86;
namespace NumSharp.Backends.Iteration
{
/// <summary>
/// Interface for kernels that work with NDIter.
/// </summary>
public unsafe interface INDIterKernel
{
/// <summary>
/// Get the inner loop function for the specified execution path.
/// </summary>
NDIterInnerLoopFunc GetInnerKernel(NDIterExecutionPath path);
/// <summary>
/// Process a single element (for general path).
/// </summary>
void ProcessElement(void** dataptrs);
/// <summary>
/// Whether this kernel supports early exit.
/// </summary>
bool SupportsEarlyExit { get; }
/// <summary>
/// Required alignment for buffers (0 for no requirement).
/// </summary>
int RequiredAlignment { get; }
}
/// <summary>
/// Execution path selection logic.
/// </summary>
public static unsafe class NDIterPathSelector
{
/// <summary>
/// Determine the optimal execution path based on operand layout.
/// </summary>
public static NDIterExecutionPath SelectPath(ref NDIterState state)
{
// Check if all operands are contiguous
if ((state.ItFlags & (uint)NDIterFlags.CONTIGUOUS) != 0)
return NDIterExecutionPath.Contiguous;
bool anyBroadcast = false;
bool canGather = true;
// Access dynamically allocated strides array
var strides = state.Strides;
int stridesNDim = state.StridesNDim;
for (int op = 0; op < state.NOp; op++)
{
// Check inner stride (axis NDim-1)
int innerIdx = op * stridesNDim + (state.NDim - 1);
long innerStride = state.NDim > 0 ? strides[innerIdx] : 1;
if (innerStride == 0)
anyBroadcast = true;
// Gather requires stride fits in int32 and is positive
if (innerStride < 0 || innerStride > int.MaxValue)
canGather = false;
}
// Check for broadcast or non-gatherable strides
if (anyBroadcast || !canGather)
{
// Need buffering for broadcast or large strides
if ((state.ItFlags & (uint)NDIterFlags.BUFFER) != 0)
return NDIterExecutionPath.Buffered;
else
return NDIterExecutionPath.General;
}
// Can use gather for strided access
if (Avx2.IsSupported)
return NDIterExecutionPath.Strided;
return NDIterExecutionPath.General;
}
/// <summary>
/// Check if the given execution path supports SIMD operations.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static bool IsSimdPath(NDIterExecutionPath path)
{
return path == NDIterExecutionPath.Contiguous ||
path == NDIterExecutionPath.Strided ||
path == NDIterExecutionPath.Buffered;
}
/// <summary>
/// Get the recommended inner loop size for the given path.
/// </summary>
public static long GetRecommendedInnerSize(NDIterExecutionPath path, NPTypeCode dtype)
{
return path switch
{
NDIterExecutionPath.Contiguous => long.MaxValue, // Process all at once
NDIterExecutionPath.Strided => 256, // AVX2 gather batch
NDIterExecutionPath.Buffered => NDIterBufferManager.DefaultBufferSize,
NDIterExecutionPath.General => 1, // Element by element
_ => 1
};
}
}
/// <summary>
/// Execution helpers for different paths.
/// </summary>
public static unsafe class NDIterExecution
{
/// <summary>
/// Execute iteration using contiguous path with SIMD kernel.
/// </summary>
public static void ExecuteContiguous<TKernel>(
ref NDIterState state,
TKernel kernel)
where TKernel : INDIterKernel
{
var dataptrs = stackalloc void*[state.NOp];
for (int op = 0; op < state.NOp; op++)
dataptrs[op] = state.GetDataPtr(op);
var strides = stackalloc long[state.NOp];
for (int op = 0; op < state.NOp; op++)
strides[op] = state.NDim > 0 ? state.GetStride(state.NDim - 1, op) : 0;
var innerKernel = kernel.GetInnerKernel(NDIterExecutionPath.Contiguous);
innerKernel(dataptrs, strides, state.IterSize, null);
}
/// <summary>
/// Execute iteration using buffered path.
/// </summary>
public static void ExecuteBuffered<TKernel>(
ref NDIterState state,
TKernel kernel)
where TKernel : INDIterKernel
{
// Ensure buffers are allocated
if (!NDIterBufferManager.AllocateBuffers(ref state, state.BufferSize))
throw new OutOfMemoryException("Failed to allocate iteration buffers");
try
{
var innerKernel = kernel.GetInnerKernel(NDIterExecutionPath.Contiguous);
long remaining = state.IterSize;
var dataptrs = stackalloc void*[state.NOp];
var strides = stackalloc long[state.NOp];
for (int op = 0; op < state.NOp; op++)
strides[op] = 1; // Buffers are contiguous
while (remaining > 0)
{
long batchSize = Math.Min(remaining, state.BufferSize);
// Copy to buffers
for (int op = 0; op < state.NOp; op++)
{
var opFlags = state.GetOpFlags(op);
if ((opFlags & NDIterOpFlags.READ) != 0)
{
// TODO: Type dispatch for copy
// For now, use byte copy as placeholder
}
}
// Get buffer pointers
for (int op = 0; op < state.NOp; op++)
{
var buf = state.GetBuffer(op);
dataptrs[op] = buf != null ? buf : state.GetDataPtr(op);
}
// Execute kernel
innerKernel(dataptrs, strides, batchSize, null);
// Copy from buffers
for (int op = 0; op < state.NOp; op++)
{
var opFlags = state.GetOpFlags(op);
if ((opFlags & NDIterOpFlags.WRITE) != 0)
{
// TODO: Type dispatch for copy
}
}
// Advance state by batch size
state.IterIndex += batchSize;
remaining -= batchSize;
}
}
finally
{
NDIterBufferManager.FreeBuffers(ref state);
}
}
/// <summary>
/// Execute iteration using general coordinate-based path.
/// </summary>
public static void ExecuteGeneral<TKernel>(
ref NDIterState state,
TKernel kernel)
where TKernel : INDIterKernel
{
var dataptrs = stackalloc void*[state.NOp];
for (long i = 0; i < state.IterSize; i++)
{
// Get current data pointers
for (int op = 0; op < state.NOp; op++)
dataptrs[op] = state.GetDataPtr(op);
// Process single element
kernel.ProcessElement(dataptrs);
// Check early exit
if (kernel.SupportsEarlyExit)
{
// TODO: Check early exit condition
}
// Advance to next position
state.Advance();
}
}
/// <summary>
/// Execute iteration with automatic path selection.
/// </summary>
public static void Execute<TKernel>(
ref NDIterState state,
TKernel kernel)
where TKernel : INDIterKernel
{
var path = NDIterPathSelector.SelectPath(ref state);
switch (path)
{
case NDIterExecutionPath.Contiguous:
ExecuteContiguous(ref state, kernel);
break;
case NDIterExecutionPath.Buffered:
ExecuteBuffered(ref state, kernel);
break;
case NDIterExecutionPath.General:
default:
ExecuteGeneral(ref state, kernel);
break;
}
}
}
}