@@ -175,10 +175,15 @@ fn tensor(shape: Vec<usize>, data: Vec<f32>) -> Result<Tensor, String> {
175175 Tensor :: from_vec ( shape, data) . map_err ( |e| format ! ( "tensor build failed: {e}" ) )
176176}
177177
178- fn prepare_small ( asset_dir : & Path ) -> Result < ( ) , String > {
179- std:: fs:: create_dir_all ( asset_dir)
180- . map_err ( |e| format ! ( "create {}: {e}" , asset_dir. display( ) ) ) ?;
181- let conv = OnnxExportGraph {
178+ fn conv3x3_relu_graph (
179+ in_c : usize ,
180+ out_c : usize ,
181+ h : usize ,
182+ w : usize ,
183+ w_scale : f32 ,
184+ b_scale : f32 ,
185+ ) -> Result < OnnxExportGraph , String > {
186+ Ok ( OnnxExportGraph {
182187 nodes : vec ! [
183188 OnnxExportNode {
184189 op_type: "Conv" . to_string( ) ,
@@ -206,21 +211,85 @@ fn prepare_small(asset_dir: &Path) -> Result<(), String> {
206211 initializers : vec ! [
207212 (
208213 "weight" . to_string( ) ,
209- tensor( vec![ 8 , 3 , 3 , 3 ] , patterned( 8 * 3 * 3 * 3 , 0.01 ) ) ?,
214+ tensor(
215+ vec![ out_c, in_c, 3 , 3 ] ,
216+ patterned( out_c * in_c * 3 * 3 , w_scale) ,
217+ ) ?,
218+ ) ,
219+ (
220+ "bias" . to_string( ) ,
221+ tensor( vec![ out_c] , patterned( out_c, b_scale) ) ?,
210222 ) ,
211- ( "bias" . to_string( ) , tensor( vec![ 8 ] , patterned( 8 , 0.005 ) ) ?) ,
212223 ] ,
213224 inputs : vec ! [ OnnxExportValueInfo {
214225 name: "input" . to_string( ) ,
215- shape: vec![ 1 , 3 , 64 , 64 ] ,
226+ shape: vec![ 1 , in_c as i64 , h as i64 , w as i64 ] ,
216227 } ] ,
217228 outputs : vec ! [ OnnxExportValueInfo {
218229 name: "output" . to_string( ) ,
219- shape: vec![ 1 , 8 , 64 , 64 ] ,
230+ shape: vec![ 1 , out_c as i64 , h as i64 , w as i64 ] ,
220231 } ] ,
221232 opset_version : 13 ,
222233 int64_initializers : Vec :: new ( ) ,
223- } ;
234+ } )
235+ }
236+
237+ fn gemm_relu_graph (
238+ m : usize ,
239+ k : usize ,
240+ n : usize ,
241+ w_scale : f32 ,
242+ b_scale : f32 ,
243+ ) -> Result < OnnxExportGraph , String > {
244+ Ok ( OnnxExportGraph {
245+ nodes : vec ! [
246+ OnnxExportNode {
247+ op_type: "Gemm" . to_string( ) ,
248+ name: "gemm" . to_string( ) ,
249+ inputs: vec![
250+ "input" . to_string( ) ,
251+ "weight" . to_string( ) ,
252+ "bias" . to_string( ) ,
253+ ] ,
254+ outputs: vec![ "gemm_out" . to_string( ) ] ,
255+ attributes: vec![
256+ OnnxExportAttr :: Float ( "alpha" . to_string( ) , 1.0 ) ,
257+ OnnxExportAttr :: Float ( "beta" . to_string( ) , 1.0 ) ,
258+ OnnxExportAttr :: Int ( "transB" . to_string( ) , 1 ) ,
259+ ] ,
260+ } ,
261+ OnnxExportNode {
262+ op_type: "Relu" . to_string( ) ,
263+ name: "relu" . to_string( ) ,
264+ inputs: vec![ "gemm_out" . to_string( ) ] ,
265+ outputs: vec![ "output" . to_string( ) ] ,
266+ attributes: Vec :: new( ) ,
267+ } ,
268+ ] ,
269+ initializers : vec ! [
270+ (
271+ "weight" . to_string( ) ,
272+ tensor( vec![ n, k] , patterned( n * k, w_scale) ) ?,
273+ ) ,
274+ ( "bias" . to_string( ) , tensor( vec![ n] , patterned( n, b_scale) ) ?) ,
275+ ] ,
276+ inputs : vec ! [ OnnxExportValueInfo {
277+ name: "input" . to_string( ) ,
278+ shape: vec![ m as i64 , k as i64 ] ,
279+ } ] ,
280+ outputs : vec ! [ OnnxExportValueInfo {
281+ name: "output" . to_string( ) ,
282+ shape: vec![ m as i64 , n as i64 ] ,
283+ } ] ,
284+ opset_version : 13 ,
285+ int64_initializers : Vec :: new ( ) ,
286+ } )
287+ }
288+
289+ fn prepare_small ( asset_dir : & Path ) -> Result < ( ) , String > {
290+ std:: fs:: create_dir_all ( asset_dir)
291+ . map_err ( |e| format ! ( "create {}: {e}" , asset_dir. display( ) ) ) ?;
292+ let conv = conv3x3_relu_graph ( 3 , 8 , 64 , 64 , 0.01 , 0.005 ) ?;
224293 export_onnx_model_to_file (
225294 & conv,
226295 "yscv-pr-bench" ,
@@ -365,56 +434,51 @@ fn prepare_small(asset_dir: &Path) -> Result<(), String> {
365434 )
366435 . map_err ( |e| format ! ( "export small residual: {e}" ) ) ?;
367436
368- let gemm = OnnxExportGraph {
369- nodes : vec ! [
370- OnnxExportNode {
371- op_type: "Gemm" . to_string( ) ,
372- name: "gemm" . to_string( ) ,
373- inputs: vec![
374- "input" . to_string( ) ,
375- "weight" . to_string( ) ,
376- "bias" . to_string( ) ,
377- ] ,
378- outputs: vec![ "gemm_out" . to_string( ) ] ,
379- attributes: vec![
380- OnnxExportAttr :: Float ( "alpha" . to_string( ) , 1.0 ) ,
381- OnnxExportAttr :: Float ( "beta" . to_string( ) , 1.0 ) ,
382- OnnxExportAttr :: Int ( "transB" . to_string( ) , 1 ) ,
383- ] ,
384- } ,
385- OnnxExportNode {
386- op_type: "Relu" . to_string( ) ,
387- name: "relu" . to_string( ) ,
388- inputs: vec![ "gemm_out" . to_string( ) ] ,
389- outputs: vec![ "output" . to_string( ) ] ,
390- attributes: Vec :: new( ) ,
391- } ,
392- ] ,
393- initializers : vec ! [
394- (
395- "weight" . to_string( ) ,
396- tensor( vec![ 64 , 128 ] , patterned( 64 * 128 , 0.002 ) ) ?,
397- ) ,
398- ( "bias" . to_string( ) , tensor( vec![ 64 ] , patterned( 64 , 0.001 ) ) ?) ,
399- ] ,
400- inputs : vec ! [ OnnxExportValueInfo {
401- name: "input" . to_string( ) ,
402- shape: vec![ 1 , 128 ] ,
403- } ] ,
404- outputs : vec ! [ OnnxExportValueInfo {
405- name: "output" . to_string( ) ,
406- shape: vec![ 1 , 64 ] ,
407- } ] ,
408- opset_version : 13 ,
409- int64_initializers : Vec :: new ( ) ,
410- } ;
437+ let gemm = gemm_relu_graph ( 1 , 128 , 64 , 0.002 , 0.001 ) ?;
411438 export_onnx_model_to_file (
412439 & gemm,
413440 "yscv-pr-bench" ,
414441 "small_gemm_relu_1x128" ,
415442 & asset_dir. join ( "small-gemm-relu-1x128.onnx" ) ,
416443 )
417- . map_err ( |e| format ! ( "export small gemm: {e}" ) )
444+ . map_err ( |e| format ! ( "export small gemm: {e}" ) ) ?;
445+
446+ // Large-spatial 3×3 stride-1 conv — the dominant Winograd workload in YOLO
447+ // backbones. 80×80 output amortises the input/output transforms, so this is
448+ // where Winograd should beat im2col+GEMM and where vectorising the
449+ // transforms shows up cleanly, without the e2e noise of a full YOLO graph.
450+ let wino_spatial = conv3x3_relu_graph ( 64 , 128 , 80 , 80 , 0.004 , 0.002 ) ?;
451+ export_onnx_model_to_file (
452+ & wino_spatial,
453+ "yscv-pr-bench" ,
454+ "winograd_3x3_80x80_c64" ,
455+ & asset_dir. join ( "winograd-3x3-80x80-c64.onnx" ) ,
456+ )
457+ . map_err ( |e| format ! ( "export winograd spatial: {e}" ) ) ?;
458+
459+ // Wide-channel 3×3 stride-1 conv — few tiles, large weight transform. Isolates
460+ // the per-inference weight-transform + pack cost (256×256 across 16 tiles),
461+ // the term that currently makes Winograd lose; caching packed weights should
462+ // move this case the most.
463+ let wino_channels = conv3x3_relu_graph ( 256 , 256 , 20 , 20 , 0.002 , 0.001 ) ?;
464+ export_onnx_model_to_file (
465+ & wino_channels,
466+ "yscv-pr-bench" ,
467+ "winograd_3x3_20x20_c256" ,
468+ & asset_dir. join ( "winograd-3x3-20x20-c256.onnx" ) ,
469+ )
470+ . map_err ( |e| format ! ( "export winograd channels: {e}" ) ) ?;
471+
472+ // Medium GEMM (M=256, K=512, N=512) — exercises the blocked multi-row
473+ // (mr12/mr6) microkernels, unlike the 1×128 GEMV which only hits the m=1 path.
474+ let gemm_mid = gemm_relu_graph ( 256 , 512 , 512 , 0.002 , 0.001 ) ?;
475+ export_onnx_model_to_file (
476+ & gemm_mid,
477+ "yscv-pr-bench" ,
478+ "gemm_relu_256x512x512" ,
479+ & asset_dir. join ( "gemm-relu-256x512x512.onnx" ) ,
480+ )
481+ . map_err ( |e| format ! ( "export medium gemm: {e}" ) )
418482}
419483
420484struct XorShift ( u32 ) ;
0 commit comments