Skip to content

Commit fc98c90

Browse files
authored
Merge pull request #121 from leehack/fix/webgpu-batch-cascade
fix: cascade WebGPU batch defaults
2 parents 46fbc4b + 4a510ee commit fc98c90

7 files changed

Lines changed: 236 additions & 129 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@
4040
* Forwarded native-compatible `ModelParams` load tuning knobs through the
4141
WebGPU bridge path, including `maxParallelSequences`, flash attention,
4242
KV-cache type, KV-unified, RoPE, split-mode, and main-GPU options.
43+
* Matched native batch defaults on the WebGPU path so unset `batchSize` /
44+
`microBatchSize` cascade to `n_batch = n_ctx` and `n_ubatch = n_batch`,
45+
avoiding first-embedding aborts for BERT-class/non-causal encoder models
46+
while preserving explicit caller values and Qwen3.5 web tuning.
4347
* **GPU device selection API**:
4448
* Added `ModelParams.mainGpu` and wired it to llama.cpp
4549
`llama_model_params.main_gpu`.

lib/src/backends/llama_cpp/llama_cpp_service.dart

Lines changed: 3 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -325,36 +325,13 @@ class LlamaCppService {
325325

326326
/// Resolves effective context batch parameters.
327327
///
328-
/// Legacy behavior is preserved when [ModelParams.batchSize] and
329-
/// [ModelParams.microBatchSize] are not set:
330-
///
331-
/// - `n_batch = n_ctx`
332-
/// - `n_ubatch = n_batch`
333-
///
334-
/// Values are clamped to safe bounds so `n_ubatch <= n_batch <= n_ctx`.
328+
/// Uses the shared non-FFI helper so native and WebGPU batch semantics stay
329+
/// in sync.
335330
static ({int batchSize, int microBatchSize}) resolveContextBatchSizes(
336331
ModelParams modelParams,
337332
int contextSize,
338333
) {
339-
final effectiveContextSize = contextSize > 0 ? contextSize : 1;
340-
341-
final configuredBatchSize = modelParams.batchSize > 0
342-
? modelParams.batchSize
343-
: effectiveContextSize;
344-
final cappedBatchSize = configuredBatchSize > effectiveContextSize
345-
? effectiveContextSize
346-
: configuredBatchSize;
347-
final batchSize = cappedBatchSize > 0 ? cappedBatchSize : 1;
348-
349-
final configuredMicroBatchSize = modelParams.microBatchSize > 0
350-
? modelParams.microBatchSize
351-
: batchSize;
352-
final cappedMicroBatchSize = configuredMicroBatchSize > batchSize
353-
? batchSize
354-
: configuredMicroBatchSize;
355-
final microBatchSize = cappedMicroBatchSize > 0 ? cappedMicroBatchSize : 1;
356-
357-
return (batchSize: batchSize, microBatchSize: microBatchSize);
334+
return resolveModelContextBatchSizes(modelParams, contextSize);
358335
}
359336

360337
/// Resolves whether multimodal projector init should use GPU.

lib/src/backends/webgpu/webgpu_backend.dart

Lines changed: 20 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -591,29 +591,28 @@ class WebGpuLlamaBackend
591591
return attempts;
592592
}
593593

