Skip to content

Commit d5be4a9

Browse files
authored
Merge branch 'main' into feature/gpu_set_layout&gpu_alloc
2 parents 7d80107 + ac89e40 commit d5be4a9

16 files changed

Lines changed: 302 additions & 6 deletions

File tree

.github/workflows/enflame3.6-gcu400-build-and-test.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,8 @@ jobs:
106106
python3 -m pytest python/test/tle \
107107
--ignore=python/test/tle/unit/test_tle_distributed_d2d.py \
108108
--ignore=python/test/tle/unit/test_tle_get_local_pe.py \
109-
--ignore=python/test/tle/unit/test_tle_d2d_barrier.py
109+
--ignore=python/test/tle/unit/test_tle_d2d_barrier.py \
110+
--ignore=python/test/tle/unit/test_tle_get_node_rank.py
110111
111112
## tle raw test
112113
python3 -m pytest third_party/enflame/python/test/tle/raw

.github/workflows/nvidia3.6-build-and-test.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,8 @@ jobs:
130130
python3 -m pytest -s python/test/tle/unit \
131131
--ignore=python/test/tle/unit/test_tle_distributed_d2d.py \
132132
--ignore=python/test/tle/unit/test_tle_get_local_pe.py \
133-
--ignore=python/test/tle/unit/test_tle_d2d_barrier.py
133+
--ignore=python/test/tle/unit/test_tle_d2d_barrier.py \
134+
--ignore=python/test/tle/unit/test_tle_get_node_rank.py
134135
## flagtree hints python tutorials
135136
python3 python/tutorials/hints/01/01-vector-add.py --only_unit_test
136137
# python3 python/tutorials/hints/02/02-fused-softmax.py --only_unit_test

python/test/tle/unit/test_tle_distributed.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,16 @@ def __init__(self):
226226
self.builder = _LegacyDistributedBarrierBuilder()
227227

228228

229+
class TestShardId:
230+
231+
@pytest.mark.parametrize("axis", ("device", "node"))
232+
def test_rank_axis_requires_device_dptr(self, axis):
233+
mesh = tle.device_mesh({"node": 2, "device": 4})
234+
semantic = _FakeSemantic()
235+
with pytest.raises(ValueError, match=rf"device_dptr is required for axis '{axis}'"):
236+
tle.shard_id(mesh, axis, _semantic=semantic)
237+
238+
229239
class TestDistributedBarrierScope:
230240

