@@ -132,6 +132,153 @@ static int test_sdpa_kvcache2_invalid_past_len()
132132 return 0 ;
133133}
134134
135+ static int test_sdpa_kvcache2_persistent_buffer ()
136+ {
137+ const int embed_dim = 4 ;
138+ const int out_embed_dim = 3 ;
139+ const int past_seqlen = 2 ;
140+ const int cur_seqlen = 2 ;
141+ const int max_seqlen = 5 ;
142+
143+ ncnn::Mat query (embed_dim, cur_seqlen, 2 );
144+ query.fill (0 .01f );
145+
146+ ncnn::Mat cur_key (embed_dim, cur_seqlen, 1 );
147+ ncnn::Mat cur_value (out_embed_dim, cur_seqlen, 1 );
148+ ncnn::Mat past_key (embed_dim, max_seqlen, 1 );
149+ ncnn::Mat past_value (out_embed_dim, max_seqlen, 1 );
150+
151+ for (int y = 0 ; y < cur_seqlen; y++)
152+ {
153+ float * kptr = cur_key.row (y);
154+ for (int x = 0 ; x < embed_dim; x++)
155+ kptr[x] = 100 .f + y * 10 + x;
156+
157+ float * vptr = cur_value.row (y);
158+ for (int x = 0 ; x < out_embed_dim; x++)
159+ vptr[x] = 200 .f + y * 10 + x;
160+ }
161+
162+ for (int y = 0 ; y < max_seqlen; y++)
163+ {
164+ float * kptr = past_key.row (y);
165+ for (int x = 0 ; x < embed_dim; x++)
166+ kptr[x] = 300 .f + y * 10 + x;
167+
168+ float * vptr = past_value.row (y);
169+ for (int x = 0 ; x < out_embed_dim; x++)
170+ vptr[x] = 400 .f + y * 10 + x;
171+ }
172+
173+ ncnn::Mat past_len (1 );
174+ past_len.fill ((float )past_seqlen);
175+
176+ ncnn::ParamDict pd;
177+ pd.set (7 , 2 ); // kv_cache
178+
179+ ncnn::Layer* op = ncnn::create_layer_cpu (" SDPA" );
180+ op->load_param (pd);
181+
182+ ncnn::Option opt;
183+ opt.num_threads = 1 ;
184+
185+ op->create_pipeline (opt);
186+
187+ std::vector<ncnn::Mat> bottom_blobs (6 );
188+ bottom_blobs[0 ] = query;
189+ bottom_blobs[1 ] = cur_key;
190+ bottom_blobs[2 ] = cur_value;
191+ bottom_blobs[3 ] = past_key;
192+ bottom_blobs[4 ] = past_value;
193+ bottom_blobs[5 ] = past_len;
194+
195+ std::vector<ncnn::Mat> top_blobs (3 );
196+ int ret = op->forward (bottom_blobs, top_blobs, opt);
197+ if (ret != 0 )
198+ {
199+ fprintf (stderr, " test_sdpa_kvcache2_persistent_buffer failed forward\n " );
200+ op->destroy_pipeline (opt);
201+ delete op;
202+ return -1 ;
203+ }
204+
205+ if (top_blobs[1 ].data != past_key.data || top_blobs[2 ].data != past_value.data )
206+ {
207+ fprintf (stderr, " test_sdpa_kvcache2_persistent_buffer failed buffer identity\n " );
208+ op->destroy_pipeline (opt);
209+ delete op;
210+ return -1 ;
211+ }
212+
213+ for (int y = 0 ; y < past_seqlen; y++)
214+ {
215+ const float * kptr = past_key.row (y);
216+ for (int x = 0 ; x < embed_dim; x++)
217+ {
218+ if (kptr[x] != 300 .f + y * 10 + x)
219+ {
220+ fprintf (stderr, " test_sdpa_kvcache2_persistent_buffer clobbered past_key\n " );
221+ op->destroy_pipeline (opt);
222+ delete op;
223+ return -1 ;
224+ }
225+ }
226+
227+ const float * vptr = past_value.row (y);
228+ for (int x = 0 ; x < out_embed_dim; x++)
229+ {
230+ if (vptr[x] != 400 .f + y * 10 + x)
231+ {
232+ fprintf (stderr, " test_sdpa_kvcache2_persistent_buffer clobbered past_value\n " );
233+ op->destroy_pipeline (opt);
234+ delete op;
235+ return -1 ;
236+ }
237+ }
238+ }
239+
240+ for (int y = 0 ; y < cur_seqlen; y++)
241+ {
242+ const float * kptr = past_key.row (past_seqlen + y);
243+ for (int x = 0 ; x < embed_dim; x++)
244+ {
245+ if (kptr[x] != 100 .f + y * 10 + x)
246+ {
247+ fprintf (stderr, " test_sdpa_kvcache2_persistent_buffer failed appended key\n " );
248+ op->destroy_pipeline (opt);
249+ delete op;
250+ return -1 ;
251+ }
252+ }
253+
254+ const float * vptr = past_value.row (past_seqlen + y);
255+ for (int x = 0 ; x < out_embed_dim; x++)
256+ {
257+ if (vptr[x] != 200 .f + y * 10 + x)
258+ {
259+ fprintf (stderr, " test_sdpa_kvcache2_persistent_buffer failed appended value\n " );
260+ op->destroy_pipeline (opt);
261+ delete op;
262+ return -1 ;
263+ }
264+ }
265+ }
266+
267+ past_len.fill ((float )(max_seqlen - cur_seqlen + 1 ));
268+ ret = op->forward (bottom_blobs, top_blobs, opt);
269+ if (ret == 0 )
270+ {
271+ fprintf (stderr, " test_sdpa_kvcache2_persistent_buffer failed overflow check\n " );
272+ op->destroy_pipeline (opt);
273+ delete op;
274+ return -1 ;
275+ }
276+
277+ op->destroy_pipeline (opt);
278+ delete op;
279+ return 0 ;
280+ }
281+
135282static int test_sdpa_0 ()
136283{
137284 return 0
@@ -152,6 +299,7 @@ static int test_sdpa_0()
152299 || test_sdpa_kvcache2 (RandomMat (32 , 16 , 8 ), RandomMat (32 , 16 , 8 ), RandomMat (20 , 16 , 8 ), 0 , 0 , 32 )
153300 || test_sdpa_kvcache2 (RandomMat (64 , 17 , 12 ), RandomMat (64 , 17 , 2 ), RandomMat (64 , 17 , 2 ), 0 , 0 , 32 )
154301 || test_sdpa_kvcache2_invalid_past_len ()
302+ || test_sdpa_kvcache2_persistent_buffer ()
155303 || test_sdpa_kvcache2 (RandomMat (32 , 16 , 8 ), RandomMat (32 , 16 , 8 ), RandomMat (20 , 16 , 8 ), 1 , 0 , 32 )
156304 || test_sdpa_kvcache2 (RandomMat (64 , 17 , 12 ), RandomMat (64 , 17 , 2 ), RandomMat (64 , 17 , 2 ), 1 , 0 , 32 );
157305}
0 commit comments