HelloSun commited on
Commit
2678876
·
verified ·
1 Parent(s): 302a939

Upload folder using huggingface_hub

Browse files
README.md ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # flint CPU optimization
2
+
3
+ CPU-optimized build of [unsloth/flint-GGUF](https://huggingface.co/unsloth/flint-GGUF) (flint-UD-Q4_K_XL.gguf) using a llama.cpp CPU patch in the style of [HelloSun/Qwen3.6-35B-A3B-GGUF_CPU_OPT](https://huggingface.co/HelloSun/Qwen3.6-35B-A3B-GGUF_CPU_OPT).
4
+
5
+ ## Apply
6
+ ```bash
7
+ git clone https://github.com/ggerganov/llama.cpp
8
+ cd llama.cpp
9
+ git checkout $(cat patches/BASE_COMMIT)
10
+ git apply patches/flint-cpu-opt.patch
11
+ ./build.sh
12
+ ```
13
+
14
+ ## Results
15
+ See benchmarks/summary.md. Mean KLD vs stock: 0.2058.
benchmarks/kld_final.log ADDED
@@ -0,0 +1,80 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ 0.01.766.660 I cmn init: llama threadpool init, n_threads = 16
2
+ 0.01.845.751 I
3
+ 0.01.845.855 I system_info: n_threads = 16 (n_threads_batch = 16) / 192 | CPU : SSE3 = 1 | SSSE3 = 1 | AVX = 1 | AVX_VNNI = 1 | AVX2 = 1 | F16C = 1 | FMA = 1 | BMI2 = 1 | AVX512 = 1 | AVX512_VBMI = 1 | AVX512_VNNI = 1 | AVX512_BF16 = 1 | AMX_INT8 = 1 | LLAMAFILE = 1 | OPENMP = 1 | REPACK = 1 |
4
+ 0.01.886.689 I kl_divergence: computing over 32 chunks, n_ctx=512, batch_size=512, n_seq=1
5
+ 0.04.588.983 I kl_divergence: 2.70 seconds per pass - ETA 1.43 minutes
6
+
7
+ chunk PPL ln(PPL(Q)/PPL(base)) KL Divergence Δp RMS Same top p
8
+ 1 31.8950 ± 10.7913 -0.01919 ± 0.12414 0.47995 ± 0.07714 16.215 ± 2.303 % 78.824 ± 2.564 %
9
+ 2 18.7693 ± 3.8844 0.00294 ± 0.06270 0.25980 ± 0.04020 12.069 ± 1.576 % 85.294 ± 1.570 %
10
+ 3 15.4713 ± 2.4439 0.01637 ± 0.04302 0.21289 ± 0.03283 11.207 ± 1.271 % 87.712 ± 1.188 %
11
+ 4 14.1940 ± 1.9658 0.03541 ± 0.03515 0.22116 ± 0.02998 10.684 ± 1.035 % 88.431 ± 1.002 %
12
+ 5 10.8930 ± 1.3155 0.01565 ± 0.03140 0.23288 ± 0.02782 11.670 ± 0.962 % 89.020 ± 0.876 %
13
+ 6 7.3329 ± 0.7564 0.01319 ± 0.02617 0.19435 ± 0.02328 10.668 ± 0.879 % 90.850 ± 0.737 %
14
+ 7 5.5187 ± 0.4963 0.01079 ± 0.02243 0.16887 ± 0.02007 9.970 ± 0.809 % 92.157 ± 0.637 %
15
+ 8 4.4589 ± 0.3553 0.00925 ± 0.01963 0.14834 ± 0.01760 9.372 ± 0.755 % 93.088 ± 0.562 %
16
+ 9 4.0899 ± 0.2965 0.00299 ± 0.01793 0.14117 ± 0.01709 9.272 ± 0.730 % 93.595 ± 0.511 %
17
+ 10 4.9979 ± 0.3706 0.01408 ± 0.02749 0.25037 ± 0.02457 10.943 ± 0.721 % 91.647 ± 0.548 %
18
+ 11 4.5721 ± 0.3136 0.01320 ± 0.02500 0.22823 ± 0.02238 10.458 ± 0.686 % 92.228 ± 0.506 %
19
+ 12 4.1937 ± 0.2670 0.01381 ± 0.02296 0.21160 ± 0.02057 10.275 ± 0.655 % 92.647 ± 0.472 %
20
+ 13 3.9080 ± 0.2322 0.01163 ± 0.02146 0.20177 ± 0.01926 10.305 ± 0.634 % 93.032 ± 0.442 %
21
+ 14 3.7166 ± 0.2082 0.01202 ± 0.01996 0.18824 ± 0.01791 10.031 ± 0.610 % 93.389 ± 0.416 %
22
+ 15 3.5969 ± 0.1918 0.01192 ± 0.01865 0.17664 ± 0.01673 9.818 ± 0.585 % 93.725 ± 0.392 %
23
+ 16 3.6195 ± 0.1845 0.00955 ± 0.01752 0.16778 ± 0.01571 9.655 ± 0.562 % 93.775 ± 0.378 %
24
+ 17 3.7784 ± 0.1872 0.00322 ± 0.01773 0.19185 ± 0.01589 10.467 ± 0.555 % 93.218 ± 0.382 %
25
+ 18 3.9020 ± 0.1906 0.01415 ± 0.01814 0.22527 ± 0.01680 11.350 ± 0.545 % 92.702 ± 0.384 %
26
+ 19 3.9563 ± 0.1872 0.01320 ± 0.01723 0.21543 ± 0.01595 11.213 ± 0.531 % 92.859 ± 0.370 %
27
+ 20 4.0206 ± 0.1852 0.00258 ± 0.01733 0.22077 ± 0.01576 11.437 ± 0.520 % 92.647 ± 0.366 %
28
+ 21 4.2279 ± 0.1948 0.01021 ± 0.01782 0.25106 ± 0.01716 12.200 ± 0.521 % 92.176 ± 0.367 %
29
+ 22 4.2137 ± 0.1895 0.01000 ± 0.01712 0.24988 ± 0.01667 12.241 ± 0.508 % 92.103 ± 0.360 %
30
+ 23 4.1466 ± 0.1817 0.01604 ± 0.01705 0.24787 ± 0.01625 12.207 ± 0.495 % 92.242 ± 0.349 %
31
+ 24 4.1800 ± 0.1779 0.01373 ± 0.01640 0.23897 ± 0.01559 11.969 ± 0.484 % 92.435 ± 0.338 %
32
+ 25 4.1542 ± 0.1722 0.01618 ± 0.01584 0.23559 ± 0.01505 12.003 ± 0.474 % 92.502 ± 0.330 %
33
+ 26 4.0994 ± 0.1649 0.01558 ± 0.01527 0.22948 ± 0.01454 11.892 ± 0.466 % 92.609 ± 0.321 %
34
+ 27 4.1120 ± 0.1618 0.01565 ± 0.01479 0.22470 ± 0.01405 11.881 ± 0.458 % 92.694 ± 0.314 %
35
+ 28 4.2240 ± 0.1640 0.01531 ± 0.01436 0.22426 ± 0.01375 11.893 ± 0.449 % 92.591 ± 0.310 %
36
+ 29 4.2403 ± 0.1614 0.01391 ± 0.01395 0.22072 ± 0.01336 11.775 ± 0.440 % 92.630 ± 0.304 %
37
+ 30 4.2686 ± 0.1591 0.01269 ± 0.01350 0.21384 ± 0.01292 11.600 ± 0.432 % 92.745 ± 0.297 %
38
+ 31 4.2673 ± 0.1558 0.01106 ± 0.01308 0.20801 ± 0.01252 11.505 ± 0.425 % 92.865 ± 0.290 %
39
+ 32 4.1299 ± 0.1470 0.01347 ± 0.01282 0.20576 ± 0.01230 11.498 ± 0.420 % 92.990 ± 0.283 %
40
+
41
+ ====== Perplexity statistics ======
42
+ Mean PPL(Q) : 4.129912 ± 0.147024
43
+ Mean PPL(base) : 4.074668 ± 0.139399
44
+ Cor(ln(PPL(Q)), ln(PPL(base))): 93.34%
45
+ Mean ln(PPL(Q)/PPL(base)) : 0.013467 ± 0.012816
46
+ Mean PPL(Q)/PPL(base) : 1.013558 ± 0.012990
47
+ Mean PPL(Q)-PPL(base) : 0.055243 ± 0.052818
48
+
49
+ ====== KL divergence statistics ======
50
+ Mean KLD: 0.205759 ± 0.012297
51
+ Maximum KLD: 20.963177
52
+ 99.9% KLD: 14.032636
53
+ 99.0% KLD: 5.719375
54
+ 95.0% KLD: 0.771354
55
+ 90.0% KLD: 0.092588
56
+ Median KLD: 0.000773
57
+ 10.0% KLD: 0.000002
58
+ 5.0% KLD: 0.000000
59
+ 1.0% KLD: -0.000000
60
+ 0.1% KLD: -0.000001
61
+ Minimum KLD: -0.000013
62
+
63
+ ====== Token probability statistics ======
64
+ Mean Δp: -0.023 ± 0.127 %
65
+ Maximum Δp: 99.114%
66
+ 99.9% Δp: 96.666%
67
+ 99.0% Δp: 42.103%
68
+ 95.0% Δp: 4.663%
69
+ 90.0% Δp: 1.578%
70
+ 75.0% Δp: 0.070%
71
+ Median Δp: -0.000%
72
+ 25.0% Δp: -0.066%
73
+ 10.0% Δp: -1.639%
74
+ 5.0% Δp: -5.594%
75
+ 1.0% Δp: -38.947%
76
+ 0.1% Δp: -98.266%
77
+ Minimum Δp: -99.912%
78
+ RMS Δp : 11.498 ± 0.420 %
79
+ Same top p: 92.990 ± 0.283 %
80
+
benchmarks/summary.md ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # flint CPU optimization benchmarks
2
+
3
+ Model: unsloth/flint-GGUF flint-UD-Q4_K_XL.gguf (25.05 GiB)
4
+ Hardware: Intel Xeon Platinum 8559C, 16 threads, taskset -c 0-15, -fa 1
5
+
6
+ | metric | stock (t/s) | optimized (t/s) | speedup |
7
+ |---|---|---|---|
8
+ | pp256 | 221.5 | 260.9 | 1.15x |
9
+ | tg128 | 32.6 | 35.4 | 1.07x |
10
+ | pp2048 | 215.4 | 263.4 | 1.22x |
11
+ | tg128 (pp2048 run) | 32.9 | 37.3 | 1.13x |
12
+
13
+ Mean KLD vs stock: 0.2058 ± 0.0123 (prefill+decode, 32 chunks x 512)
14
+ Mean PPL(Q)/PPL(base): 1.0136
build.sh ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ #!/usr/bin/env bash
2
+ set -e
3
+ cmake -B build -DCMAKE_BUILD_TYPE=Release -DGGML_NATIVE=ON -DGGML_CUDA=OFF -DLLAMA_CURL=OFF
4
+ cmake --build build -j$(nproc) --target llama-bench llama-cli llama-perplexity
machine.txt ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ CPU: Intel Xeon Platinum 8559C (Emerald Rapids)
2
+ Threads used: 16 (cgroup cpu.max 1600000/100000)
3
+ Memory cap: ~104 GiB (cgroup memory.max)
4
+ Build: GCC 14.2.0, cmake 3.31.6, -march=native (AVX512, VNNI, AMX-INT8)
5
+ llama.cpp base commit: 338fce651 (b11942)
6
+ AMX buffer: 1716 MiB (Q8_0 2D tensors); CPU_REPACK: 864 MiB (Q4_K 8x8)
patches/BASE_COMMIT ADDED
@@ -0,0 +1 @@
 
 
1
+ 338fce6511c744893cc0d3d958b275f40552c7a4
patches/flint-cpu-opt.patch ADDED
@@ -0,0 +1,513 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ diff --git a/ggml/src/ggml-cpu/amx/amx.cpp b/ggml/src/ggml-cpu/amx/amx.cpp
2
+ index e90c2797f..e8b61a2eb 100644
3
+ --- a/ggml/src/ggml-cpu/amx/amx.cpp
4
+ +++ b/ggml/src/ggml-cpu/amx/amx.cpp
5
+ @@ -161,6 +161,17 @@ class extra_buffer_type : ggml::cpu::extra_buffer_type {
6
+ if (op->ne[0] % (TILE_N * 2)) {
7
+ return false;
8
+ }
9
+ + // machine-specific knobs: keep selected types out of the AMX buffer (decode then uses the VNNI vec_dot kernels)
10
+ + {
11
+ + // Q6_K (lm_head): the AVX512-VNNI vec_dot is ~9% faster for decode than the AMX M=1 kernel; opt back in with GGML_AMX_Q6K=1
12
+ + static const char * keep_q6 = getenv("GGML_AMX_Q6K");
13
+ + if (src0->type == GGML_TYPE_Q6_K && !(keep_q6 && keep_q6[0] == '1')) { return false; }
14
+ + static const char * skip = getenv("GGML_AMX_SKIP");
15
+ + if (skip) {
16
+ + if (src0->type == GGML_TYPE_Q6_K && strstr(skip, "q6_K")) { return false; }
17
+ + if (src0->type == GGML_TYPE_Q8_0 && strstr(skip, "q8_0")) { return false; }
18
+ + }
19
+ + }
20
+ int alignment;
21
+ switch (src0->type) {
22
+ case GGML_TYPE_Q4_0:
23
+ diff --git a/ggml/src/ggml-cpu/amx/mmq.cpp b/ggml/src/ggml-cpu/amx/mmq.cpp
24
+ index 9f3a744b5..d23e4d971 100644
25
+ --- a/ggml/src/ggml-cpu/amx/mmq.cpp
26
+ +++ b/ggml/src/ggml-cpu/amx/mmq.cpp
27
+ @@ -1989,6 +1989,91 @@ struct tinygemm_kernel_vnni<block_q8_K, block_iq4_xs, float, BLOCK_M, BLOCK_N, B
28
+ (const char *)src0->data + src0_offset + PACKED_INDEX(nb * kTilesN, 0, KB, TILE_SIZE), \
29
+ (float *) dst->data + dst_offset + nb_start, ldc)
30
+
31
+ +// Q8_0 x Q8_0 AMX gemm with register accumulation: one 16x16 output tile at a time, its 16 float row
32
+ +// accumulators live in zmm registers across all K blocks (the generic kernel read-modify-writes C through memory
33
+ +// every 32-wide K block and converts the A scale per row). The AMX product of block i+1 is issued before the AVX
34
+ +// epilogue of block i so the two overlap. Per-block math (cvt(int32) * (dW*dA) accumulated with an fma, blocks in
35
+ +// order) is identical to acc_C, so results are bit-identical. Returns false when KB is too large for the scale cache.
36
+ +// tile registers are pasted into the asm string by the intrinsics, so they must be literals: one function per slot
37
+ +#define Q8_REGACC_ISSUE_FN(NAME, TB_, TA_, TC_, SLOT) \
38
+ +static ALWAYS_INLINE void NAME(const block_q8_0 * RESTRICT A, const char * RESTRICT B, int KB, int u, int t, \
39
+ + int mrows, int i, int8_t (*TileA)[TILE_M * TILE_K], int32_t (*TileC)[TILE_M * TILE_N]) { \
40
+ + const int TILE_SIZE = get_tile_size<block_q8_0>(); /* 16 x 34 B + 16 x int32 compensation */ \
41
+ + const char * Bb = B + PACKED_INDEX(t, i, KB, TILE_SIZE); \
42
+ + _tile_loadd(TB_, Bb, TILE_N * VNNI_BLK); \
43
+ + const block_q8_0 * Ab = A + (size_t) (u * TILE_M) * KB + i; \
44
+ + if (mrows == TILE_M) { \
45
+ + _tile_loadd(TA_, Ab->qs, KB * sizeof(block_q8_0)); \
46
+ + } else { \
47
+ + unpack_A(TileA[SLOT], Ab, KB, mrows); \
48
+ + _tile_loadd(TA_, TileA[SLOT], TILE_K); \
49
+ + } \
50
+ + _tile_zero(TC_); \
51
+ + _tile_dpbssd(TC_, TA_, TB_); \
52
+ + _tile_stored(TC_, TileC[SLOT], TILE_N * sizeof(int32_t)); \
53
+ +}
54
+ +Q8_REGACC_ISSUE_FN(q8_regacc_issue0, 0, 2, 4, 0)
55
+ +Q8_REGACC_ISSUE_FN(q8_regacc_issue1, 1, 3, 5, 1)
56
+ +#undef Q8_REGACC_ISSUE_FN
57
+ +
58
+ +static bool tinygemm_kernel_amx_q8_0_regacc(int M, int KB, const block_q8_0 * RESTRICT A, const char * RESTRICT B,
59
+ + float * RESTRICT C, int ldc) {
60
+ + constexpr int MAX_KB = 256;
61
+ + if (KB > MAX_KB || KB < 1) {
62
+ + return false;
63
+ + }
64
+ + const int TILE_SIZE = get_tile_size<block_q8_0>(); /* 16 x 34 B + 16 x int32 compensation */
65
+ + constexpr int SCALE_OFF = TILE_N * TILE_K;
66
+ +
67
+ + alignas(64) static thread_local int8_t TileA[2][TILE_M * TILE_K];
68
+ + alignas(64) static thread_local int32_t TileC[2][TILE_M * TILE_N];
69
+ + alignas(64) static thread_local float sA[2 * TILE_M][MAX_KB];
70
+ +
71
+ + const int rows = std::min(M, 2 * TILE_M);
72
+ + for (int r = 0; r < 2 * TILE_M; ++r) {
73
+ + for (int i = 0; i < KB; ++i) {
74
+ + sA[r][i] = r < rows ? GGML_CPU_FP16_TO_FP32(A[(size_t) r * KB + i].d) : 0.0f;
75
+ + }
76
+ + }
77
+ +
78
+ + const int nu = rows > TILE_M ? 2 : 1;
79
+ + for (int u = 0; u < nu; ++u) {
80
+ + const int mrows = std::min(TILE_M, rows - u * TILE_M);
81
+ + for (int t = 0; t < 2; ++t) {
82
+ + __m512 acc[TILE_M];
83
+ + #pragma GCC unroll 16
84
+ + for (int m = 0; m < TILE_M; ++m) {
85
+ + acc[m] = _mm512_setzero_ps();
86
+ + }
87
+ +
88
+ + q8_regacc_issue0(A, B, KB, u, t, mrows, 0, TileA, TileC);
89
+ + for (int i = 0; i < KB; ++i) {
90
+ + const int slot = i & 1;
91
+ + if (i + 1 < KB) {
92
+ + if (slot == 0) {
93
+ + q8_regacc_issue1(A, B, KB, u, t, mrows, i + 1, TileA, TileC);
94
+ + } else {
95
+ + q8_regacc_issue0(A, B, KB, u, t, mrows, i + 1, TileA, TileC);
96
+ + }
97
+ + }
98
+ + const __m512 vd0 = _mm512_cvtph_ps(_mm256_loadu_si256((const __m256i *) (B + PACKED_INDEX(t, i, KB, TILE_SIZE) + SCALE_OFF)));
99
+ + const int32_t * tile = TileC[slot];
100
+ + const float * sa = &sA[u * TILE_M][0];
101
+ + #pragma GCC unroll 16
102
+ + for (int m = 0; m < TILE_M; ++m) {
103
+ + const __m512 vtile = _mm512_cvtepi32_ps(_mm512_loadu_si512(tile + m * TILE_N));
104
+ + const __m512 vd1 = _mm512_set1_ps(sa[(size_t) m * MAX_KB + i]);
105
+ + acc[m] = _mm512_fmadd_ps(vtile, _mm512_mul_ps(vd0, vd1), acc[m]);
106
+ + }
107
+ + }
108
+ + for (int m = 0; m < mrows; ++m) {
109
+ + _mm512_storeu_ps(C + (size_t) (u * TILE_M + m) * ldc + t * TILE_N, acc[m]);
110
+ + }
111
+ + }
112
+ + }
113
+ + return true;
114
+ +}
115
+ +
116
+ template <typename TA, typename TB, typename TC, int BLOCK_K,
117
+ typename std::enable_if<!is_type_qkk<TB>::value, int>::type = 0>
118
+ void tinygemm_kernel_amx(int M, int N, int KB, const void * RESTRICT _A, const void * RESTRICT _B, TC * RESTRICT C, int ldc) {
119
+ @@ -1997,6 +2082,12 @@ void tinygemm_kernel_amx(int M, int N, int KB, const void * RESTRICT _A, const v
120
+ const bool need_unpack = do_unpack<TB>::value;
121
+
122
+ GGML_ASSERT(M <= 2 * TILE_M && N == 2 * TILE_N);
123
+ + if constexpr (std::is_same<TB, block_q8_0>::value && std::is_same<TA, block_q8_0>::value) {
124
+ + static const bool regacc = [] { const char * e = getenv("GGML_AMX_Q8_REGACC"); return !(e && e[0] == '0'); }();
125
+ + if (regacc && tinygemm_kernel_amx_q8_0_regacc(M, KB, static_cast<const block_q8_0 *>(_A), static_cast<const char *>(_B), C, ldc)) {
126
+ + return;
127
+ + }
128
+ + }
129
+ const TA * RESTRICT A = static_cast<const TA *>(_A);
130
+ const char * RESTRICT B = static_cast<const char *>(_B);
131
+
132
+ @@ -2434,8 +2525,9 @@ void ggml_backend_amx_mul_mat(const ggml_compute_params * params, struct ggml_te
133
+
134
+ if (M == 1) {
135
+ // MB = 1 and handle 8 tiles in each block
136
+ - constexpr int kTilesN = 4;
137
+ - constexpr int BLOCK_N = TILE_N * kTilesN;
138
+ + // small N (e.g. 512) would give fewer tasks than threads with 64-wide blocks; use 32-wide blocks
139
+ + const int BLOCK_N = (N <= 1024) ? 32 : 64;
140
+ + const int kTilesN = BLOCK_N / TILE_N;
141
+ const int NB = div_up(N, BLOCK_N);
142
+
143
+ parallel_for_ggml(params, n_batch * NB, [&](int begin, int end) {
144
+ diff --git a/ggml/src/ggml-cpu/arch/x86/quants.c b/ggml/src/ggml-cpu/arch/x86/quants.c
145
+ index 21cd6312a..0e8aafa76 100644
146
+ --- a/ggml/src/ggml-cpu/arch/x86/quants.c
147
+ +++ b/ggml/src/ggml-cpu/arch/x86/quants.c
148
+ @@ -2118,7 +2118,89 @@ void ggml_vec_dot_q4_K_q8_K(int n, float * GGML_RESTRICT s, size_t bs, const voi
149
+
150
+ uint32_t utmp[4];
151
+
152
+ -#if defined __AVX2__
153
+ +#if defined(__AVX512F__) && defined(__AVX512BW__) && defined(__AVX512VL__) && defined(__AVX512VNNI__)
154
+ + // AVX512-VNNI path (Q4_K x Q8_K): 64 weights per vector, two 32-wide sub-blocks per vector.
155
+ + const __m512i m4 = _mm512_set1_epi8(0xF);
156
+ + #define K4_SCALE_IDX(a, b) _mm512_inserti64x4( \
157
+ + _mm512_castsi256_si512(_mm256_set_m128i(_mm_set1_epi16((short)((2*(a)) | ((2*(a)+1) << 8))), _mm_set1_epi16((short)((2*(a)) | ((2*(a)+1) << 8))))), \
158
+ + _mm256_set_m128i(_mm_set1_epi16((short)((2*(b)) | ((2*(b)+1) << 8))), _mm_set1_epi16((short)((2*(b)) | ((2*(b)+1) << 8)))), 1)
159
+ + const __m512i sidx0 = K4_SCALE_IDX(0, 2);
160
+ + const __m512i sidx1 = K4_SCALE_IDX(1, 3);
161
+ + const __m512i sidx2 = K4_SCALE_IDX(4, 6);
162
+ + const __m512i sidx3 = K4_SCALE_IDX(5, 7);
163
+ + #undef K4_SCALE_IDX
164
+ +
165
+ + __m512 acc = _mm512_setzero_ps();
166
+ + __m128 acc_m = _mm_setzero_ps();
167
+ +
168
+ + for (int i = 0; i < nb; ++i) {
169
+ +#ifndef K4_PF_DIST
170
+ +#define K4_PF_DIST 40 // ~9 KB ahead, tuned for Xeon 8559C (prefetch into L2)
171
+ +#endif
172
+ + static int k4pf = -1;
173
+ + if (k4pf < 0) { const char * e = getenv("GGML_K4_PF"); k4pf = e ? atoi(e) : K4_PF_DIST; }
174
+ + {
175
+ + const char * pf = (const char *)(x + i + k4pf);
176
+ + _mm_prefetch(pf, _MM_HINT_T1);
177
+ + _mm_prefetch(pf + 64, _MM_HINT_T1);
178
+ + _mm_prefetch(pf + 128, _MM_HINT_T1);
179
+ + }
180
+ +
181
+ + const float d = y[i].d * GGML_CPU_FP16_TO_FP32(x[i].d);
182
+ + const float dmin = -y[i].d * GGML_CPU_FP16_TO_FP32(x[i].dmin);
183
+ +
184
+ + memcpy(utmp, x[i].scales, 12);
185
+ + utmp[3] = ((utmp[2] >> 4) & kmask2) | (((utmp[1] >> 6) & kmask3) << 4);
186
+ + const uint32_t uaux = utmp[1] & kmask1;
187
+ + utmp[1] = (utmp[2] & kmask2) | (((utmp[0] >> 6) & kmask3) << 4);
188
+ + utmp[2] = uaux;
189
+ + utmp[0] &= kmask1;
190
+ +
191
+ + const uint8_t * GGML_RESTRICT q4 = x[i].qs;
192
+ + const int8_t * GGML_RESTRICT q8 = y[i].qs;
193
+ +
194
+ + const __m256i mins_and_scales = _mm256_cvtepu8_epi16(_mm_set_epi32(utmp[3], utmp[2], utmp[1], utmp[0]));
195
+ +
196
+ + const __m256i q8sums = _mm256_loadu_si256((const __m256i*)y[i].bsums);
197
+ + const __m128i q8s = _mm_hadd_epi16(_mm256_extracti128_si256(q8sums, 0), _mm256_extracti128_si256(q8sums, 1));
198
+ + const __m128i prod = _mm_madd_epi16(_mm256_extracti128_si256(mins_and_scales, 1), q8s);
199
+ + acc_m = _mm_fmadd_ps(_mm_set1_ps(dmin), _mm_cvtepi32_ps(prod), acc_m);
200
+ +
201
+ + const __m512i scales = _mm512_broadcast_i32x4(_mm256_extracti128_si256(mins_and_scales, 0));
202
+ +
203
+ + const __m512i v0 = _mm512_loadu_si512((const void *)(q4));
204
+ + const __m512i v1 = _mm512_loadu_si512((const void *)(q4 + 64));
205
+ +
206
+ + const __m512i Q0 = _mm512_loadu_si512((const void *)(q8));
207
+ + const __m512i Q1 = _mm512_loadu_si512((const void *)(q8 + 64));
208
+ + const __m512i Q2 = _mm512_loadu_si512((const void *)(q8 + 128));
209
+ + const __m512i Q3 = _mm512_loadu_si512((const void *)(q8 + 192));
210
+ +
211
+ + const __m512i a0 = _mm512_and_si512(v0, m4);
212
+ + const __m512i a1 = _mm512_and_si512(_mm512_srli_epi16(v0, 4), m4);
213
+ + const __m512i a2 = _mm512_and_si512(v1, m4);
214
+ + const __m512i a3 = _mm512_and_si512(_mm512_srli_epi16(v1, 4), m4);
215
+ +
216
+ + const __m512i b0 = _mm512_shuffle_i64x2(Q0, Q1, 0x44);
217
+ + const __m512i b1 = _mm512_shuffle_i64x2(Q0, Q1, 0xEE);
218
+ + const __m512i b2 = _mm512_shuffle_i64x2(Q2, Q3, 0x44);
219
+ + const __m512i b3 = _mm512_shuffle_i64x2(Q2, Q3, 0xEE);
220
+ +
221
+ + __m512i sumi = _mm512_setzero_si512();
222
+ + sumi = _mm512_dpwssd_epi32(sumi, _mm512_maddubs_epi16(a0, b0), _mm512_shuffle_epi8(scales, sidx0));
223
+ + sumi = _mm512_dpwssd_epi32(sumi, _mm512_maddubs_epi16(a1, b1), _mm512_shuffle_epi8(scales, sidx1));
224
+ + sumi = _mm512_dpwssd_epi32(sumi, _mm512_maddubs_epi16(a2, b2), _mm512_shuffle_epi8(scales, sidx2));
225
+ + sumi = _mm512_dpwssd_epi32(sumi, _mm512_maddubs_epi16(a3, b3), _mm512_shuffle_epi8(scales, sidx3));
226
+ +
227
+ + acc = _mm512_fmadd_ps(_mm512_set1_ps(d), _mm512_cvtepi32_ps(sumi), acc);
228
+ + }
229
+ +
230
+ + acc_m = _mm_add_ps(acc_m, _mm_movehl_ps(acc_m, acc_m));
231
+ + acc_m = _mm_add_ss(acc_m, _mm_movehdup_ps(acc_m));
232
+ +
233
+ + *s = _mm512_reduce_add_ps(acc) + _mm_cvtss_f32(acc_m);
234
+ +
235
+ +#elif defined __AVX2__
236
+
237
+ const __m256i m4 = _mm256_set1_epi8(0xF);
238
+
239
+ @@ -2296,7 +2378,97 @@ void ggml_vec_dot_q5_K_q8_K(int n, float * GGML_RESTRICT s, size_t bs, const voi
240
+
241
+ uint32_t utmp[4];
242
+
243
+ -#if defined __AVX2__
244
+ +#if defined(__AVX512F__) && defined(__AVX512BW__) && defined(__AVX512VL__) && defined(__AVX512VNNI__)
245
+ + // AVX512-VNNI path (Q5_K x Q8_K), same structure as the Q4_K kernel: 64 weights per vector, two 32-wide
246
+ + // sub-blocks per vector. The 5th bit of sub-block s is bit s of qh[l]; it is merged in as +16 before maddubs.
247
+ + const __m512i m4 = _mm512_set1_epi8(0xF);
248
+ + const __m512i m10 = _mm512_set1_epi8(0x10);
249
+ + #define K5_SCALE_IDX(a, b) _mm512_inserti64x4( \
250
+ + _mm512_castsi256_si512(_mm256_set_m128i(_mm_set1_epi16((short)((2*(a)) | ((2*(a)+1) << 8))), _mm_set1_epi16((short)((2*(a)) | ((2*(a)+1) << 8))))), \
251
+ + _mm256_set_m128i(_mm_set1_epi16((short)((2*(b)) | ((2*(b)+1) << 8))), _mm_set1_epi16((short)((2*(b)) | ((2*(b)+1) << 8)))), 1)
252
+ + const __m512i sidx0 = K5_SCALE_IDX(0, 2);
253
+ + const __m512i sidx1 = K5_SCALE_IDX(1, 3);
254
+ + const __m512i sidx2 = K5_SCALE_IDX(4, 6);
255
+ + const __m512i sidx3 = K5_SCALE_IDX(5, 7);
256
+ + #undef K5_SCALE_IDX
257
+ + #define K5_CNT(a, b) _mm512_inserti64x4(_mm512_castsi256_si512(_mm256_set1_epi16(a)), _mm256_set1_epi16(b), 1)
258
+ + const __m512i cl0 = K5_CNT(4, 2); // sub-blocks 0,2 : bit s -> bit 4 via left shift 4-s
259
+ + const __m512i cl1 = K5_CNT(3, 1); // sub-blocks 1,3
260
+ + const __m512i cr2 = K5_CNT(0, 2); // sub-blocks 4,6 : right shift s-4
261
+ + const __m512i cr3 = K5_CNT(1, 3); // sub-blocks 5,7
262
+ + #undef K5_CNT
263
+ +
264
+ + static int k5pf = -1;
265
+ + if (k5pf < 0) { const char * e = getenv("GGML_K5_PF"); k5pf = e ? atoi(e) : 48; }
266
+ +
267
+ + __m512 acc = _mm512_setzero_ps();
268
+ + __m128 acc_m = _mm_setzero_ps();
269
+ +
270
+ + for (int i = 0; i < nb; ++i) {
271
+ + {
272
+ + const char * pf = (const char *)(x + i + k5pf);
273
+ + _mm_prefetch(pf, _MM_HINT_T1);
274
+ + _mm_prefetch(pf + 64, _MM_HINT_T1);
275
+ + _mm_prefetch(pf + 128, _MM_HINT_T1);
276
+ + }
277
+ +
278
+ + const float d = y[i].d * GGML_CPU_FP16_TO_FP32(x[i].d);
279
+ + const float dmin = -y[i].d * GGML_CPU_FP16_TO_FP32(x[i].dmin);
280
+ +
281
+ + memcpy(utmp, x[i].scales, 12);
282
+ + utmp[3] = ((utmp[2] >> 4) & kmask2) | (((utmp[1] >> 6) & kmask3) << 4);
283
+ + const uint32_t uaux = utmp[1] & kmask1;
284
+ + utmp[1] = (utmp[2] & kmask2) | (((utmp[0] >> 6) & kmask3) << 4);
285
+ + utmp[2] = uaux;
286
+ + utmp[0] &= kmask1;
287
+ +
288
+ + const uint8_t * GGML_RESTRICT q5 = x[i].qs;
289
+ + const int8_t * GGML_RESTRICT q8 = y[i].qs;
290
+ +
291
+ + const __m256i mins_and_scales = _mm256_cvtepu8_epi16(_mm_set_epi32(utmp[3], utmp[2], utmp[1], utmp[0]));
292
+ +
293
+ + const __m256i q8sums = _mm256_loadu_si256((const __m256i*)y[i].bsums);
294
+ + const __m128i q8s = _mm_hadd_epi16(_mm256_extracti128_si256(q8sums, 0), _mm256_extracti128_si256(q8sums, 1));
295
+ + const __m128i prod = _mm_madd_epi16(_mm256_extracti128_si256(mins_and_scales, 1), q8s);
296
+ + acc_m = _mm_fmadd_ps(_mm_set1_ps(dmin), _mm_cvtepi32_ps(prod), acc_m);
297
+ +
298
+ + const __m512i scales = _mm512_broadcast_i32x4(_mm256_extracti128_si256(mins_and_scales, 0));
299
+ +
300
+ + const __m512i qhv = _mm512_broadcast_i64x4(_mm256_loadu_si256((const __m256i *)x[i].qh));
301
+ +
302
+ + const __m512i v0 = _mm512_loadu_si512((const void *)(q5));
303
+ + const __m512i v1 = _mm512_loadu_si512((const void *)(q5 + 64));
304
+ +
305
+ + const __m512i Q0 = _mm512_loadu_si512((const void *)(q8));
306
+ + const __m512i Q1 = _mm512_loadu_si512((const void *)(q8 + 64));
307
+ + const __m512i Q2 = _mm512_loadu_si512((const void *)(q8 + 128));
308
+ + const __m512i Q3 = _mm512_loadu_si512((const void *)(q8 + 192));
309
+ +
310
+ + const __m512i a0 = _mm512_or_si512(_mm512_and_si512(v0, m4), _mm512_and_si512(_mm512_sllv_epi16(qhv, cl0), m10));
311
+ + const __m512i a1 = _mm512_or_si512(_mm512_and_si512(_mm512_srli_epi16(v0, 4), m4), _mm512_and_si512(_mm512_sllv_epi16(qhv, cl1), m10));
312
+ + const __m512i a2 = _mm512_or_si512(_mm512_and_si512(v1, m4), _mm512_and_si512(_mm512_srlv_epi16(qhv, cr2), m10));
313
+ + const __m512i a3 = _mm512_or_si512(_mm512_and_si512(_mm512_srli_epi16(v1, 4), m4), _mm512_and_si512(_mm512_srlv_epi16(qhv, cr3), m10));
314
+ +
315
+ + const __m512i b0 = _mm512_shuffle_i64x2(Q0, Q1, 0x44);
316
+ + const __m512i b1 = _mm512_shuffle_i64x2(Q0, Q1, 0xEE);
317
+ + const __m512i b2 = _mm512_shuffle_i64x2(Q2, Q3, 0x44);
318
+ + const __m512i b3 = _mm512_shuffle_i64x2(Q2, Q3, 0xEE);
319
+ +
320
+ + __m512i sumi = _mm512_setzero_si512();
321
+ + sumi = _mm512_dpwssd_epi32(sumi, _mm512_maddubs_epi16(a0, b0), _mm512_shuffle_epi8(scales, sidx0));
322
+ + sumi = _mm512_dpwssd_epi32(sumi, _mm512_maddubs_epi16(a1, b1), _mm512_shuffle_epi8(scales, sidx1));
323
+ + sumi = _mm512_dpwssd_epi32(sumi, _mm512_maddubs_epi16(a2, b2), _mm512_shuffle_epi8(scales, sidx2));
324
+ + sumi = _mm512_dpwssd_epi32(sumi, _mm512_maddubs_epi16(a3, b3), _mm512_shuffle_epi8(scales, sidx3));
325
+ +
326
+ + acc = _mm512_fmadd_ps(_mm512_set1_ps(d), _mm512_cvtepi32_ps(sumi), acc);
327
+ + }
328
+ +
329
+ + acc_m = _mm_add_ps(acc_m, _mm_movehl_ps(acc_m, acc_m));
330
+ + acc_m = _mm_add_ss(acc_m, _mm_movehdup_ps(acc_m));
331
+ +
332
+ + *s = _mm512_reduce_add_ps(acc) + _mm_cvtss_f32(acc_m);
333
+ +
334
+ +#elif defined __AVX2__
335
+
336
+ const __m256i m4 = _mm256_set1_epi8(0xF);
337
+ const __m128i mzero = _mm_setzero_si128();
338
+ @@ -2500,7 +2672,81 @@ void ggml_vec_dot_q6_K_q8_K(int n, float * GGML_RESTRICT s, size_t bs, const voi
339
+
340
+ const int nb = n / QK_K;
341
+
342
+ -#if defined __AVX2__
343
+ +#if defined(__AVX512F__) && defined(__AVX512BW__) && defined(__AVX512VL__) && defined(__AVX512VNNI__)
344
+ + // AVX512-VNNI path (Q6_K x Q8_K): each 64-byte vector holds two 32-wide chunks.
345
+ + const __m512i m15 = _mm512_set1_epi8(0xF);
346
+ + const __m512i m30 = _mm512_set1_epi8(0x30);
347
+ + const __m512i shl_cnt = _mm512_inserti64x4(_mm512_castsi256_si512(_mm256_set1_epi16(4)), _mm256_set1_epi16(2), 1);
348
+ + const __m512i shr_cnt = _mm512_inserti64x4(_mm512_castsi256_si512(_mm256_set1_epi16(0)), _mm256_set1_epi16(2), 1);
349
+ + #define Q6_SIDX(c1, c2) _mm512_inserti64x4( \
350
+ + _mm512_castsi256_si512(_mm256_set_m128i(_mm_set1_epi16((short)(2*(c1)+1)), _mm_set1_epi16((short)(2*(c1))))), \
351
+ + _mm256_set_m128i(_mm_set1_epi16((short)(2*(c2)+1)), _mm_set1_epi16((short)(2*(c2)))), 1)
352
+ + const __m512i sidx0 = Q6_SIDX(0, 1);
353
+ + const __m512i sidx1 = Q6_SIDX(2, 3);
354
+ + const __m512i sidx2 = Q6_SIDX(4, 5);
355
+ + const __m512i sidx3 = Q6_SIDX(6, 7);
356
+ + #undef Q6_SIDX
357
+ +
358
+ + __m512 acc = _mm512_setzero_ps();
359
+ +
360
+ + for (int i = 0; i < nb; ++i) {
361
+ +#ifndef Q6_PF_DIST
362
+ +#define Q6_PF_DIST 44 // ~9 KB ahead, tuned for Xeon 8559C (prefetch into L2)
363
+ +#endif
364
+ + static int q6pf = -1;
365
+ + if (q6pf < 0) { const char * e = getenv("GGML_Q6_PF"); q6pf = e ? atoi(e) : Q6_PF_DIST; }
366
+ + {
367
+ + const char * pf = (const char *)(x + i + q6pf);
368
+ + _mm_prefetch(pf, _MM_HINT_T1);
369
+ + _mm_prefetch(pf + 64, _MM_HINT_T1);
370
+ + _mm_prefetch(pf + 128, _MM_HINT_T1);
371
+ + _mm_prefetch(pf + 192, _MM_HINT_T1);
372
+ + }
373
+ +
374
+ + const float d = y[i].d * GGML_CPU_FP16_TO_FP32(x[i].d);
375
+ +
376
+ + const uint8_t * GGML_RESTRICT ql = x[i].ql;
377
+ + const uint8_t * GGML_RESTRICT qh = x[i].qh;
378
+ + const int8_t * GGML_RESTRICT q8 = y[i].qs;
379
+ +
380
+ + // -32 offset handled with bsums
381
+ + const __m256i q8sums = _mm256_loadu_si256((const __m256i*)y[i].bsums);
382
+ + const __m256i scales_16 = _mm256_cvtepi8_epi16(_mm_loadu_si128((const __m128i*)x[i].scales));
383
+ + const __m256i q8sclsub = _mm256_slli_epi32(_mm256_madd_epi16(q8sums, scales_16), 5);
384
+ + const __m512i scales512 = _mm512_zextsi256_si512(scales_16);
385
+ +
386
+ + const __m512i QL0 = _mm512_loadu_si512((const void *)(ql));
387
+ + const __m512i QL1 = _mm512_loadu_si512((const void *)(ql + 64));
388
+ + const __m512i H0 = _mm512_broadcast_i64x4(_mm256_loadu_si256((const __m256i*)(qh)));
389
+ + const __m512i H1 = _mm512_broadcast_i64x4(_mm256_loadu_si256((const __m256i*)(qh + 32)));
390
+ +
391
+ + const __m512i X0 = _mm512_loadu_si512((const void *)(q8));
392
+ + const __m512i X1 = _mm512_loadu_si512((const void *)(q8 + 64));
393
+ + const __m512i X2 = _mm512_loadu_si512((const void *)(q8 + 128));
394
+ + const __m512i X3 = _mm512_loadu_si512((const void *)(q8 + 192));
395
+ +
396
+ + // 6-bit values (unsigned 0..63): low nibble / high nibble of ql combined with 2 bits of qh
397
+ + const __m512i q6_0 = _mm512_or_si512(_mm512_and_si512(QL0, m15),
398
+ + _mm512_and_si512(_mm512_sllv_epi16(H0, shl_cnt), m30));
399
+ + const __m512i q6_1 = _mm512_or_si512(_mm512_and_si512(_mm512_srli_epi16(QL0, 4), m15),
400
+ + _mm512_and_si512(_mm512_srlv_epi16(H0, shr_cnt), m30));
401
+ + const __m512i q6_2 = _mm512_or_si512(_mm512_and_si512(QL1, m15),
402
+ + _mm512_and_si512(_mm512_sllv_epi16(H1, shl_cnt), m30));
403
+ + const __m512i q6_3 = _mm512_or_si512(_mm512_and_si512(_mm512_srli_epi16(QL1, 4), m15),
404
+ + _mm512_and_si512(_mm512_srlv_epi16(H1, shr_cnt), m30));
405
+ +
406
+ + __m512i sumi = _mm512_zextsi256_si512(_mm256_sub_epi32(_mm256_setzero_si256(), q8sclsub));
407
+ + sumi = _mm512_dpwssd_epi32(sumi, _mm512_maddubs_epi16(q6_0, X0), _mm512_permutexvar_epi16(sidx0, scales512));
408
+ + sumi = _mm512_dpwssd_epi32(sumi, _mm512_maddubs_epi16(q6_1, X1), _mm512_permutexvar_epi16(sidx1, scales512));
409
+ + sumi = _mm512_dpwssd_epi32(sumi, _mm512_maddubs_epi16(q6_2, X2), _mm512_permutexvar_epi16(sidx2, scales512));
410
+ + sumi = _mm512_dpwssd_epi32(sumi, _mm512_maddubs_epi16(q6_3, X3), _mm512_permutexvar_epi16(sidx3, scales512));
411
+ +
412
+ + acc = _mm512_fmadd_ps(_mm512_set1_ps(d), _mm512_cvtepi32_ps(sumi), acc);
413
+ + }
414
+ +
415
+ + *s = _mm512_reduce_add_ps(acc);
416
+ +
417
+ +#elif defined __AVX2__
418
+
419
+ const __m256i m3 = _mm256_set1_epi8(3);
420
+ const __m256i m15 = _mm256_set1_epi8(15);
421
+ diff --git a/ggml/src/ggml-cpu/ggml-cpu.c b/ggml/src/ggml-cpu/ggml-cpu.c
422
+ index 53d39d249..02889fd24 100644
423
+ --- a/ggml/src/ggml-cpu/ggml-cpu.c
424
+ +++ b/ggml/src/ggml-cpu/ggml-cpu.c
425
+ @@ -35,6 +35,49 @@
426
+ #include <limits.h>
427
+ #include <stdarg.h>
428
+ #include <signal.h>
429
+ +
430
+ +// ---- lightweight per-op profiling (GGML_PROF=1), diagnostic only ----
431
+ +static int g_prof = -1;
432
+ +struct ggml_prof_entry { const char * op; const char * type; const char * ty0; int64_t ne0, ne1, ne2; int64_t us; int64_t calls; };
433
+ +static struct ggml_prof_entry g_prof_tab[1024];
434
+ +static int g_prof_n = 0;
435
+ +static int64_t g_prof_total = 0;
436
+ +
437
+ +static void ggml_prof_dump(void) {
438
+ + if (g_prof_n == 0) return;
439
+ + fprintf(stderr, "\n[GGML_PROF] total_us=%" PRId64 "\n", g_prof_total);
440
+ + for (int i = 0; i < g_prof_n; ++i) {
441
+ + fprintf(stderr, "[GGML_PROF] %-20s %-6s<%-6s [%6" PRId64 ",%5" PRId64 ",%4" PRId64 "] %9.2f ms %6.2f%% calls=%" PRId64 " us/call=%.1f\n",
442
+ + g_prof_tab[i].op, g_prof_tab[i].type, g_prof_tab[i].ty0, g_prof_tab[i].ne0, g_prof_tab[i].ne1, g_prof_tab[i].ne2,
443
+ + g_prof_tab[i].us/1000.0,
444
+ + 100.0*g_prof_tab[i].us/(g_prof_total?g_prof_total:1),
445
+ + g_prof_tab[i].calls,
446
+ + (double)g_prof_tab[i].us/(g_prof_tab[i].calls?g_prof_tab[i].calls:1));
447
+ + }
448
+ + fflush(stderr);
449
+ +}
450
+ +
451
+ +static void ggml_prof_add(const struct ggml_tensor * node, int64_t us) {
452
+ + g_prof_total += us;
453
+ + const char * op = ggml_op_name(node->op);
454
+ + const char * ty = ggml_type_name(node->type);
455
+ + const char * ty0 = node->src[0] ? ggml_type_name(node->src[0]->type) : "-";
456
+ + const int64_t ne0 = node->ne[0], ne1 = node->ne[1], ne2 = node->ne[2];
457
+ + for (int i = 0; i < g_prof_n; ++i) {
458
+ + if (strcmp(g_prof_tab[i].op, op) == 0 && strcmp(g_prof_tab[i].type, ty) == 0 && strcmp(g_prof_tab[i].ty0, ty0) == 0 &&
459
+ + g_prof_tab[i].ne0 == ne0 && g_prof_tab[i].ne1 == ne1 && g_prof_tab[i].ne2 == ne2) {
460
+ + g_prof_tab[i].us += us; g_prof_tab[i].calls++;
461
+ + return;
462
+ + }
463
+ + }
464
+ + if (g_prof_n < 1024) {
465
+ + g_prof_tab[g_prof_n].op = op; g_prof_tab[g_prof_n].type = ty; g_prof_tab[g_prof_n].ty0 = ty0;
466
+ + g_prof_tab[g_prof_n].ne0 = ne0; g_prof_tab[g_prof_n].ne1 = ne1; g_prof_tab[g_prof_n].ne2 = ne2;
467
+ + g_prof_tab[g_prof_n].us = us; g_prof_tab[g_prof_n].calls = 1;
468
+ + g_prof_n++;
469
+ + }
470
+ +}
471
+ +// ----------------------------------------------------------------
472
+ #if defined(__gnu_linux__)
473
+ #include <syscall.h>
474
+ #endif
475
+ @@ -3148,6 +3191,13 @@ static thread_ret_t ggml_graph_compute_thread(void * data) {
476
+ continue;
477
+ }
478
+
479
+ + if (g_prof < 0) {
480
+ + const char * e = getenv("GGML_PROF");
481
+ + g_prof = e && e[0] == '1' ? 1 : 0;
482
+ + if (g_prof) atexit(ggml_prof_dump);
483
+ + }
484
+ + const int64_t prof_t0 = (g_prof && state->ith == 0) ? ggml_time_us() : 0;
485
+ +
486
+ // TODO: move fused-op detection into ggml_graph_plan so fusion decisions are made once at planning time
487
+ // Try fused ops, fall back to normal compute
488
+ const int n_fused = ggml_cpu_try_fuse_ops(cgraph, node_n, &params, cplan);
489
+ @@ -3166,6 +3216,9 @@ static thread_ret_t ggml_graph_compute_thread(void * data) {
490
+ if (node_n + 1 < cgraph->n_nodes) {
491
+ ggml_barrier(state->threadpool);
492
+ }
493
+ + if (g_prof && state->ith == 0) {
494
+ + ggml_prof_add(node, ggml_time_us() - prof_t0);
495
+ + }
496
+ }
497
+
498
+ #ifdef GGML_USE_OPENMP
499
+ diff --git a/ggml/src/ggml-cpu/tiled/tiled.cpp b/ggml/src/ggml-cpu/tiled/tiled.cpp
500
+ index 77e1a9945..06fd9a2d5 100644
501
+ --- a/ggml/src/ggml-cpu/tiled/tiled.cpp
502
+ +++ b/ggml/src/ggml-cpu/tiled/tiled.cpp
503
+ @@ -716,7 +716,9 @@ static void tiled_store_window(const float * buf, int n_src0, int n_src1, int bu
504
+ // unpack and microtile are reused unchanged from mul_mat.
505
+
506
+ // src0 rows per g group; finer than the TILED_TILE_K kernel tile so all threads stay busy at small ne01
507
+ -#define TILED_MMID_GROUP 64
508
+ +#ifndef TILED_MMID_GROUP
509
+ +#define TILED_MMID_GROUP 32
510
+ +#endif
511
+
512
+ // like tiled_store_window, but the dst columns are not contiguous: col_ptrs[j] points at the
513
+ // start of dst column j, whose rows are contiguous (dim 0, nb0 == 4 bytes).
patches/optional-profiler.patch ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ diff --git a/ggml/src/ggml-cpu/ggml-cpu.c b/ggml/src/ggml-cpu/ggml-cpu.c
2
+ index 53d39d249..02889fd24 100644
3
+ --- a/ggml/src/ggml-cpu/ggml-cpu.c
4
+ +++ b/ggml/src/ggml-cpu/ggml-cpu.c
5
+ @@ -35,6 +35,49 @@
6
+ #include <limits.h>
7
+ #include <stdarg.h>
8
+ #include <signal.h>
9
+ +
10
+ +// ---- lightweight per-op profiling (GGML_PROF=1), diagnostic only ----
11
+ +static int g_prof = -1;
12
+ +struct ggml_prof_entry { const char * op; const char * type; const char * ty0; int64_t ne0, ne1, ne2; int64_t us; int64_t calls; };
13
+ +static struct ggml_prof_entry g_prof_tab[1024];
14
+ +static int g_prof_n = 0;
15
+ +static int64_t g_prof_total = 0;
16
+ +
17
+ +static void ggml_prof_dump(void) {
18
+ + if (g_prof_n == 0) return;
19
+ + fprintf(stderr, "\n[GGML_PROF] total_us=%" PRId64 "\n", g_prof_total);
20
+ + for (int i = 0; i < g_prof_n; ++i) {
21
+ + fprintf(stderr, "[GGML_PROF] %-20s %-6s<%-6s [%6" PRId64 ",%5" PRId64 ",%4" PRId64 "] %9.2f ms %6.2f%% calls=%" PRId64 " us/call=%.1f\n",
22
+ + g_prof_tab[i].op, g_prof_tab[i].type, g_prof_tab[i].ty0, g_prof_tab[i].ne0, g_prof_tab[i].ne1, g_prof_tab[i].ne2,
23
+ + g_prof_tab[i].us/1000.0,
24
+ + 100.0*g_prof_tab[i].us/(g_prof_total?g_prof_total:1),
25
+ + g_prof_tab[i].calls,
26
+ + (double)g_prof_tab[i].us/(g_prof_tab[i].calls?g_prof_tab[i].calls:1));
27
+ + }
28
+ + fflush(stderr);
29
+ +}
30
+ +
31
+ +static void ggml_prof_add(const struct ggml_tensor * node, int64_t us) {
32
+ + g_prof_total += us;
33
+ + const char * op = ggml_op_name(node->op);
34
+ + const char * ty = ggml_type_name(node->type);
35
+ + const char * ty0 = node->src[0] ? ggml_type_name(node->src[0]->type) : "-";
36
+ + const int64_t ne0 = node->ne[0], ne1 = node->ne[1], ne2 = node->ne[2];
37
+ + for (int i = 0; i < g_prof_n; ++i) {
38
+ + if (strcmp(g_prof_tab[i].op, op) == 0 && strcmp(g_prof_tab[i].type, ty) == 0 && strcmp(g_prof_tab[i].ty0, ty0) == 0 &&
39
+ + g_prof_tab[i].ne0 == ne0 && g_prof_tab[i].ne1 == ne1 && g_prof_tab[i].ne2 == ne2) {
40
+ + g_prof_tab[i].us += us; g_prof_tab[i].calls++;
41
+ + return;
42
+ + }
43
+ + }
44
+ + if (g_prof_n < 1024) {
45
+ + g_prof_tab[g_prof_n].op = op; g_prof_tab[g_prof_n].type = ty; g_prof_tab[g_prof_n].ty0 = ty0;
46
+ + g_prof_tab[g_prof_n].ne0 = ne0; g_prof_tab[g_prof_n].ne1 = ne1; g_prof_tab[g_prof_n].ne2 = ne2;
47
+ + g_prof_tab[g_prof_n].us = us; g_prof_tab[g_prof_n].calls = 1;
48
+ + g_prof_n++;
49
+ + }
50
+ +}
51
+ +// ----------------------------------------------------------------
52
+ #if defined(__gnu_linux__)
53
+ #include <syscall.h>
54
+ #endif
55
+ @@ -3148,6 +3191,13 @@ static thread_ret_t ggml_graph_compute_thread(void * data) {
56
+ continue;
57
+ }
58
+
59
+ + if (g_prof < 0) {
60
+ + const char * e = getenv("GGML_PROF");
61
+ + g_prof = e && e[0] == '1' ? 1 : 0;
62
+ + if (g_prof) atexit(ggml_prof_dump);
63
+ + }
64
+ + const int64_t prof_t0 = (g_prof && state->ith == 0) ? ggml_time_us() : 0;
65
+ +
66
+ // TODO: move fused-op detection into ggml_graph_plan so fusion decisions are made once at planning time
67
+ // Try fused ops, fall back to normal compute
68
+ const int n_fused = ggml_cpu_try_fuse_ops(cgraph, node_n, &params, cplan);
69
+ @@ -3166,6 +3216,9 @@ static thread_ret_t ggml_graph_compute_thread(void * data) {
70
+ if (node_n + 1 < cgraph->n_nodes) {
71
+ ggml_barrier(state->threadpool);
72
+ }
73
+ + if (g_prof && state->ith == 0) {
74
+ + ggml_prof_add(node, ggml_time_us() - prof_t0);
75
+ + }
76
+ }
77
+
78
+ #ifdef GGML_USE_OPENMP