Latent int16 overflow in ggml_vec_dot_i2_i8_s_1x1: 128 maddubs results per lane against a 32767 ceiling
Nadie ha tomado este issue todavía.
Evaluación
- Dificultad
- 4/5
- Tiempo estimado
- 3-5 días
- Aptitud para principiantes
- 56/100
- Tipo de issue
- Error
- Claridad
- Bien especificado
- Estado de actividad
- Activo
- Área
- backend, performance
Línea de trabajo
Empieza en ggml/src/ggml-cpu/quants.c, en ggml_vec_dot_i2_i8_s_1x1, y sigue la acumulación int16 a través del bucle de 32 iteraciones y el fold final. Reproduce el problema con los pesos de BitNet-b1.58-2B-4T y activaciones int8 de +127; después compara la salida del fallback con aritmética int64 exacta. Se considera terminado cuando el fallback ya no desborda y conserva los resultados correctos para activaciones ordinarias.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
Summary
ggml_vec_dot_i2_i8_s_1x1 accumulates 128 vpmaddubsw results into one
int16 lane before folding. The worst case is 128 * 508 = 65024 against a
32767 ceiling. It is correct in practice only because signed activations
cancel, and it is latent on a default build because the sgemm path takes
the work instead.
I am filing it as hardening, not as a wrong-output bug, and the distinction is
deliberate — see Severity below.
The arithmetic
ggml/src/ggml-cpu/quants.c:
const int group32_num = nb / 32; // :1396
...
__m256i accu32 = _mm256_setzero_si256(); // :1413 (int16 lanes)
for (int j = 0; j < 32; j++) { // :1414
...
accu32 = _mm256_add_epi16(accu32, _mm256_add_epi16(xq8_0, xq8_1)); // :1439
accu32 = _mm256_add_epi16(accu32, _mm256_add_epi16(xq8_2, xq8_3)); // :1440
}
accu = _mm256_add_epi32(_mm256_madd_epi16(accu32, one16), accu); // :1445
32 iterations × 4 planes = 128 vpmaddubsw results per int16 lane. Each is at
most 2 * 2 * 127 = 508, so the lane can reach 65024, roughly twice the
ceiling. The name accu32 suggests 32-bit; the type is __m256i used with
_mm256_add_epi16.
What actually happens
With all activations the same sign the sum does not cancel and the lane wraps.
Measured on the real weights of BitNet-b1.58-2B-4T — 30 tensors at
K = 6912, driven against int8 activations pinned to +127 — 11,998 of
12,000 rows disagree with exact int64 arithmetic, and every error is a
multiple of 65536, the signature of a wrapped int16.
With ordinary int8 activations, 0 of 12,000 rows wrap. That is why it has
never been seen.
Severity: low, and I want to be accurate about why
- On a build where
llamafile_sgemmhandles these shapes this function is not
called at all. The kernel's own counters on my build read
calls=0 sgemm=52068. - It becomes reachable when that path is disabled, which is a supported switch
rather than an exotic one. - Even then it needs same-signed activations across a whole group of 32 blocks.
So: a latent overflow in a fallback, not a correctness bug users are hitting.
I would not have filed it on its own; I am filing it because it is the same
missing bound-derivation as the fold issue I opened alongside this one (#628), in the
same file family, and a fix for one is naturally a fix for both.
Not a duplicate of the ARM reports
#411 and the audit in #525 report an int16 overflow, but it is the NEON
vmlal_s8 accumulation in src/ggml-bitnet-mad.cpp, which does produce visible
garbage. This is a different function, a different instruction set and a
different failure mode — this one is invisible precisely because it cancels.
Suggested fix
Fold every 8 groups rather than every 32, or accumulate in int32 directly.
Upstream ggml's equivalent kernels derive the bound explicitly and stay inside
it — TQ2_0 in arch/x86/quants.c carries // 16-bit sums, because 256*127 still fits — which is the pattern worth copying here.
Reproducible from https://github.com/purpleskulll/bitnet-t5b.
- Lenguaje dominante
- C++
- Estrellas
- 40.3k
- Forks
- 3.7k
- Métricas de merge de PR
- Sin PR fusionados en 30 d
Preparar el entorno
Este proyecto no incluye contenedor de desarrollo, Dockerfile ni guía de contribución, así que la configuración corre por tu cuenta: empieza por su README y consulta nuestra guía para la primera contribución para los pasos generales.
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de microsoft/BitNet
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 86/100
-
i2_s SIGSEGVs at n_ubatch >= 32: BLAS backend dequantises by a row stride 4x the real packed rowAbierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 76/100
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 86/100
-
Dificultad 1/5 Menos de una hora Aptitud para principiantes 92/100
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 76/100
Todos los issues de microsoft/BitNet
Issues similares
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
hyprwm/aquamarine#426 ·
Los mantenedores suelen responder en 1 día
-
Winget hash mismatch for 5.0.3.0Abierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 62/100
amnezia-vpn/amnezia-client#3222 ·
Los mantenedores suelen responder en 2 días
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 82/100
sdatkinson/NeuralAmpModelerCore#342 ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 86/100
valkey-io/valkey-search#1465 ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
KhronosGroup/Vulkan-Tutorial#524 ·
Los mantenedores suelen responder en 1 día