Skip to content

Commit 830149c

Browse files
committed
topk: fix STL compatibility, cstep indexing, omp barrier, and code style
- Guard <algorithm>/<vector> behind #if NCNN_SIMPLESTL, include simplestl.h - Use std::partial_sort in simplestl mode (no std::nth_element available) - Guard <math.h> in tests behind #if !NCNN_SIMPLESTL to avoid simplemath.h conflict; define INFINITY/NAN as float expressions in simplestl mode - Fix cstep-unaware indexing for 3D/4D output tensors: use actual cstep for channel offset instead of assuming contiguous w*h layout - Convert #pragma omp parallel + inner #pragma omp for to #pragma omp parallel for to avoid __kmpc_barrier in simpleomp mode - Fix copyright year 2026->2025 - Apply code-format whitespace cleanup
1 parent 642d413 commit 830149c

5 files changed

Lines changed: 145 additions & 69 deletions

File tree

src/layer/topk.cpp

Lines changed: 123 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
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
}

src/layer/topk.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2026 Tencent
1+
// Copyright 2025 Tencent
22
// SPDX-License-Identifier: BSD-3-Clause
33

44
#ifndef LAYER_TOPK_H

tests/test_topk.cpp

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,17 @@
1-
// Copyright 2026 Tencent
1+
// Copyright 2025 Tencent
22
// SPDX-License-Identifier: BSD-3-Clause
33

44
#include "testutil.h"
55

6-
#include <limits>
6+
#if NCNN_SIMPLESTL
7+
// simplemath.h conflicts with system math.h; define only what we need
8+
static const float TEST_INF = 1.f / 0.f;
9+
static const float TEST_NAN = 0.f / 0.f;
10+
#define INFINITY TEST_INF
11+
#define NAN TEST_NAN
12+
#else
13+
#include <math.h>
14+
#endif
715

816
static int test_topk_cpu_forward(const ncnn::Mat& a, int axis, int k, int largest, int sorted, ncnn::Mat& values, ncnn::Mat& indices)
917
{
@@ -121,7 +129,7 @@ static int test_topk_0()
121129
return 0
122130
|| test_topk(a, 0, 1, 1, 1)
123131
|| test_topk(a, 0, 5, 1, 1)
124-
|| test_topk(a, 0, 1, 0, 0)
132+
|| test_topk(a, 0, 1, 0, 0)
125133
|| test_topk(a, -1, 7, 0, 1)
126134
|| test_topk(a, 0, 4, 1, 0)
127135
|| test_topk(a, 0, 9, 1, 1);
@@ -175,9 +183,9 @@ static int test_topk_inf_order()
175183
ncnn::Mat a(6);
176184
float* ptr = a;
177185
ptr[0] = 1.f;
178-
ptr[1] = std::numeric_limits<float>::infinity();
186+
ptr[1] = INFINITY;
179187
ptr[2] = -2.f;
180-
ptr[3] = -std::numeric_limits<float>::infinity();
188+
ptr[3] = -INFINITY;
181189
ptr[4] = 0.5f;
182190
ptr[5] = 3.f;
183191

@@ -193,7 +201,7 @@ static int test_topk_inf_order()
193201

194202
const float* vptr = values;
195203
const float* iptr = indices;
196-
if (values.w != 2 || indices.w != 2 || vptr[0] != std::numeric_limits<float>::infinity() || vptr[1] != 3.f || (int)iptr[0] != 1 || (int)iptr[1] != 5)
204+
if (values.w != 2 || indices.w != 2 || vptr[0] != INFINITY || vptr[1] != 3.f || (int)iptr[0] != 1 || (int)iptr[1] != 5)
197205
{
198206
fprintf(stderr, "test_topk_inf_order largest result mismatch\n");
199207
return -1;
@@ -208,7 +216,7 @@ static int test_topk_inf_order()
208216

209217
vptr = values;
210218
iptr = indices;
211-
if (values.w != 2 || indices.w != 2 || vptr[0] != -std::numeric_limits<float>::infinity() || vptr[1] != -2.f || (int)iptr[0] != 3 || (int)iptr[1] != 2)
219+
if (values.w != 2 || indices.w != 2 || vptr[0] != -INFINITY || vptr[1] != -2.f || (int)iptr[0] != 3 || (int)iptr[1] != 2)
212220
{
213221
fprintf(stderr, "test_topk_inf_order smallest result mismatch\n");
214222
return -1;
@@ -222,7 +230,7 @@ static int test_topk_nan_robust()
222230
ncnn::Mat a(4);
223231
float* ptr = a;
224232
ptr[0] = 1.f;
225-
ptr[1] = std::numeric_limits<float>::quiet_NaN();
233+
ptr[1] = NAN;
226234
ptr[2] = 2.f;
227235
ptr[3] = -1.f;
228236

tools/pnnx/src/ir.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1640,12 +1640,12 @@ int Graph::python(const std::string& pypath, const std::string& pnnxbinpath, con
16401640
continue;
16411641

16421642
fprintf(pyfp, " self.%s = TopK(", sanitize_identifier(op->name).c_str());
1643-
1643+
16441644
int i = 0;
16451645
for (const auto& it : op->params)
16461646
{
16471647
fprintf(pyfp, "%s=", it.first.c_str());
1648-
1648+
16491649
const Parameter& param = it.second;
16501650
if (param.type == 2)
16511651
{
@@ -1655,12 +1655,12 @@ int Graph::python(const std::string& pypath, const std::string& pnnxbinpath, con
16551655
{
16561656
fprintf(pyfp, "%d", param.b ? 1 : 0);
16571657
}
1658-
1658+
16591659
if (i + 1 != op->params.size())
16601660
fprintf(pyfp, ", ");
16611661
i++;
16621662
}
1663-
1663+
16641664
fprintf(pyfp, ")\n");
16651665
}
16661666
}

tools/pnnx/src/pass_ncnn/TopK.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2026 Tencent
1+
// Copyright 2025 Tencent
22
// SPDX-License-Identifier: BSD-3-Clause
33

44
#include "pass_ncnn.h"

0 commit comments

Comments
 (0)