HelloSun commited on
Commit
722130e
·
verified ·
1 Parent(s): f65ef17

Pipeline stage P1: shape-level profile diagnostic, flash-attn K packing once per head, build.sh submodule reset fix

Browse files
README.md CHANGED
@@ -56,6 +56,12 @@ n = 2 run(s) per row (median). Speedup = stock-Q4_K generate time / row generate
56
  - 「像素差」只對**權重相同**的列有意義。第 6 列與第 1 列完全相同,表示 FFN 融合沒有改變輸出。第 5、7 列是 AMX 的數值差異(平均差 1.28),視覺上與 stock 相同。Q4_0、Q8_0 使用不同權重,差異大是預期的,不代表品質問題。
57
  - AVX2 與 AVX512 的輸出與 stock 不同,原因是浮點運算順序不同。
58
 
 
 
 
 
 
 
59
  ## 第一階段:VAE 解碼(已完成)
60
 
61
  OpenVINO 的 VAE 解碼約 0.55 s,sd.cpp 約 10 s。第一階段嘗試了 conv-direct、VAE tiling、F16 VAE 權重與 llamafile sgemm。
 
56
  - 「像素差」只對**權重相同**的列有意義。第 6 列與第 1 列完全相同,表示 FFN 融合沒有改變輸出。第 5、7 列是 AMX 的數值差異(平均差 1.28),視覺上與 stock 相同。Q4_0、Q8_0 使用不同權重,差異大是預期的,不代表品質問題。
57
  - AVX2 與 AVX512 的輸出與 stock 不同,原因是浮點運算順序不同。
58
 
59
+ ## Pipeline 階段 P1:flash-attn K 打包(已完成,優先執行)
60
+
61
+ 先量測而非猜測:矩陣乘在 pipeline 內與微基準一致,沒有額外開銷;pipeline 的額外開銷是非矩陣乘部分(約 1.4 s/步),主要是 flash-attn。
62
+ 把 K 的轉置打包改為每個頭只做一次,FLASH_ATTN 從 1607 ms 降到 1226 ms(-24%),輸出與修改前逐像素相同。
63
+ 詳見 `stages/stage_p1_pipe/README.md`。
64
+
65
  ## 第一階段:VAE 解碼(已完成)
66
 
67
  OpenVINO 的 VAE 解碼約 0.55 s,sd.cpp 約 10 s。第一階段嘗試了 conv-direct、VAE tiling、F16 VAE 權重與 llamafile sgemm。
