1- // Copyright 2026 Tencent
1+ // Copyright 2025 Tencent
22// SPDX-License-Identifier: BSD-3-Clause
33
44#include " topk.h"
55
6- #include < algorithm>
76#include < stdint.h>
87#include < string.h>
8+
9+ #if NCNN_SIMPLESTL
10+ #include " simplestl.h"
11+ #else
12+ #include < algorithm>
913#include < vector>
14+ #endif
1015
1116#if __ARM_NEON
1217#include < arm_neon.h>
@@ -185,6 +190,21 @@ int TopK::forward(const std::vector<Mat>& bottom_blobs, std::vector<Mat>& top_bl
185190
186191 const int total_lines = outer * inner;
187192
193+ // ncnn 3-/4-D mats have a channel stride (cstep) that may be larger than w*h
194+ // due to alignment padding. The flat inner/outer indexing must account for this:
195+ // - when axis reduces a non-channel dim, the outer loop spans channels and
196+ // the channel offset must use cstep rather than the product of spatial sizes;
197+ // - when axis IS the channel dim, the per-element j-stride must be cstep.
198+ const size_t in_cstep = (dims >= 3 ) ? (size_t )bottom_blob.cstep : 0 ;
199+ const size_t out_cstep = (dims >= 3 ) ? values.cstep : 0 ;
200+ const bool axis_is_channel = (dims >= 3 && positive_axis == dims - 1 );
201+ // spatial-only outer count: channels factored out so cstep can be used separately
202+ const int c_channels = (!axis_is_channel && dims >= 3 ) ? shape[dims - 1 ] : 1 ;
203+ const int outer_spatial = (dims >= 3 && !axis_is_channel) ? outer / c_channels : outer;
204+ // stride when stepping along the axis in memory
205+ const size_t in_axis_stride = axis_is_channel ? in_cstep : (size_t )inner;
206+ const size_t out_axis_stride = axis_is_channel ? out_cstep : (size_t )inner;
207+
188208 if (_k == 1 )
189209 {
190210 #pragma omp parallel for num_threads(opt.num_threads)
@@ -193,8 +213,19 @@ int TopK::forward(const std::vector<Mat>& bottom_blobs, std::vector<Mat>& top_bl
193213 int outer_i = line / inner;
194214 int inner_i = line - outer_i * inner;
195215
196- int in_base = outer_i * axis_size * inner + inner_i;
197- int out_base = outer_i * inner + inner_i;
216+ size_t in_base, out_base;
217+ if (!axis_is_channel && dims >= 3 )
218+ {
219+ const int ci = outer_i / outer_spatial;
220+ const int sp_i = outer_i % outer_spatial;
221+ in_base = (size_t )ci * in_cstep + (size_t )sp_i * axis_size * inner + inner_i;
222+ out_base = (size_t )ci * out_cstep + (size_t )sp_i * 1 * inner + inner_i;
223+ }
224+ else
225+ {
226+ in_base = (size_t )outer_i * axis_size * inner + inner_i;
227+ out_base = (size_t )outer_i * 1 * inner + inner_i;
228+ }
198229
199230#if __ARM_NEON
200231 if (!output_indices && inner == 1 && axis_size >= 4 )
@@ -273,7 +304,7 @@ int TopK::forward(const std::vector<Mat>& bottom_blobs, std::vector<Mat>& top_bl
273304
274305 for (int j = 1 ; j < axis_size; j++)
275306 {
276- const float candidate_value = ptr[in_base + j * inner ];
307+ const float candidate_value = ptr[in_base + j * in_axis_stride ];
277308 if (topk_value_index_comp (candidate_value, j, best_value, best_index, largest_flag))
278309 {
279310 best_value = candidate_value;
@@ -301,22 +332,33 @@ int TopK::forward(const std::vector<Mat>& bottom_blobs, std::vector<Mat>& top_bl
301332 int outer_i = line / inner;
302333 int inner_i = line - outer_i * inner;
303334
304- int in_base = outer_i * axis_size * inner + inner_i;
305- int out_base = outer_i * _k * inner + inner_i;
335+ size_t in_base, out_base;
336+ if (!axis_is_channel && dims >= 3 )
337+ {
338+ const int ci = outer_i / outer_spatial;
339+ const int sp_i = outer_i % outer_spatial;
340+ in_base = (size_t )ci * in_cstep + (size_t )sp_i * axis_size * inner + inner_i;
341+ out_base = (size_t )ci * out_cstep + (size_t )sp_i * _k * inner + inner_i;
342+ }
343+ else
344+ {
345+ in_base = (size_t )outer_i * axis_size * inner + inner_i;
346+ out_base = (size_t )outer_i * _k * inner + inner_i;
347+ }
306348
307349 if (output_indices)
308350 {
309351 for (int j = 0 ; j < _k; j++)
310352 {
311- outptr[out_base + j * inner ] = ptr[in_base + j * inner ];
312- outidxptr[out_base + j * inner ] = (float )j;
353+ outptr[out_base + j * out_axis_stride ] = ptr[in_base + j * in_axis_stride ];
354+ outidxptr[out_base + j * out_axis_stride ] = (float )j;
313355 }
314356 }
315357 else
316358 {
317359 for (int j = 0 ; j < _k; j++)
318360 {
319- outptr[out_base + j * inner ] = ptr[in_base + j * inner ];
361+ outptr[out_base + j * out_axis_stride ] = ptr[in_base + j * in_axis_stride ];
320362 }
321363 }
322364 }
@@ -336,8 +378,19 @@ int TopK::forward(const std::vector<Mat>& bottom_blobs, std::vector<Mat>& top_bl
336378 int outer_i = line / inner;
337379 int inner_i = line - outer_i * inner;
338380
339- int in_base = outer_i * axis_size * inner + inner_i;
340- int out_base = outer_i * _k * inner + inner_i;
381+ size_t in_base, out_base;
382+ if (!axis_is_channel && dims >= 3 )
383+ {
384+ const int ci = outer_i / outer_spatial;
385+ const int sp_i = outer_i % outer_spatial;
386+ in_base = (size_t )ci * in_cstep + (size_t )sp_i * axis_size * inner + inner_i;
387+ out_base = (size_t )ci * out_cstep + (size_t )sp_i * _k * inner + inner_i;
388+ }
389+ else
390+ {
391+ in_base = (size_t )outer_i * axis_size * inner + inner_i;
392+ out_base = (size_t )outer_i * _k * inner + inner_i;
393+ }
341394
342395 float top_values[4 ];
343396 int top_indices[4 ];
@@ -347,7 +400,7 @@ int TopK::forward(const std::vector<Mat>& bottom_blobs, std::vector<Mat>& top_bl
347400 {
348401 for (int j = 0 ; j < axis_size; j++)
349402 {
350- const float candidate_value = ptr[in_base + j * inner ];
403+ const float candidate_value = ptr[in_base + j * in_axis_stride ];
351404
352405 if (top_count < _k)
353406 {
@@ -382,7 +435,7 @@ int TopK::forward(const std::vector<Mat>& bottom_blobs, std::vector<Mat>& top_bl
382435 {
383436 for (int j = 0 ; j < axis_size; j++)
384437 {
385- const float candidate_value = ptr[in_base + j * inner ];
438+ const float candidate_value = ptr[in_base + j * in_axis_stride ];
386439
387440 if (top_count < _k)
388441 {
@@ -412,15 +465,15 @@ int TopK::forward(const std::vector<Mat>& bottom_blobs, std::vector<Mat>& top_bl
412465 {
413466 for (int j = 0 ; j < _k; j++)
414467 {
415- outptr[out_base + j * inner ] = top_values[j];
416- outidxptr[out_base + j * inner ] = (float )top_indices[j];
468+ outptr[out_base + j * out_axis_stride ] = top_values[j];
469+ outidxptr[out_base + j * out_axis_stride ] = (float )top_indices[j];
417470 }
418471 }
419472 else
420473 {
421474 for (int j = 0 ; j < _k; j++)
422475 {
423- outptr[out_base + j * inner ] = top_values[j];
476+ outptr[out_base + j * out_axis_stride ] = top_values[j];
424477 }
425478 }
426479 }
@@ -432,58 +485,73 @@ int TopK::forward(const std::vector<Mat>& bottom_blobs, std::vector<Mat>& top_bl
432485 return 0 ;
433486 }
434487
435- #pragma omp parallel num_threads(opt.num_threads)
488+ #pragma omp parallel for num_threads(opt.num_threads)
489+ for (int line = 0 ; line < total_lines; line++)
436490 {
437- std::vector<std::pair<float , int > > vec;
438- vec.resize (axis_size);
491+ std::vector<std::pair<float , int > > vec (axis_size);
439492
440493 topk_pair_comparator comp (largest_flag);
441494
442- #pragma omp for
443- for (int line = 0 ; line < total_lines; line++)
444- {
445- int outer_i = line / inner;
446- int inner_i = line - outer_i * inner;
495+ int outer_i = line / inner;
496+ int inner_i = line - outer_i * inner;
447497
448- int in_base = outer_i * axis_size * inner + inner_i;
449- int out_base = outer_i * _k * inner + inner_i;
498+ size_t in_base, out_base;
499+ if (!axis_is_channel && dims >= 3 )
500+ {
501+ const int ci = outer_i / outer_spatial;
502+ const int sp_i = outer_i % outer_spatial;
503+ in_base = (size_t )ci * in_cstep + (size_t )sp_i * axis_size * inner + inner_i;
504+ out_base = (size_t )ci * out_cstep + (size_t )sp_i * _k * inner + inner_i;
505+ }
506+ else
507+ {
508+ in_base = (size_t )outer_i * axis_size * inner + inner_i;
509+ out_base = (size_t )outer_i * _k * inner + inner_i;
510+ }
450511
451- for (int j = 0 ; j < axis_size; j++)
452- {
453- vec[j].first = ptr[in_base + j * inner ];
454- vec[j].second = j;
455- }
512+ for (int j = 0 ; j < axis_size; j++)
513+ {
514+ vec[j].first = ptr[in_base + j * in_axis_stride ];
515+ vec[j].second = j;
516+ }
456517
457- if (_k < axis_size)
518+ if (_k < axis_size)
519+ {
520+ #if NCNN_SIMPLESTL
521+ std::partial_sort (vec.begin (), vec.begin () + _k, vec.end (), comp);
522+ #else
523+ if (sorted_flag)
458524 {
459- if (sorted_flag)
460- {
461- std::nth_element (vec.begin (), vec.begin () + _k, vec.end (), comp);
462- std::sort (vec.begin (), vec.begin () + _k, comp);
463- }
464- else
465- std::nth_element (vec.begin (), vec.begin () + _k, vec.end (), comp);
525+ std::nth_element (vec.begin (), vec.begin () + _k, vec.end (), comp);
526+ std::sort (vec.begin (), vec.begin () + _k, comp);
466527 }
467528 else
468- {
469- if (sorted_flag)
470- std::sort (vec.begin (), vec.end (), comp);
471- }
529+ std::nth_element (vec.begin (), vec.begin () + _k, vec.end (), comp);
530+ #endif
531+ }
532+ else
533+ {
534+ if (sorted_flag)
535+ #if NCNN_SIMPLESTL
536+ std::partial_sort (vec.begin (), vec.end (), vec.end (), comp);
537+ #else
538+ std::sort (vec.begin (), vec.end (), comp);
539+ #endif
540+ }
472541
473- if (output_indices)
542+ if (output_indices)
543+ {
544+ for (int j = 0 ; j < _k; j++)
474545 {
475- for (int j = 0 ; j < _k; j++)
476- {
477- outptr[out_base + j * inner] = vec[j].first ;
478- outidxptr[out_base + j * inner] = (float )vec[j].second ;
479- }
546+ outptr[out_base + j * out_axis_stride] = vec[j].first ;
547+ outidxptr[out_base + j * out_axis_stride] = (float )vec[j].second ;
480548 }
481- else
549+ }
550+ else
551+ {
552+ for (int j = 0 ; j < _k; j++)
482553 {
483- for (int j = 0 ; j < _k; j++)
484- {
485- outptr[out_base + j * inner] = vec[j].first ;
486- }
554+ outptr[out_base + j * out_axis_stride] = vec[j].first ;
487555 }
488556 }
489557 }
0 commit comments