Skip to content

Commit d737a5a

Browse files
committed
TEST: cover mixed-radix allgather schedules
Cover schedule parsing, legacy fixed-radix behavior, and exact mixed-radix peer and segment layouts.
1 parent 573dd36 commit d737a5a

2 files changed

Lines changed: 187 additions & 0 deletions

File tree

test/gtest/Makefile.am

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,12 @@ gtest_LDFLAGS += $(UCX_LDFLAGS)
144144
gtest_LDADD += $(UCX_LIBS) $(UCX_LIBADD)
145145
endif
146146

147+
if TL_UCP_ENABLED
148+
gtest_SOURCES += \
149+
coll/test_knomial_schedule.cc \
150+
$(top_srcdir)/src/components/tl/ucp/allgather/allgather_knomial_schedule.c
151+
endif
152+
147153
noinst_HEADERS = \
148154
common/gtest.h \
149155
common/test.h \
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
/**
2+
* Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3+
* See file LICENSE for terms.
4+
*/
5+
6+
extern "C" {
7+
#include "utils/ucc_coll_utils.h"
8+
#include "coll_patterns/sra_knomial.h"
9+
10+
ucc_status_t ucc_tl_ucp_allgather_knomial_parse_radices(
11+
const char *value, ucc_rank_t team_size, ucc_kn_radix_t *radices,
12+
uint8_t *nradices);
13+
}
14+
15+
#include <common/test.h>
16+
#include <sstream>
17+
#include <vector>
18+
19+
class test_knomial_schedule : public ucc::test {
20+
protected:
21+
static void expect_parse_status(
22+
const char *value, ucc_rank_t size, ucc_status_t expected)
23+
{
24+
ucc_kn_radix_t radices[UCC_KN_MAX_RADIX_PHASES];
25+
uint8_t nradices;
26+
27+
EXPECT_EQ(
28+
expected,
29+
ucc_tl_ucp_allgather_knomial_parse_radices(
30+
value, size, radices, &nradices));
31+
}
32+
33+
static void expect_valid(
34+
const char *value, ucc_rank_t size,
35+
const std::vector<ucc_kn_radix_t> &expected)
36+
{
37+
ucc_kn_radix_t radices[UCC_KN_MAX_RADIX_PHASES];
38+
uint8_t nradices;
39+
40+
ASSERT_EQ(
41+
UCC_OK,
42+
ucc_tl_ucp_allgather_knomial_parse_radices(
43+
value, size, radices, &nradices));
44+
ASSERT_EQ(expected.size(), nradices);
45+
for (size_t i = 0; i < expected.size(); i++) {
46+
EXPECT_EQ(expected[i], radices[i]);
47+
}
48+
}
49+
50+
static void expect_mixed_pattern(
51+
ucc_rank_t size, const std::vector<ucc_kn_radix_t> &radices)
52+
{
53+
for (ucc_rank_t rank = 0; rank < size; rank++) {
54+
ucc_knomial_pattern_t p;
55+
ucc_rank_t phase_size = 1;
56+
57+
ucc_kn_ag_pattern_init_mixed(
58+
size, rank, size, radices.data(), radices.size(), &p);
59+
ASSERT_TRUE(p.is_mixed);
60+
ASSERT_EQ(radices.size(), p.n_iters);
61+
ASSERT_EQ(0, p.n_extra);
62+
for (size_t phase = 0; phase < radices.size(); phase++) {
63+
ucc_kn_radix_t radix = radices[phase];
64+
ucc_rank_t group_size = phase_size * radix;
65+
size_t count;
66+
ptrdiff_t offset;
67+
68+
EXPECT_EQ(radix, p.radix);
69+
EXPECT_EQ(radix, ucc_kn_compute_step_radix(&p));
70+
ucc_kn_ag_pattern_peer_seg(rank, &p, &count, &offset);
71+
EXPECT_EQ(phase_size, count);
72+
EXPECT_EQ((rank / phase_size) * phase_size, offset);
73+
74+
for (ucc_kn_radix_t step = 1; step < radix; step++) {
75+
ucc_rank_t peer = ucc_knomial_pattern_get_loop_peer(
76+
&p, rank, step);
77+
ASSERT_NE(UCC_KN_PEER_NULL, peer);
78+
EXPECT_EQ(rank / group_size, peer / group_size);
79+
EXPECT_EQ(rank % phase_size, peer % phase_size);
80+
ucc_kn_ag_pattern_peer_seg(peer, &p, &count, &offset);
81+
EXPECT_EQ(phase_size, count);
82+
EXPECT_EQ((peer / phase_size) * phase_size, offset);
83+
}
84+
ucc_kn_ag_pattern_next_iter(&p);
85+
phase_size = group_size;
86+
}
87+
EXPECT_TRUE(ucc_knomial_pattern_loop_done(&p));
88+
EXPECT_EQ(size, phase_size);
89+
}
90+
}
91+
};
92+
93+
UCC_TEST_F(test_knomial_schedule, parse_valid_exact_schedules)
94+
{
95+
expect_valid("8,6", 48, {8, 6});
96+
expect_valid("3,3,2,2,2", 72, {3, 3, 2, 2, 2});
97+
expect_valid("4,4,6", 96, {4, 4, 6});
98+
}
99+
100+
UCC_TEST_F(test_knomial_schedule, parse_invalid_tokens_and_radices)
101+
{
102+
expect_parse_status("", 96, UCC_ERR_NOT_FOUND);
103+
expect_parse_status("4,x,6", 96, UCC_ERR_INVALID_PARAM);
104+
expect_parse_status("4,0,6", 96, UCC_ERR_INVALID_PARAM);
105+
expect_parse_status("4,1,6", 96, UCC_ERR_INVALID_PARAM);
106+
expect_parse_status("4,4,4", 96, UCC_ERR_INVALID_PARAM);
107+
expect_parse_status("4,4,6,", 96, UCC_ERR_INVALID_PARAM);
108+
expect_parse_status("65536", 65536, UCC_ERR_INVALID_PARAM);
109+
expect_parse_status("65535,65535,2", 2, UCC_ERR_INVALID_PARAM);
110+
}
111+
112+
UCC_TEST_F(test_knomial_schedule, parse_overlong_schedule)
113+
{
114+
std::ostringstream value;
115+
116+
for (unsigned i = 0; i <= UCC_KN_MAX_RADIX_PHASES; i++) {
117+
value << (i ? ",2" : "2");
118+
}
119+
expect_parse_status(
120+
value.str().c_str(), UCC_RANK_MAX, UCC_ERR_INVALID_PARAM);
121+
}
122+
123+
UCC_TEST_F(test_knomial_schedule, fixed_pattern_preserves_legacy_layout)
124+
{
125+
const ucc_rank_t sizes[] = {16, 48, 72, 96};
126+
const ucc_kn_radix_t radices[] = {2, 3, 4, 6, 8};
127+
128+
for (auto size : sizes) {
129+
for (auto radix : radices) {
130+
for (ucc_rank_t rank = 0; rank < size; rank++) {
131+
ucc_knomial_pattern_t p;
132+
ucc_rank_t legacy_radix_pow = 1;
133+
134+
ucc_kn_ag_pattern_init(size, rank, radix, size, &p);
135+
ASSERT_FALSE(p.is_mixed);
136+
for (uint8_t phase = 0; phase < p.n_iters; phase++) {
137+
ucc_rank_t n_full = size / p.full_pow_size;
138+
ucc_rank_t legacy_segment_radix = radix;
139+
140+
if (legacy_radix_pow * radix >= size && n_full > 1) {
141+
legacy_segment_radix = n_full;
142+
}
143+
EXPECT_EQ(radix, p.radix);
144+
EXPECT_EQ(legacy_segment_radix,
145+
ucc_kn_compute_step_radix(&p));
146+
if (p.node_type != KN_NODE_EXTRA) {
147+
for (ucc_kn_radix_t step = 1; step < radix; step++) {
148+
ucc_rank_t
149+
loop_rank = ucc_knomial_pattern_loop_rank(
150+
&p, rank);
151+
ucc_rank_t step_size = legacy_radix_pow * radix;
152+
ucc_rank_t peer = (loop_rank +
153+
step * legacy_radix_pow) %
154+
step_size +
155+
ucc_align_down(
156+
loop_rank, step_size);
157+
ucc_rank_t expected =
158+
peer >= size - p.n_extra
159+
? UCC_KN_PEER_NULL
160+
: ucc_knomial_pattern_loop_rank_inv(
161+
&p, peer);
162+
EXPECT_EQ(
163+
expected,
164+
ucc_knomial_pattern_get_loop_peer(
165+
&p, rank, step));
166+
}
167+
}
168+
ucc_kn_ag_pattern_next_iter(&p);
169+
legacy_radix_pow *= radix;
170+
}
171+
}
172+
}
173+
}
174+
}
175+
176+
UCC_TEST_F(test_knomial_schedule, mixed_peer_and_segment_layout)
177+
{
178+
expect_mixed_pattern(48, {8, 6});
179+
expect_mixed_pattern(72, {3, 3, 2, 2, 2});
180+
expect_mixed_pattern(96, {4, 4, 6});
181+
}

0 commit comments

Comments
 (0)