231241
def test_distributed_barrier_full_cluster_mesh(self):
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import os
2+
3+
import torch
4+
import torch.distributed as dist
5+
import triton
6+
import triton.experimental.tle.language as tle
7+
import triton.language as tl
8+
9+
LOCAL_WORLD_SIZE = int(os.environ["LOCAL_WORLD_SIZE"])
10+
WORLD_SIZE = int(os.environ["WORLD_SIZE"])
11+
if WORLD_SIZE % LOCAL_WORLD_SIZE != 0:
12+
raise ValueError("WORLD_SIZE must be divisible by LOCAL_WORLD_SIZE")
13+
14+
DEVICE_MESH = tle.device_mesh(tle.MeshConfig(node=WORLD_SIZE // LOCAL_WORLD_SIZE, device=LOCAL_WORLD_SIZE))
15+
16+
17+
@triton.jit
18+
def _tle_node_rank_kernel(out_ptr, device_dptr: tl.constexpr, mesh: tl.constexpr):
19+
pid = tl.program_id(0)
20+
node_rank = tle.shard_id(mesh, "node", device_dptr=device_dptr)
21+
tl.store(out_ptr + pid, node_rank)
22+
23+
24+
def test_tle_get_node_rank():
25+
grid = 2
26+
with torch.cuda.use_mem_pool(tle.get_mem_pool()):
27+
source = torch.empty((1, ), dtype=torch.float32, device="cuda")
28+
device_dptr = tle.create_dist_tensor(source)
29+
node_rank_out = torch.empty((grid, ), dtype=torch.int32, device="cuda")
30+
31+
compiled = _tle_node_rank_kernel.warmup(
32+
out_ptr=node_rank_out,
33+
device_dptr=device_dptr,
34+
mesh=DEVICE_MESH,
35+
grid=(grid, ),
36+
num_ctas=1,
37+
num_warps=4,
38+
)
39+
assert "get_world_rank" in compiled.asm["ttgir"]
40+
assert "get_num_pes" in compiled.asm["ttgir"]
41+
assert "flagcxDevCommGetRank" in compiled.asm["ptx"]
42+
assert "flagcxDevCommGetIntraSize" in compiled.asm["ptx"]
43+
44+
_tle_node_rank_kernel[(grid, )](
45+
out_ptr=node_rank_out,
46+
device_dptr=device_dptr,
47+
mesh=DEVICE_MESH,
48+
)
49+
torch.cuda.synchronize()
50+
51+
rank = dist.get_rank()
52+
expected_node_rank = rank // LOCAL_WORLD_SIZE
53+
actual_node_ranks = node_rank_out.cpu().tolist()
54+
try:
55+
torch.testing.assert_close(
56+
node_rank_out,
57+
torch.full_like(node_rank_out, expected_node_rank),
58+
)
59+
except AssertionError:
60+
print(
61+
f"[Rank {rank}] FAILED: node ranks={actual_node_ranks}, "
62+
f"expected={expected_node_rank}",
63+
flush=True,
64+
)
65+
raise
66+
else:
67+
print(
68+
f"[Rank {rank}] PASSED: node ranks={actual_node_ranks}, "
69+
f"expected={expected_node_rank}",
70+
flush=True,
71+
)
72+
finally:
73+
tle.cleanup_communicator()
74+
75+
76+
if __name__ == "__main__":
77+
test_tle_get_node_rank()
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
#!/bin/bash
2+
3+
if [ "$1" = "debug" ]; then
4+
export NCCL_DEBUG=INFO
5+
export NCCL_DEBUG_SUBSYS=all
6+
else
7+
unset NCCL_DEBUG
8+
unset NCCL_DEBUG_SUBSYS
9+
fi
10+
11+
export FLAGCX_IB_HCA=mlx5_0,mlx5_1,mlx5_2,mlx5_3,mlx5_6,mlx5_7,mlx5_8,mlx5_9
12+
export FLAGCX_USE_HETERO_COMM=1
13+
export FLAGCX_MEM_ENABLE=1
14+
export FLAGCX_VMM_ENABLE=0
15+
export FLAGCX_P2P_DISABLE=1
16+
export CUDA_VISIBLE_DEVICES=0,1
17+
18+
nproc_per_node=${NPROC_PER_NODE:-2}
19+
nnodes=${NNODES:-2}
20+
node_rank=${NODE_RANK:-0}
21+
master_addr=${MASTER_ADDR:-10.0.9.3}
22+
port=${MASTER_PORT:-8335}
23+
24+
if [ "${nnodes}" -eq 1 ]; then
25+
while ss -ltn | grep -q ":${port} "; do
26+
echo "Port ${port} is occupied, trying next..."
27+
port=$((port + 2))
28+
done
29+
fi
30+
31+
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
32+
echo "Using master ${master_addr}:${port}, node ${node_rank}/${nnodes}"
33+
34+
torchrun \
35+
--nproc_per_node="${nproc_per_node}" \
36+
--nnodes="${nnodes}" \
37+
--node_rank="${node_rank}" \
38+
--master_addr="${master_addr}" \
39+
--master_port="${port}" \
40+
"${script_dir}/test_tle_get_node_rank.py"

python/triton/experimental/tle/language/distributed.py

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,12 +71,23 @@ def _get_local_rank(device_dptr, _semantic=None, ret_dtype=tl.int32):
7171
return tl.tensor(result, ret_dtype)
7272

7373

74-
# The number of devices in the world
74+
# Get the current world rank
75+
@tl.builtin
76+
def _get_world_rank(device_dptr, _semantic=None, ret_dtype=tl.int32):
77+
builder = _semantic.builder
78+
ret_ir_ty = ret_dtype.to_ir(builder)
79+
ptr = _parse_src_arg(builder, device_dptr, 1)
80+
result = builder.get_world_rank(ret_ir_ty, ptr)
81+
return tl.tensor(result, ret_dtype)
82+
83+
84+
# The number of devices on the current node
7585
@tl.builtin
7686
def n_pes(dev_mem_ptr, _semantic=None, ret_dtype=tl.int32):
7787
builder = _semantic.builder
7888
ret_ir_ty = ret_dtype.to_ir(builder)
79-
result = builder.get_n_pes(ret_ir_ty, dev_mem_ptr.handle)
89+
ptr = _parse_src_arg(builder, dev_mem_ptr, 1)
90+
result = builder.get_n_pes(ret_ir_ty, ptr)
8091
return tl.tensor(result, ret_dtype)
8192

8293

@@ -639,13 +650,21 @@ def shard_id(
639650
Return current shard coordinate on the given launch mesh axis.
640651
641652
`axis` can be axis name (`str`) or axis index (`int`, supports negative).
653+
`device` returns the intra-node rank; `node` returns the inter-node rank.
642654
The returned value is a scalar int32 tensor.
643655
"""
644656
mesh = tl._unwrap_if_constexpr(mesh)
645657
axis = tl._unwrap_if_constexpr(axis)
646658

647-
if axis in ("device", "node"):
659+
if axis in ("device", "node") and device_dptr is None:
660+
raise ValueError(f"device_dptr is required for axis {axis!r}")
661+
662+
if axis == "device":
648663
return _get_local_rank(device_dptr, _semantic=_semantic, ret_dtype=tl.int32)
664+
if axis == "node":
665+
world_rank = _get_world_rank(device_dptr, _semantic=_semantic, ret_dtype=tl.int32)
666+
local_world_size = n_pes(device_dptr, _semantic=_semantic, ret_dtype=tl.int32)
667+
return _semantic.floordiv(world_rank, local_world_size)
649668

650669
if not isinstance(mesh, device_mesh):
651670
raise TypeError(f"mesh must be device_mesh, got {type(mesh).__name__}")

third_party/tle/dialect/include/Conversion/TleToLLVM/FlagCxOpToLLVM/FlagCxOpToLLVM.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323

2424
#include "tle/dialect/include/Conversion/TleToLLVM/FlagCxOpToLLVM/DeviceIntraBarrierOpToLLVM.h"
2525
#include "tle/dialect/include/Conversion/TleToLLVM/FlagCxOpToLLVM/GetLocalRankOpToLLVM.h"
26+
#include "tle/dialect/include/Conversion/TleToLLVM/FlagCxOpToLLVM/GetWorldRankOpToLLVM.h"
2627

2728
namespace mlir::triton::tle {
2829
void populateFlagCxOpToLLVMPatterns(LLVMTypeConverter &typeConverter,
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
* Copyright 2025- FlagOS Contributors
3+
*
4+
* Permission is hereby granted, free of charge, to any person obtaining
5+
* a copy of this software and associated documentation files
6+
* (the "Software"), to deal in the Software without restriction,
7+
* including without limitation the rights to use, copy, modify, merge,
8+
* publish, distribute, sublicense, and/or sell copies of the Software,
9+
* and to permit persons to whom the Software is furnished to do so,
10+
* subject to the following conditions:
11+
*
12+
* The above copyright notice and this permission notice shall be
13+
* included in all copies or substantial portions of the Software.
14+
*
15+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16+
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17+
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18+
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19+
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20+
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21+
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22+
*/
23+
24+
#ifndef TLE_CONVERSION_TLETOLLVM_FLAGCXOPTOLLVM_GETWORLDRANKOPTOLLVM_H
25+
#define TLE_CONVERSION_TLETOLLVM_FLAGCXOPTOLLVM_GETWORLDRANKOPTOLLVM_H
26+
27+
#include "mlir/Conversion/LLVMCommon/TypeConverter.h"
28+
29+
namespace mlir::triton::tle {
30+
31+
void populateGetWorldRankOpToLLVMPatterns(LLVMTypeConverter &typeConverter,
32+
RewritePatternSet &patterns,
33+
PatternBenefit benefit);
34+
35+
} // namespace mlir::triton::tle
36+
37+
#endif

third_party/tle/dialect/include/IR/TleOps.td

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -384,7 +384,7 @@ def Tle_RemotePointersOp : Tle_Op<"remote_pointers", [Pure, AttrSizedOperandSegm
384384
let hasVerifier = 1;
385385
}
386386
def Tle_GetNumPesOp : Tle_Op<"get_num_pes"> {
387-
let summary = "Get nume pes";
387+
let summary = "Get the number of intra-node PEs";
388388
let arguments = (ins
389389
Tle_LocalPointerResultType:$src
390390
);
@@ -401,6 +401,15 @@ def Tle_GetDeviceIdOp : Tle_Op<"get_device_id"> {
401401
let hasVerifier = 1;
402402
}
403403

404+
def Tle_GetWorldRankOp : Tle_Op<"get_world_rank"> {
405+
let summary = "Get global PE rank";
406+
let arguments = (ins
407+
Tle_LocalPointerResultType:$input
408+
);
409+
let results = (outs I32:$result);
410+
let hasVerifier = 1;
411+
}
412+
404413

405414
def Tle_DSLRegionOp : Tle_Op<"dsl_region", [IsolatedFromAbove, MemDescViewTrait,
406415
DeclareOpInterfaceMethods<MemoryEffectsOpInterface>]> {

third_party/tle/dialect/include/Tools/FlagcxUtils.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ LLVM::CallOp getLocalPeFuncCall(mlir::Location loc,
3535
ConversionPatternRewriter &rewriter,
3636
Value memPtrInt);
3737

38+
LLVM::CallOp getWorldRankFuncCall(mlir::Location loc,
39+
ConversionPatternRewriter &rewriter,
40+
Value memPtrInt);
41+
3842
LLVM::CallOp getNumPesFunCall(mlir::Location loc,
3943
ConversionPatternRewriter &rewriter,
4044
Value memPtrInt);

0 commit comments

Comments
 (0)