build.sh CHANGED
@@ -123,6 +123,8 @@ if [[ ${#SELECTED[@]} -gt 0 ]]; then
123
  git -C "$SRC" checkout -q -f "$SDCPP_REF"
124
  git -C "$SRC" reset -q --hard "$SDCPP_REF"
125
  git -C "$SRC" submodule update -q --init --recursive
 
 
126
 
127
  # Apply our patches (idempotent: skip a patch that is already applied).
128
  apply_patch() {
@@ -137,6 +139,7 @@ if [[ ${#SELECTED[@]} -gt 0 ]]; then
137
  }
138
  apply_patch "$SRC" "$ROOT/patches/sdcpp-0001-zimage-ffn-w13-and-cpu-extra-buft.patch"
139
  apply_patch "$SRC/ggml" "$ROOT/patches/ggml-0001-cpu-op-profile.patch"
 
140
 
141
  # ------------------------------------------------------------ build
142
  for v in "${SELECTED[@]}"; do
 
123
  git -C "$SRC" checkout -q -f "$SDCPP_REF"
124
  git -C "$SRC" reset -q --hard "$SDCPP_REF"
125
  git -C "$SRC" submodule update -q --init --recursive
126
+ # discard leftovers of earlier patch runs so the patches below always apply to the pinned sources
127
+ git -C "$SRC" submodule foreach -q --recursive 'git checkout -q -- . 2>/dev/null || true'
128
 
129
  # Apply our patches (idempotent: skip a patch that is already applied).
130
  apply_patch() {
 
139
  }
140
  apply_patch "$SRC" "$ROOT/patches/sdcpp-0001-zimage-ffn-w13-and-cpu-extra-buft.patch"
141
  apply_patch "$SRC/ggml" "$ROOT/patches/ggml-0001-cpu-op-profile.patch"
142
+ apply_patch "$SRC/ggml" "$ROOT/patches/ggml-0002-flash-attn-kpack.patch"
143
 
144
  # ------------------------------------------------------------ build
145
  for v in "${SELECTED[@]}"; do
patches/ggml-0001-cpu-op-profile.patch CHANGED
@@ -1,8 +1,8 @@
1
  diff --git a/src/ggml-cpu/ggml-cpu.c b/src/ggml-cpu/ggml-cpu.c
2
- index fd55589f..42a4c789 100644
3
  --- a/src/ggml-cpu/ggml-cpu.c
4
  +++ b/src/ggml-cpu/ggml-cpu.c
5
- @@ -3235,6 +3235,21 @@ struct ggml_cplan ggml_graph_plan(
6
  // Returns the number of nodes skipped by fusion (>=1), or 0 if no fusion was applied.
7
  static bool ggml_cpu_disable_fusion = false; // initialized once in ggml_cpu_init(), read-only afterwards
8
 
@@ -10,6 +10,32 @@ index fd55589f..42a4c789 100644
10
  +static bool ggml_cpu_profile_enabled = false;
11
  +static double ggml_cpu_prof_us[GGML_OP_COUNT + 1];
12
  +static int64_t ggml_cpu_prof_n[GGML_OP_COUNT + 1];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
  +static void ggml_cpu_profile_dump(void) {
14
  + double total = 0;
15
  + for (int i = 0; i <= GGML_OP_COUNT; i++) total += ggml_cpu_prof_us[i];
@@ -24,7 +50,7 @@ index fd55589f..42a4c789 100644
24
  static int ggml_cpu_try_fuse_ops(
25
  const struct ggml_cgraph * cgraph,
26
  const int node_n,
27
- @@ -3311,6 +3326,8 @@ static thread_ret_t ggml_graph_compute_thread(void * data) {
28
 
29
  // TODO: move fused-op detection into ggml_graph_plan so fusion decisions are made once at planning time
30
  // Try fused ops, fall back to normal compute
@@ -33,7 +59,7 @@ index fd55589f..42a4c789 100644
33
  const int n_fused = ggml_cpu_try_fuse_ops(cgraph, node_n, &params, cplan);
34
  if (n_fused > 0) {
35
  node_n += n_fused;
36
- @@ -3327,6 +3344,11 @@ static thread_ret_t ggml_graph_compute_thread(void * data) {
37
  if (node_n + 1 < cgraph->n_nodes) {
38
  ggml_barrier(state->threadpool);
39
  }
@@ -41,19 +67,26 @@ index fd55589f..42a4c789 100644
41
  + const int idx = n_fused > 0 ? GGML_OP_COUNT : (int) node->op;
42
  + ggml_cpu_prof_us[idx] += (double) (ggml_time_us() - prof_t0);
43
  + ggml_cpu_prof_n[idx] += 1;
 
 
 
44
  + }
45
  }
46
 
47
  #ifdef GGML_USE_OPENMP
48
- @@ -4105,6 +4127,13 @@ void ggml_cpu_init(void) {
49
  const char * env = getenv("GGML_CPU_DISABLE_FUSION");
50
  ggml_cpu_disable_fusion = (env != NULL && atoi(env) == 1);
51
  }
52
  + {
53
  + const char * env = getenv("GGML_CPU_PROFILE");
54
- + if (env != NULL && atoi(env) == 1) {
55
  + ggml_cpu_profile_enabled = true;
56
  + atexit(ggml_cpu_profile_dump);
 
 
 
 
57
  + }
58
  + }
59
 
 
1
  diff --git a/src/ggml-cpu/ggml-cpu.c b/src/ggml-cpu/ggml-cpu.c
2
+ index fd55589f..4c3575ff 100644
3
  --- a/src/ggml-cpu/ggml-cpu.c
4
  +++ b/src/ggml-cpu/ggml-cpu.c
5
+ @@ -3235,6 +3235,47 @@ struct ggml_cplan ggml_graph_plan(
6
  // Returns the number of nodes skipped by fusion (>=1), or 0 if no fusion was applied.
7
  static bool ggml_cpu_disable_fusion = false; // initialized once in ggml_cpu_init(), read-only afterwards
8
 
 
10
  +static bool ggml_cpu_profile_enabled = false;
11
  +static double ggml_cpu_prof_us[GGML_OP_COUNT + 1];
12
  +static int64_t ggml_cpu_prof_n[GGML_OP_COUNT + 1];
13
+ +// shape-level MUL_MAT profile (GGML_CPU_PROFILE=2): key = K,N,M, src0 buffer kind, src1 contiguity
14
+ +#define GGML_CPU_PROF_MM_MAX 128
15
+ +struct ggml_cpu_prof_mm_entry { int64_t k, n, m; int buf; int contig; double us; int64_t cnt; int used; };
16
+ +static struct ggml_cpu_prof_mm_entry ggml_cpu_prof_mm[GGML_CPU_PROF_MM_MAX];
17
+ +static bool ggml_cpu_profile_shapes = false;
18
+ +static void ggml_cpu_prof_mm_add(const struct ggml_tensor * t, double us) {
19
+ + const int buf = (t->src[0]->buffer && t->src[0]->buffer->buft && strcmp(ggml_backend_buft_name(t->src[0]->buffer->buft), "AMX") == 0) ? 1 :
20
+ + (t->src[0]->buffer && t->src[0]->buffer->buft && strstr(ggml_backend_buft_name(t->src[0]->buffer->buft), "REPACK")) ? 2 : 0;
21
+ + const int contig = ggml_is_contiguous(t->src[1]) ? 1 : 0;
22
+ + const int64_t k = t->src[0]->ne[0], n = t->src[0]->ne[1], m = t->src[1]->ne[1];
23
+ + for (int i = 0; i < GGML_CPU_PROF_MM_MAX; ++i) {
24
+ + struct ggml_cpu_prof_mm_entry * e = &ggml_cpu_prof_mm[i];
25
+ + if (!e->used) { e->used = 1; e->k = k; e->n = n; e->m = m; e->buf = buf; e->contig = contig; }
26
+ + if (e->k == k && e->n == n && e->m == m && e->buf == buf && e->contig == contig) { e->us += us; e->cnt++; return; }
27
+ + }
28
+ +}
29
+ +static void ggml_cpu_profile_dump_shapes(void) {
30
+ + fprintf(stderr, "PROFMM K N M buf contig calls total_ms per_call_ms\n");
31
+ + for (int i = 0; i < GGML_CPU_PROF_MM_MAX; ++i) {
32
+ + struct ggml_cpu_prof_mm_entry * e = &ggml_cpu_prof_mm[i];
33
+ + if (!e->used) continue;
34
+ + fprintf(stderr, "PROFMM %lld %lld %lld %s %d %lld %.1f %.3f\n", (long long)e->k, (long long)e->n, (long long)e->m,
35
+ + e->buf == 1 ? "AMX" : e->buf == 2 ? "REPACK" : "default", e->contig, (long long)e->cnt, e->us / 1000.0, e->us / 1000.0 / (double)e->cnt);
36
+ + }
37
+ +}
38
+ +
39
  +static void ggml_cpu_profile_dump(void) {
40
  + double total = 0;
41
  + for (int i = 0; i <= GGML_OP_COUNT; i++) total += ggml_cpu_prof_us[i];
 
50
  static int ggml_cpu_try_fuse_ops(
51
  const struct ggml_cgraph * cgraph,
52
  const int node_n,
53
+ @@ -3311,6 +3352,8 @@ static thread_ret_t ggml_graph_compute_thread(void * data) {
54
 
55
  // TODO: move fused-op detection into ggml_graph_plan so fusion decisions are made once at planning time
56
  // Try fused ops, fall back to normal compute
 
59
  const int n_fused = ggml_cpu_try_fuse_ops(cgraph, node_n, &params, cplan);
60
  if (n_fused > 0) {
61
  node_n += n_fused;
62
+ @@ -3327,6 +3370,14 @@ static thread_ret_t ggml_graph_compute_thread(void * data) {
63
  if (node_n + 1 < cgraph->n_nodes) {
64
  ggml_barrier(state->threadpool);
65
  }
 
67
  + const int idx = n_fused > 0 ? GGML_OP_COUNT : (int) node->op;
68
  + ggml_cpu_prof_us[idx] += (double) (ggml_time_us() - prof_t0);
69
  + ggml_cpu_prof_n[idx] += 1;
70
+ + if (ggml_cpu_profile_shapes && node->op == GGML_OP_MUL_MAT && n_fused == 0) {
71
+ + ggml_cpu_prof_mm_add(node, (double) (ggml_time_us() - prof_t0));
72
+ + }
73
  + }
74
  }
75
 
76
  #ifdef GGML_USE_OPENMP
77
+ @@ -4105,6 +4156,17 @@ void ggml_cpu_init(void) {
78
  const char * env = getenv("GGML_CPU_DISABLE_FUSION");
79
  ggml_cpu_disable_fusion = (env != NULL && atoi(env) == 1);
80
  }
81
  + {
82
  + const char * env = getenv("GGML_CPU_PROFILE");
83
+ + if (env != NULL && (atoi(env) == 1 || atoi(env) == 2)) {
84
  + ggml_cpu_profile_enabled = true;
85
  + atexit(ggml_cpu_profile_dump);
86
+ + if (atoi(env) == 2) {
87
+ + ggml_cpu_profile_shapes = true;
88
+ + atexit(ggml_cpu_profile_dump_shapes);
89
+ + }
90
  + }
91
  + }
92
 
patches/ggml-0002-flash-attn-kpack.patch ADDED
@@ -0,0 +1,97 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ diff --git a/src/ggml-cpu/ops.cpp b/src/ggml-cpu/ops.cpp
2
+ index e8ef14f2..8a155984 100644
3
+ --- a/src/ggml-cpu/ops.cpp
4
+ +++ b/src/ggml-cpu/ops.cpp
5
+ @@ -9238,10 +9238,20 @@ static void ggml_compute_forward_flash_attn_ext_f16_one_chunk(
6
+ }
7
+ }
8
+
9
+ +// Stage P1 (pipeline): per-thread cache of the transposed, KV-tile-packed K for the current head.
10
+ +// The tiled kernel used to re-pack K for every (Q tile, KV tile) pair; now each head is packed once per
11
+ +// thread and reused by all Q tiles of that head. The cache is reset at every call of the tiled kernel,
12
+ +// so it never survives a graph execution (buffers are reused across graphs and their contents change).
13
+ +struct fa_kpack_key { const void * data; int64_t ik2, ik3, nek1, nbk1, nbk2, nbk3; };
14
+ +static thread_local std::vector<float> fa_kpack_buf;
15
+ +static thread_local fa_kpack_key fa_kpack_cur;
16
+ +static thread_local bool fa_kpack_valid = false;
17
+ +
18
+ static void ggml_compute_forward_flash_attn_ext_tiled(
19
+ const ggml_compute_params * params,
20
+ ggml_tensor * dst,
21
+ int ir0, int ir1) {
22
+ + fa_kpack_valid = false; // new chunk: K contents may differ from the previous call
23
+ const ggml_tensor * q = dst->src[0];
24
+ const ggml_tensor * k = dst->src[1];
25
+ const ggml_tensor * v = dst->src[2];
26
+ @@ -9381,6 +9391,43 @@ static void ggml_compute_forward_flash_attn_ext_tiled(
27
+ memset(K_f32, 0, DK * KV_TILE_SZ * sizeof(float));
28
+ memset(V32, 0, KV_TILE_SZ * DV * sizeof(float));
29
+
30
+ + // packed K for this head: [n_kv_tiles][DK][KV_TILE_SZ], zero-padded last tile
31
+ + const float * kpack = nullptr;
32
+ + {
33
+ + const fa_kpack_key key{ k->data, ik2, ik3, nek1, nbk1, nbk2, nbk3 };
34
+ + const int64_t n_kv_tiles = (nek1 + KV_TILE_SZ - 1) / KV_TILE_SZ;
35
+ + const size_t need = (size_t) (n_kv_tiles * DK * KV_TILE_SZ);
36
+ + if (!fa_kpack_valid || memcmp(&key, &fa_kpack_cur, sizeof(key)) != 0 || fa_kpack_buf.size() < need) {
37
+ + if (fa_kpack_buf.size() < need) {
38
+ + fa_kpack_buf.resize(need);
39
+ + }
40
+ + float * dst_pack = fa_kpack_buf.data();
41
+ + for (int64_t t = 0; t < n_kv_tiles; t++) {
42
+ + float * tile = dst_pack + t * DK * KV_TILE_SZ;
43
+ + const int64_t base_k = t * KV_TILE_SZ;
44
+ + const int kv_tile_n = (int) std::min((int64_t)KV_TILE_SZ, nek1 - base_k);
45
+ + memset(tile, 0, DK * KV_TILE_SZ * sizeof(float));
46
+ + for (int tk = 0; tk < kv_tile_n; tk++) {
47
+ + const char * k_data = (const char *)k->data + (base_k + tk)*nbk1 + ik2*nbk2 + ik3*nbk3;
48
+ + if (kv_type == GGML_TYPE_F16) {
49
+ + const ggml_fp16_t * k_f16 = (const ggml_fp16_t *)k_data;
50
+ + for (int64_t dk = 0; dk < DK; dk++) {
51
+ + tile[dk * KV_TILE_SZ + tk] = GGML_CPU_FP16_TO_FP32(k_f16[dk]);
52
+ + }
53
+ + } else {
54
+ + const float * k_f32_src = (const float *)k_data;
55
+ + for (int64_t dk = 0; dk < DK; dk++) {
56
+ + tile[dk * KV_TILE_SZ + tk] = k_f32_src[dk];
57
+ + }
58
+ + }
59
+ + }
60
+ + }
61
+ + fa_kpack_cur = key;
62
+ + fa_kpack_valid = true;
63
+ + }
64
+ + kpack = fa_kpack_buf.data();
65
+ + }
66
+ +
67
+ for (int64_t ic = 0; ic < nek1; ic += KV_TILE_SZ) {
68
+ const int kv_tile = (int)std::min((int64_t)KV_TILE_SZ, nek1 - ic);
69
+
70
+ @@ -9406,24 +9453,10 @@ static void ggml_compute_forward_flash_attn_ext_tiled(
71
+ }
72
+ }
73
+
74
+ - // Pack K tile transposed: K_f32[dk][kv] so KV_TILE is contiguous (SIMD dim)
75
+ - // Zero-pad the last tile so the GEMM always operates on KV_TILE_SZ columns
76
+ - for (int tk = 0; tk < kv_tile; tk++) {
77
+ - const char * k_data = (const char *)k->data + (ic + tk)*nbk1 + ik2*nbk2 + ik3*nbk3;
78
+ - if (kv_type == GGML_TYPE_F16) {
79
+ - const ggml_fp16_t * k_f16 = (const ggml_fp16_t *)k_data;
80
+ - for (int64_t dk = 0; dk < DK; dk++) {
81
+ - K_f32[dk * KV_TILE_SZ + tk] = GGML_CPU_FP16_TO_FP32(k_f16[dk]);
82
+ - }
83
+ - } else {
84
+ - const float * k_f32_src = (const float *)k_data;
85
+ - for (int64_t dk = 0; dk < DK; dk++) {
86
+ - K_f32[dk * KV_TILE_SZ + tk] = k_f32_src[dk];
87
+ - }
88
+ - }
89
+ - }
90
+ + // K tile (transposed, KV_TILE contiguous) comes from the per-head packed buffer (see above)
91
+ + const float * K_tile = kpack + (ic / KV_TILE_SZ) * DK * KV_TILE_SZ;
92
+ memset(KQ, 0, Q_TILE_SZ * KV_TILE_SZ * sizeof(float));
93
+ - simd_gemm(KQ, (const float *)Q_q, K_f32, Q_TILE_SZ, DK, KV_TILE_SZ);
94
+ + simd_gemm(KQ, (const float *)Q_q, K_tile, Q_TILE_SZ, DK, KV_TILE_SZ);
95
+ ggml_vec_scale_f32(Q_TILE_SZ * KV_TILE_SZ, KQ, scale);
96
+
97
+ if (logit_softcap != 0.0f) {
stages/stage_p1_pipe/README.md ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Stage P1 — pipeline overhead (sampling, non-matmul ops)
2
+
3
+ Order of work: this stage first, as requested (pipeline before VAE).
4
+
5
+ ## 1. Diagnosis (`GGML_CPU_PROFILE=2`, shape-level MUL_MAT profile, 2 steps, llamafile build)
6
+
7
+ Per-step numbers (2-step run divided by 2):
8
+
9
+ | part | per step | notes |
10
+ |---|---|---|
11
+ | main-layer matmuls (M=1088, AMX) | ~3.25 s | w13 48.6 ms, qkv 25.7 ms, w2 24.5 ms, out 9.3 ms per call |
12
+ | noise-refiner matmuls (M=1024, AMX) | ~0.33 s | w13 is 81 ms per call here vs 49 ms for the main layers (unexplained) |
13
+ | `FLASH_ATTN_EXT` | ~0.8 s | F32 tiled kernel |
14
+ | other element-wise ops | ~0.6 s | RMS_NORM+MUL, ADD, SCALE, MUL, CPY, CONT, ROPE |
15
+
16
+ Finding: the matmul kernel shows **no pipeline overhead** (in-pipeline per-call times match the standalone
17
+ micro-benchmark, `kbench/bench_mm.cpp`). The pipeline overhead is the non-matmul part (~1.4 s/step): mainly flash-attn.
18
+ An earlier attribution of the 1.4 s/step to matmul was wrong; the AMX buffer-name bug in the diagnostic
19
+ (`CPU_AMX` vs `AMX`) is fixed in the uploaded patch.
20
+
21
+ ## 2. Fix P1: flash-attn K packing hoisted to once per head (`patches/ggml-0002-flash-attn-kpack.patch`)
22
+
23
+ The tiled kernel re-packed K (transposed, KV-tile layout) for every (Q tile, KV tile) pair, i.e. ~17x per head.
24
+ Now each thread packs the whole head once and reuses it for all Q tiles of that head. The cache is reset at each
25
+ chunk call so it never survives a graph execution (scratch buffers are reused and their contents change).
26
+
27
+ | run (2 steps, same config) | FLASH_ATTN_EXT | sampling |
28
+ |---|---|---|
29
+ | before P1 (`p1 before`, `diag_shape_*`) | 1607.5 ms | 9.89 s |
30
+ | after P1 (`p1_profile_2step.txt`) | 1225.9 ms (-24%) | 9.33 s |
31
+ | after P1, rebuilt from scratch with `build.sh` (`p1_verify_*`) | 1336.2 ms | 11.53 s (host load was high: other tenants at 100% CPU) |
32
+
33
+ Output: pixel-identical to the pre-P1 build (diff mean 0, max 0), both for the same-tree build and the
34
+ from-scratch `build.sh` build.
35
+
36
+ Timing caveat: the host is shared (load average ~40). Compare the FLASH_ATTN_EXT profile numbers within a
37
+ batch, not the wall-clock sampling time across batches.
38
+
39
+ ## 3. Build fix found while verifying
40
+
41
+ `build.sh` now resets local changes inside the ggml submodule before applying patches. Without this, a
42
+ second run on the same work dir failed with "patch does not apply" (`git submodule update` does not revert a
43
+ submodule whose commit did not change).
44
+
45
+ ## Not done yet (next steps of stage P1)
46
+
47
+ - V tile copy and softmax are still per (Q tile, KV tile); hoisting V packing is the next small step.
48
+ - The F32 tiled kernel reaches only ~0.5 TFLOPS on this machine; a bf16 AMX attention would be the larger change.
49
+ - Element-wise fusion (modulate `x + x*scale`, gated residuals) — candidate for the next step of P1.
50
+ - Noise-refiner w13 is ~1.7x slower per call than the main-layer w13 at the same shape; not explained yet.
stages/stage_p1_pipe/diag_shape_profile_2step.txt ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ PROFMM K N M buf contig calls total_ms per_call_ms
2
+ PROFMM 2560 4096 40 default 1 35 69.5 1.986
3
+ PROFMM 2560 1024 40 default 1 70 127.5 1.821
4
+ PROFMM 4096 2560 40 default 1 35 62.7 1.791
5
+ PROFMM 2560 9728 40 default 1 70 168.6 2.409
6
+ PROFMM 9728 2560 40 default 1 35 92.4 2.639
7
+ PROFMM 2560 3840 40 default 1 2 9.7 4.846
8
+ PROFMM 3840 11520 64 AMX 1 4 6.9 1.736
9
+ PROFMM 3840 3840 64 AMX 1 4 2.6 0.656
10
+ PROFMM 3840 20480 64 AMX 1 4 19.0 4.761
11
+ PROFMM 10240 3840 64 AMX 1 4 6.0 1.512
12
+ PROFMM 64 3840 1024 default 1 2 10.3 5.139
13
+ PROFMM 256 1024 1 default 1 2 0.0 0.013
14
+ PROFMM 1024 256 1 default 1 2 0.0 0.011
15
+ PROFMM 256 15360 1 AMX 1 64 19.0 0.297
16
+ PROFMM 3840 11520 1024 AMX 1 4 108.3 27.070
17
+ PROFMM 3840 3840 1024 AMX 1 4 35.6 8.903
18
+ PROFMM 3840 20480 1024 AMX 1 4 324.4 81.093
19
+ PROFMM 10240 3840 1024 AMX 1 4 96.9 24.232
20
+ PROFMM 3840 11520 1088 AMX 1 60 1539.3 25.656
21
+ PROFMM 3840 3840 1088 AMX 1 60 558.0 9.300
22
+ PROFMM 3840 20480 1088 AMX 1 60 2918.1 48.636
23
+ PROFMM 10240 3840 1088 AMX 1 60 1470.4 24.506
24
+ PROFMM 256 3840 1 default 1 2 0.1 0.043
25
+ PROFMM 3840 64 1088 default 1 2 1.6 0.789
26
+ PROF ADD n= 496 total_ms= 199.2 pct= 1.36
27
+ PROF MUL n= 323 total_ms= 137.2 pct= 0.93
28
+ PROF REPEAT n= 2 total_ms= 0.1 pct= 0.00
29
+ PROF CONCAT n= 4 total_ms= 8.2 pct= 0.06
30
+ PROF NORM n= 2 total_ms= 1.1 pct= 0.01
31
+ PROF GROUP_NORM n= 30 total_ms= 65.7 pct= 0.45
32
+ PROF MUL_MAT n= 593 total_ms= 7646.9 pct= 52.06
33
+ PROF SCALE n= 480 total_ms= 137.2 pct= 0.93
34
+ PROF CPY n= 243 total_ms= 115.0 pct= 0.78
35
+ PROF CONT n= 418 total_ms= 101.8 pct= 0.69
36
+ PROF GET_ROWS n= 1 total_ms= 5.7 pct= 0.04
37
+ PROF ROPE n= 70 total_ms= 64.2 pct= 0.44
38
+ PROF CONV_2D n= 39 total_ms= 3960.7 pct= 26.96
39
+ PROF UPSCALE n= 3 total_ms= 28.9 pct= 0.20
40
+ PROF TIMESTEP_EMBEDDING n= 2 total_ms= 0.0 pct= 0.00
41
+ PROF FLASH_ATTN_EXT n= 104 total_ms= 1607.5 pct= 10.94
42
+ PROF UNARY n= 196 total_ms= 66.8 pct= 0.45
43
+ PROF GLU n= 68 total_ms= 78.0 pct= 0.53
44
+ PROF ROPE_APPLY n= 136 total_ms= 86.9 pct= 0.59
45
+ PROF FUSED_RMS_NORM_MUL n= 550 total_ms= 378.0 pct= 2.57
46
+ PROF TOTAL_ms=14688.9
stages/stage_p1_pipe/diag_shape_run.log ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0
  |### | 21/386 - 7.81MB/s
1
  |##################################################| 386/386 - 10.61GB/s
 
 
 
 
 
2
  |##### | 36/418 - 247.31MB/s
3
  |########################### | 224/418 - 7.89GB/s
4
  |##################################################| 418/418 - 8.16GB/s
 
 
5
  |=========================> | 1/2 - 5.45s/it
6
  |==================================================| 2/2 - 4.43s/it
 
 
 
 
 
7
  |#################### | 55/138 - 465.83MB/s
8
  |##################################################| 138/138 - 940.30MB/s
 
 
 
 
 
 
 
1
+ [I] loading diffusion model from 'models/z_image_turbo-Q4_K-ffn13.gguf' --- diffusion_engine.cpp:692
2
+ [I] load models/z_image_turbo-Q4_K-ffn13.gguf using gguf format --- model_loader.cpp:216
3
+ [I] loading llm from 'models/Qwen3-4B-Instruct-2507-Q4_K_M.gguf' --- diffusion_engine.cpp:749
4
+ [I] load models/Qwen3-4B-Instruct-2507-Q4_K_M.gguf using gguf format --- model_loader.cpp:216
5
+ [I] loading vae from 'models/ae.safetensors' --- diffusion_engine.cpp:763
6
+ [I] load models/ae.safetensors using safetensors format --- model_loader.cpp:222
7
+ [I] Version: Z-Image --- diffusion_engine.cpp:975
8
+ [W] auto-fit: no GPU devices; using the default backend --- backend_fit.cpp:444
9
+ [I] Weight type stat: f32: 640 | q8_0: 18 | q4_K: 366 | q6_K: 37 --- diffusion_engine.cpp:1548
10
+ [I] Conditioner weight type stat: f32: 145 | q4_K: 216 | q6_K: 37 --- diffusion_engine.cpp:1549
11
+ [I] Diffusion model weight type stat: f32: 251 | q8_0: 18 | q4_K: 150 --- diffusion_engine.cpp:1550
12
+ [I] VAE weight type stat: f32: 244 --- diffusion_engine.cpp:1551
13
+ [I] using VAE for encoding / decoding --- model_builders.cpp:210
14
+ [I] vae decoder: ch = 128 --- auto_encoder_kl.hpp:529
15
+ [I] flash attention auto: CPU -> on (CPU) --- flash_attn.cpp:66
16
+ [I] conv2d direct auto: CPU -> off (no implicit GEMM path) --- conv_direct.cpp:90
17
+ [I] conv3d direct auto: CPU -> off (no implicit GEMM path) --- conv_direct.cpp:90
18
+ [I] total params memory size = 6221.12MB (VRAM 0.00MB, RAM 6221.12MB): text_encoders 2375.91MB(RAM), diffusion_model 3685.21MB(RAM), vae 160.00MB(RAM), controlnet 0.00MB(N/A), extensions 0.00MB(N/A) --- diffusion_engine.cpp:1423
19
+ [I] running in FLOW mode --- diffusion_engine.cpp:1478
20
+ [I] generate_image 512x512 --- image.cpp:869
21
+ [I] get_sigmas with Simple scheduler --- denoiser.hpp:1157
22
+ [I] sampling using Euler method --- request.cpp:477
23
+ [I] qwen3 build cached graph cut plan done (taking 2 ms) --- ggml_graph_cut.cpp:1032
24
+
25
  |### | 21/386 - 7.81MB/s
26
  |##################################################| 386/386 - 10.61GB/s
27
+ [I] loading tensors completed, taking 0.21s (read: 0.12s, memcpy: 0.00s, convert: 0.00s, copy_to_backend: 0.00s) --- model_loader.cpp:1407
28
+ [I] get_learned_condition completed, taking 1.22s --- image.cpp:580
29
+ [I] generating image: 1/1 - seed 42 --- image.cpp:920
30
+ [I] z_image build cached graph cut plan done (taking 2 ms) --- ggml_graph_cut.cpp:1032
31
+
32
  |##### | 36/418 - 247.31MB/s
33
  |########################### | 224/418 - 7.89GB/s
34
  |##################################################| 418/418 - 8.16GB/s
35
+ [I] loading tensors completed, taking 0.44s (read: 0.04s, memcpy: 0.00s, convert: 0.00s, copy_to_backend: 0.31s) --- model_loader.cpp:1407
36
+
37
  |=========================> | 1/2 - 5.45s/it
38
  |==================================================| 2/2 - 4.43s/it
39
+ [I] sampling completed, taking 9.89s --- image.cpp:953
40
+ [I] generating 1 latent images completed, taking 9.89s --- image.cpp:965
41
+ [I] decoding 1 latents --- image.cpp:605
42
+ [I] vae build cached graph cut plan done (taking 0 ms) --- ggml_graph_cut.cpp:1032
43
+
44
  |#################### | 55/138 - 465.83MB/s
45
  |##################################################| 138/138 - 940.30MB/s
46
+ [I] loading tensors completed, taking 0.20s (read: 0.00s, memcpy: 0.00s, convert: 0.01s, copy_to_backend: 0.00s) --- model_loader.cpp:1407
47
+ [I] latent 1 decoded, taking 4.55s --- image.cpp:671
48
+ [I] decode_first_stage completed, taking 4.55s --- image.cpp:675
49
+ [I] generate_image completed in 15.67s --- image.cpp:1140
50
+ [I] save result image 0 to 'pipe_diag/out.png' (success) --- main.cpp:495
51
+ [I] 1/1 images saved --- main.cpp:571
stages/stage_p1_pipe/p1_profile_2step.txt ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ PROF ADD n= 496 total_ms= 195.2 pct= 1.41
2
+ PROF MUL n= 323 total_ms= 98.6 pct= 0.71
3
+ PROF REPEAT n= 2 total_ms= 0.1 pct= 0.00
4
+ PROF CONCAT n= 4 total_ms= 7.1 pct= 0.05
5
+ PROF NORM n= 2 total_ms= 1.1 pct= 0.01
6
+ PROF GROUP_NORM n= 30 total_ms= 82.2 pct= 0.59
7
+ PROF MUL_MAT n= 593 total_ms= 7185.6 pct= 51.72
8
+ PROF SCALE n= 480 total_ms= 99.2 pct= 0.71
9
+ PROF CPY n= 243 total_ms= 30.2 pct= 0.22
10
+ PROF CONT n= 418 total_ms= 49.2 pct= 0.35
11
+ PROF GET_ROWS n= 1 total_ms= 4.2 pct= 0.03
12
+ PROF ROPE n= 70 total_ms= 9.5 pct= 0.07
13
+ PROF CONV_2D n= 39 total_ms= 4481.8 pct= 32.26
14
+ PROF UPSCALE n= 3 total_ms= 15.9 pct= 0.11
15
+ PROF TIMESTEP_EMBEDDING n= 2 total_ms= 0.0 pct= 0.00
16
+ PROF FLASH_ATTN_EXT n= 104 total_ms= 1225.9 pct= 8.82
17
+ PROF UNARY n= 196 total_ms= 27.7 pct= 0.20
18
+ PROF GLU n= 68 total_ms= 57.3 pct= 0.41
19
+ PROF ROPE_APPLY n= 136 total_ms= 70.3 pct= 0.51
20
+ PROF FUSED_RMS_NORM_MUL n= 550 total_ms= 252.4 pct= 1.82
21
+ PROF TOTAL_ms=13893.4
stages/stage_p1_pipe/p1_run.log ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0
  |### | 20/386 - 15.62MB/s
1
  |##################################################| 386/386 - 9.66GB/s
 
 
 
 
 
2
  |##### | 37/418 - 130.98MB/s
3
  |########################## | 210/418 - 7.32GB/s
4
  |##################################################| 418/418 - 7.87GB/s
 
 
5
  |=========================> | 1/2 - 4.79s/it
6
  |==================================================| 2/2 - 4.53s/it
 
 
 
 
 
7
  |#################### | 54/138 - 465.83MB/s
8
  |##################################################| 138/138 - 935.65MB/s
 
 
 
 
 
 
 
1
+ [I] loading diffusion model from 'models/z_image_turbo-Q4_K-ffn13.gguf' --- diffusion_engine.cpp:692
2
+ [I] load models/z_image_turbo-Q4_K-ffn13.gguf using gguf format --- model_loader.cpp:216
3
+ [I] loading llm from 'models/Qwen3-4B-Instruct-2507-Q4_K_M.gguf' --- diffusion_engine.cpp:749
4
+ [I] load models/Qwen3-4B-Instruct-2507-Q4_K_M.gguf using gguf format --- model_loader.cpp:216
5
+ [I] loading vae from 'models/ae.safetensors' --- diffusion_engine.cpp:763
6
+ [I] load models/ae.safetensors using safetensors format --- model_loader.cpp:222
7
+ [I] Version: Z-Image --- diffusion_engine.cpp:975
8
+ [W] auto-fit: no GPU devices; using the default backend --- backend_fit.cpp:444
9
+ [I] Weight type stat: f32: 640 | q8_0: 18 | q4_K: 366 | q6_K: 37 --- diffusion_engine.cpp:1548
10
+ [I] Conditioner weight type stat: f32: 145 | q4_K: 216 | q6_K: 37 --- diffusion_engine.cpp:1549
11
+ [I] Diffusion model weight type stat: f32: 251 | q8_0: 18 | q4_K: 150 --- diffusion_engine.cpp:1550
12
+ [I] VAE weight type stat: f32: 244 --- diffusion_engine.cpp:1551
13
+ [I] using VAE for encoding / decoding --- model_builders.cpp:210
14
+ [I] vae decoder: ch = 128 --- auto_encoder_kl.hpp:529
15
+ [I] flash attention auto: CPU -> on (CPU) --- flash_attn.cpp:66
16
+ [I] conv2d direct auto: CPU -> off (no implicit GEMM path) --- conv_direct.cpp:90
17
+ [I] conv3d direct auto: CPU -> off (no implicit GEMM path) --- conv_direct.cpp:90
18
+ [I] total params memory size = 6221.12MB (VRAM 0.00MB, RAM 6221.12MB): text_encoders 2375.91MB(RAM), diffusion_model 3685.21MB(RAM), vae 160.00MB(RAM), controlnet 0.00MB(N/A), extensions 0.00MB(N/A) --- diffusion_engine.cpp:1423
19
+ [I] running in FLOW mode --- diffusion_engine.cpp:1478
20
+ [I] generate_image 512x512 --- image.cpp:869
21
+ [I] get_sigmas with Simple scheduler --- denoiser.hpp:1157
22
+ [I] sampling using Euler method --- request.cpp:477
23
+ [I] qwen3 build cached graph cut plan done (taking 2 ms) --- ggml_graph_cut.cpp:1032
24
+
25
  |### | 20/386 - 15.62MB/s
26
  |##################################################| 386/386 - 9.66GB/s
27
+ [I] loading tensors completed, taking 0.23s (read: 0.13s, memcpy: 0.00s, convert: 0.00s, copy_to_backend: 0.00s) --- model_loader.cpp:1407
28
+ [I] get_learned_condition completed, taking 0.49s --- image.cpp:580
29
+ [I] generating image: 1/1 - seed 42 --- image.cpp:920
30
+ [I] z_image build cached graph cut plan done (taking 2 ms) --- ggml_graph_cut.cpp:1032
31
+
32
  |##### | 37/418 - 130.98MB/s
33
  |########################## | 210/418 - 7.32GB/s
34
  |##################################################| 418/418 - 7.87GB/s
35
+ [I] loading tensors completed, taking 0.46s (read: 0.04s, memcpy: 0.00s, convert: 0.00s, copy_to_backend: 0.31s) --- model_loader.cpp:1407
36
+
37
  |=========================> | 1/2 - 4.79s/it
38
  |==================================================| 2/2 - 4.53s/it
39
+ [I] sampling completed, taking 9.33s --- image.cpp:953
40
+ [I] generating 1 latent images completed, taking 9.34s --- image.cpp:965
41
+ [I] decoding 1 latents --- image.cpp:605
42
+ [I] vae build cached graph cut plan done (taking 0 ms) --- ggml_graph_cut.cpp:1032
43
+
44
  |#################### | 54/138 - 465.83MB/s
45
  |##################################################| 138/138 - 935.65MB/s
46
+ [I] loading tensors completed, taking 0.20s (read: 0.00s, memcpy: 0.00s, convert: 0.01s, copy_to_backend: 0.00s) --- model_loader.cpp:1407
47
+ [I] latent 1 decoded, taking 5.07s --- image.cpp:671
48
+ [I] decode_first_stage completed, taking 5.07s --- image.cpp:675
49
+ [I] generate_image completed in 14.89s --- image.cpp:1140
50
+ [I] save result image 0 to 'pipe_p1/out.png' (success) --- main.cpp:495
51
+ [I] 1/1 images saved --- main.cpp:571
stages/stage_p1_pipe/p1_verify_profile_2step.txt ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ PROF ADD n= 496 total_ms= 215.4 pct= 1.33
2
+ PROF MUL n= 323 total_ms= 155.4 pct= 0.96
3
+ PROF REPEAT n= 2 total_ms= 0.1 pct= 0.00
4
+ PROF CONCAT n= 4 total_ms= 7.2 pct= 0.04
5
+ PROF NORM n= 2 total_ms= 1.2 pct= 0.01
6
+ PROF GROUP_NORM n= 30 total_ms= 88.4 pct= 0.55
7
+ PROF MUL_MAT n= 593 total_ms= 8765.4 pct= 54.04
8
+ PROF SCALE n= 480 total_ms= 223.9 pct= 1.38
9
+ PROF CPY n= 243 total_ms= 68.0 pct= 0.42
10
+ PROF CONT n= 418 total_ms= 89.7 pct= 0.55
11
+ PROF GET_ROWS n= 1 total_ms= 0.1 pct= 0.00
12
+ PROF ROPE n= 70 total_ms= 0.8 pct= 0.01
13
+ PROF CONV_2D n= 39 total_ms= 4632.0 pct= 28.56
14
+ PROF UPSCALE n= 3 total_ms= 14.6 pct= 0.09
15
+ PROF TIMESTEP_EMBEDDING n= 2 total_ms= 0.0 pct= 0.00
16
+ PROF FLASH_ATTN_EXT n= 104 total_ms= 1336.2 pct= 8.24
17
+ PROF UNARY n= 196 total_ms= 42.9 pct= 0.26
18
+ PROF GLU n= 68 total_ms= 81.3 pct= 0.50
19
+ PROF ROPE_APPLY n= 136 total_ms= 138.0 pct= 0.85
20
+ PROF FUSED_RMS_NORM_MUL n= 550 total_ms= 359.3 pct= 2.22
21
+ PROF TOTAL_ms=16219.9
stages/stage_p1_pipe/p1_verify_run.log ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0
  |### | 22/386 - 31.25MB/s
1
  |##################################################| 386/386 - 10.23GB/s
 
 
 
 
 
2
  |##### | 37/418 - 261.96MB/s
3
  |######################## | 198/418 - 6.75GB/s
4
  |##################################################| 418/418 - 8.03GB/s
 
 
5
  |=========================> | 1/2 - 6.01s/it
6
  |==================================================| 2/2 - 5.51s/it
 
 
 
 
 
7
  |################### | 52/138 - 340.34MB/s
8
  |##################################################| 138/138 - 940.30MB/s
 
 
 
 
 
 
 
1
+ [I] loading diffusion model from 'models/z_image_turbo-Q4_K-ffn13.gguf' --- diffusion_engine.cpp:692
2
+ [I] load models/z_image_turbo-Q4_K-ffn13.gguf using gguf format --- model_loader.cpp:216
3
+ [I] loading llm from 'models/Qwen3-4B-Instruct-2507-Q4_K_M.gguf' --- diffusion_engine.cpp:749
4
+ [I] load models/Qwen3-4B-Instruct-2507-Q4_K_M.gguf using gguf format --- model_loader.cpp:216
5
+ [I] loading vae from 'models/ae.safetensors' --- diffusion_engine.cpp:763
6
+ [I] load models/ae.safetensors using safetensors format --- model_loader.cpp:222
7
+ [I] Version: Z-Image --- diffusion_engine.cpp:975
8
+ [W] auto-fit: no GPU devices; using the default backend --- backend_fit.cpp:444
9
+ [I] Weight type stat: f32: 640 | q8_0: 18 | q4_K: 366 | q6_K: 37 --- diffusion_engine.cpp:1548
10
+ [I] Conditioner weight type stat: f32: 145 | q4_K: 216 | q6_K: 37 --- diffusion_engine.cpp:1549
11
+ [I] Diffusion model weight type stat: f32: 251 | q8_0: 18 | q4_K: 150 --- diffusion_engine.cpp:1550
12
+ [I] VAE weight type stat: f32: 244 --- diffusion_engine.cpp:1551
13
+ [I] using VAE for encoding / decoding --- model_builders.cpp:210
14
+ [I] vae decoder: ch = 128 --- auto_encoder_kl.hpp:529
15
+ [I] flash attention auto: CPU -> on (CPU) --- flash_attn.cpp:66
16
+ [I] conv2d direct auto: CPU -> off (no implicit GEMM path) --- conv_direct.cpp:90
17
+ [I] conv3d direct auto: CPU -> off (no implicit GEMM path) --- conv_direct.cpp:90
18
+ [I] total params memory size = 6221.12MB (VRAM 0.00MB, RAM 6221.12MB): text_encoders 2375.91MB(RAM), diffusion_model 3685.21MB(RAM), vae 160.00MB(RAM), controlnet 0.00MB(N/A), extensions 0.00MB(N/A) --- diffusion_engine.cpp:1423
19
+ [I] running in FLOW mode --- diffusion_engine.cpp:1478
20
+ [I] generate_image 512x512 --- image.cpp:869
21
+ [I] get_sigmas with Simple scheduler --- denoiser.hpp:1157
22
+ [I] sampling using Euler method --- request.cpp:477
23
+ [I] qwen3 build cached graph cut plan done (taking 1 ms) --- ggml_graph_cut.cpp:1032
24
+
25
  |### | 22/386 - 31.25MB/s
26
  |##################################################| 386/386 - 10.23GB/s
27
+ [I] loading tensors completed, taking 0.22s (read: 0.13s, memcpy: 0.00s, convert: 0.00s, copy_to_backend: 0.00s) --- model_loader.cpp:1407
28
+ [I] get_learned_condition completed, taking 0.41s --- image.cpp:580
29
+ [I] generating image: 1/1 - seed 42 --- image.cpp:920
30
+ [I] z_image build cached graph cut plan done (taking 2 ms) --- ggml_graph_cut.cpp:1032
31
+
32
  |##### | 37/418 - 261.96MB/s
33
  |######################## | 198/418 - 6.75GB/s
34
  |##################################################| 418/418 - 8.03GB/s
35
+ [I] loading tensors completed, taking 0.45s (read: 0.05s, memcpy: 0.00s, convert: 0.00s, copy_to_backend: 0.30s) --- model_loader.cpp:1407
36
+
37
  |=========================> | 1/2 - 6.01s/it
38
  |==================================================| 2/2 - 5.51s/it
39
+ [I] sampling completed, taking 11.53s --- image.cpp:953
40
+ [I] generating 1 latent images completed, taking 11.53s --- image.cpp:965
41
+ [I] decoding 1 latents --- image.cpp:605
42
+ [I] vae build cached graph cut plan done (taking 0 ms) --- ggml_graph_cut.cpp:1032
43
+
44
  |################### | 52/138 - 340.34MB/s
45
  |##################################################| 138/138 - 940.30MB/s
46
+ [I] loading tensors completed, taking 0.20s (read: 0.00s, memcpy: 0.00s, convert: 0.01s, copy_to_backend: 0.00s) --- model_loader.cpp:1407
47
+ [I] latent 1 decoded, taking 5.25s --- image.cpp:671
48
+ [I] decode_first_stage completed, taking 5.25s --- image.cpp:675
49
+ [I] generate_image completed in 17.20s --- image.cpp:1140
50
+ [I] save result image 0 to 'pipe_p1_verify/out.png' (success) --- main.cpp:495
51
+ [I] 1/1 images saved --- main.cpp:571