594-
({int? nBatch, int? nUbatch}) _resolveWebBatchTuning({
594+
({int nBatch, int nUbatch}) _resolveWebBatchSizes({
595595
required String url,
596596
required ModelParams params,
597+
required int contextSize,
597598
}) {
598-
if (params.batchSize > 0 || params.microBatchSize > 0) {
599-
return (nBatch: null, nUbatch: null);
600-
}
601-
602-
if (params.preferredBackend == GpuBackend.cpu || params.gpuLayers == 0) {
603-
return (nBatch: null, nUbatch: null);
604-
}
605-
606599
final normalizedUrl = url.toLowerCase();
607600
final isQwen35Small =
608601
normalizedUrl.contains('qwen3.5-0.8b') ||
609602
normalizedUrl.contains('qwen_qwen3.5-0.8b');
610-
if (!isQwen35Small) {
611-
return (nBatch: null, nUbatch: null);
603+
final shouldUseQwen35SmallTuning =
604+
params.batchSize <= 0 &&
605+
params.microBatchSize <= 0 &&
606+
params.preferredBackend != GpuBackend.cpu &&
607+
params.gpuLayers != 0 &&
608+
isQwen35Small;
609+
610+
if (shouldUseQwen35SmallTuning) {
611+
return (nBatch: 32, nUbatch: 8);
612612
}
613613

614-
final tunedBatch = 32;
615-
final tunedUbatch = 8;
616-
return (nBatch: tunedBatch, nUbatch: tunedUbatch);
614+
final resolved = resolveModelContextBatchSizes(params, contextSize);
615+
return (nBatch: resolved.batchSize, nUbatch: resolved.microBatchSize);
617616
}
618617

619618
int _webGpuFlashAttentionValue(ModelParams params) {
@@ -894,8 +893,6 @@ class WebGpuLlamaBackend
894893
requestedContextSize: params.contextSize,
895894
requestedGpuLayers: requestedGpuLayers,
896895
);
897-
final batchTuning = _resolveWebBatchTuning(url: url, params: params);
898-
899896
Object? lastError;
900897
Map<String, String> lastRuntimeHints = const <String, String>{};
901898
var retriedWithWasm32 = false;
@@ -909,6 +906,11 @@ class WebGpuLlamaBackend
909906
for (var index = 0; index < loadAttempts.length; index += 1) {
910907
final attempt = loadAttempts[index];
911908
_lastNCtx = attempt.contextSize;
909+
final batchSizes = _resolveWebBatchSizes(
910+
url: url,
911+
params: params,
912+
contextSize: attempt.contextSize,
913+
);
912914
LlamaWebGpuBridge? bridgeForAttempt;
913915
bool? forceRemoteFetchBackend;
914916
final attemptThreads = switch (index) {
@@ -940,12 +942,8 @@ class WebGpuLlamaBackend
940942
nThreadsBatch: params.numberOfThreadsBatch > 0
941943
? params.numberOfThreadsBatch
942944
: null,
943-
nBatch: params.batchSize > 0
944-
? params.batchSize
945-
: batchTuning.nBatch,
946-
nUbatch: params.microBatchSize > 0
947-
? params.microBatchSize
948-
: batchTuning.nUbatch,
945+
nBatch: batchSizes.nBatch,
946+
nUbatch: batchSizes.nUbatch,
949947
nGpuLayers: attempt.gpuLayers,
950948
nSeqMax: math.max(1, params.maxParallelSequences),
951949
flashAttention: _webGpuFlashAttentionValue(params),

lib/src/core/models/inference/model_params.dart

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,3 +248,37 @@ class ModelParams {
248248
);
249249
}
250250
}
251+
252+
/// Resolves llama.cpp-compatible context batch parameters.
253+
///
254+
/// Preserves native defaults when [ModelParams.batchSize] and
255+
/// [ModelParams.microBatchSize] are unset:
256+
///
257+
/// - `n_batch = n_ctx`
258+
/// - `n_ubatch = n_batch`
259+
///
260+
/// Values are clamped to safe bounds so `n_ubatch <= n_batch <= n_ctx`.
261+
({int batchSize, int microBatchSize}) resolveModelContextBatchSizes(
262+
ModelParams modelParams,
263+
int contextSize,
264+
) {
265+
final effectiveContextSize = contextSize > 0 ? contextSize : 1;
266+
267+
final configuredBatchSize = modelParams.batchSize > 0
268+
? modelParams.batchSize
269+
: effectiveContextSize;
270+
final cappedBatchSize = configuredBatchSize > effectiveContextSize
271+
? effectiveContextSize
272+
: configuredBatchSize;
273+
final batchSize = cappedBatchSize > 0 ? cappedBatchSize : 1;
274+
275+
final configuredMicroBatchSize = modelParams.microBatchSize > 0
276+
? modelParams.microBatchSize
277+
: batchSize;
278+
final cappedMicroBatchSize = configuredMicroBatchSize > batchSize
279+
? batchSize
280+
: configuredMicroBatchSize;
281+
final microBatchSize = cappedMicroBatchSize > 0 ? cappedMicroBatchSize : 1;
282+
283+
return (batchSize: batchSize, microBatchSize: microBatchSize);
284+
}

0 commit comments

Comments
 (0)