Skip to content

Commit 7b99764

Browse files
authored
Merge branch 'flagos-ai:master' into fix/benchmark-bugs
2 parents 89dd82e + 5c7fe9f commit 7b99764

9 files changed

Lines changed: 950 additions & 64 deletions

File tree

src/flag_gems/fused/DSA/bin_topk.py

Lines changed: 867 additions & 8 deletions
Large diffs are not rendered by default.

src/flag_gems/fused/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from flag_gems.fused.concat_and_cache_mla import concat_and_cache_mla
44
from flag_gems.fused.cross_entropy_loss import cross_entropy_loss
55
from flag_gems.fused.cutlass_scaled_mm import cutlass_scaled_mm
6+
from flag_gems.fused.DSA.bin_topk import bucket_sort_topk
67
from flag_gems.fused.FLA import (
78
chunk_gated_delta_rule_fwd,
89
fused_recurrent_gated_delta_rule_fwd,
@@ -43,6 +44,7 @@
4344
"apply_repetition_penalties",
4445
"apply_rotary_pos_emb",
4546
"bincount",
47+
"bucket_sort_topk",
4648
"chunk_gated_delta_rule_fwd",
4749
"concat_and_cache_mla",
4850
"cutlass_scaled_mm",

src/flag_gems/fused/moe_align_block_size.py

Lines changed: 3 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -6,27 +6,9 @@
66
import triton
77
import triton.language as tl
88

9-
from flag_gems.utils import libentry, libtuner
10-
11-
12-
def _triton_version_at_least(major: int, minor: int, patch: int = 0) -> bool:
13-
version = str(getattr(triton, "__version__", "0.0.0")).split("+", 1)[0]
14-
parts = version.split(".")
15-
parsed = []
16-
for part in parts[:3]:
17-
digits = []
18-
for ch in part:
19-
if ch.isdigit():
20-
digits.append(ch)
21-
else:
22-
break
23-
parsed.append(int("".join(digits)) if digits else 0)
24-
while len(parsed) < 3:
25-
parsed.append(0)
26-
return tuple(parsed) >= (major, minor, patch)
27-
28-
29-
if _triton_version_at_least(3, 6, 0):
9+
from flag_gems.utils import has_triton_tle, libentry, libtuner
10+
11+
if has_triton_tle(3, 6, 0):
3012
try:
3113
import triton.experimental.tle.language as tle
3214
import triton.experimental.tle.language.gpu as tleg

src/flag_gems/utils/__init__.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,11 @@
1414
)
1515
from flag_gems.utils.triton_driver_helper import get_device_properties
1616
from flag_gems.utils.triton_lang_helper import tl_extra_shim
17-
from flag_gems.utils.triton_version_utils import HAS_TLE, _triton_version_at_least
17+
from flag_gems.utils.triton_version_utils import (
18+
HAS_TLE,
19+
_triton_version_at_least,
20+
has_triton_tle,
21+
)
1822

1923
__all__ = [
2024
"libentry",
@@ -31,5 +35,6 @@
3135
"get_device_properties",
3236
"tl_extra_shim",
3337
"_triton_version_at_least",
38+
"has_triton_tle",
3439
"HAS_TLE",
3540
]
Lines changed: 28 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,36 @@
1+
import re
2+
13
import triton
4+
from packaging.version import InvalidVersion, Version
5+
6+
7+
def _coerce_triton_version(version: str) -> Version:
8+
try:
9+
return Version(version)
10+
except InvalidVersion:
11+
release = []
12+
for part in version.split("+", 1)[0].split(".")[:3]:
13+
match = re.match(r"\d+", part)
14+
release.append(match.group(0) if match else "0")
15+
while len(release) < 3:
16+
release.append("0")
17+
return Version(".".join(release))
218

319

420
def _triton_version_at_least(major: int, minor: int, patch: int = 0) -> bool:
5-
version = str(getattr(triton, "__version__", "0.0.0")).split("+", 1)[0]
6-
parts = version.split(".")
7-
parsed = []
8-
for part in parts[:3]:
9-
digits = []
10-
for ch in part:
11-
if ch.isdigit():
12-
digits.append(ch)
13-
else:
14-
break
15-
parsed.append(int("".join(digits)) if digits else 0)
16-
while len(parsed) < 3:
17-
parsed.append(0)
18-
return tuple(parsed) >= (major, minor, patch)
19-
20-
21-
HAS_TLE = False
22-
if _triton_version_at_least(3, 1, 0):
21+
version = str(getattr(triton, "__version__", "0.0.0"))
22+
return _coerce_triton_version(version) >= Version(f"{major}.{minor}.{patch}")
23+
24+
25+
def has_triton_tle(major: int = 0, minor: int = 0, patch: int = 0) -> bool:
26+
if not _triton_version_at_least(major, minor, patch):
27+
return False
2328
try:
2429
import triton.experimental.tle.language as _tle # noqa: F401
2530

26-
HAS_TLE = True
31+
return True
2732
except ImportError:
28-
pass
33+
return False
34+
35+
36+
HAS_TLE = has_triton_tle()

tests/conftest.py

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44
from datetime import datetime
55

66
import pytest
7-
import torch
7+
8+
# TODO(Qiming): Try remove this line
9+
import torch # noqa: F401
810
import yaml
911

1012
import flag_gems
@@ -44,16 +46,9 @@ def pytest_addoption(parser):
4446
)
4547

4648
parser.addoption(
47-
(
48-
"--mode"
49-
if not (flag_gems.vendor_name == "kunlunxin" and torch.__version__ < "2.5")
50-
else "--fg_mode"
51-
), # TODO: fix pytest-* common --mode args,
52-
action="store",
53-
default="normal",
54-
required=False,
55-
choices=["normal", "quick"],
56-
help="run tests on normal or quick mode",
49+
"--quick",
50+
action="store_true",
51+
help="run tests on quick mode",
5752
)
5853

5954
parser.addoption(
@@ -85,7 +80,7 @@ def pytest_configure(config):
8580

8681
RECORD_LOG = config.getoption("--record") == "log"
8782
TO_CPU = config.getoption("--ref") == "cpu"
88-
QUICK_MODE = config.getoption("--mode") == "quick"
83+
QUICK_MODE = config.getoption("--quick") is True
8984

9085
if RECORD_LOG:
9186
RUNTEST_INFO = {}

tests/test_DSA/test_bin_topk.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from flag_gems.fused.DSA.bin_topk import (
99
bucket_sort_topk, # Replace with actual module name
1010
)
11+
from flag_gems.fused.DSA.bin_topk import HAS_TLE
1112

1213

1314
def assert_set_similar(actual, expected, dtype, equal_nan=False):
@@ -134,6 +135,37 @@ def debug_topk_results(actual, expected, inputs, test_name=""):
134135
print(f" Expected top values: {np.sort(expected_values)[-m:][::-1]}")
135136

136137

138+
@pytest.mark.skipif(not torch.cuda.is_available(), reason="CUDA device required")
139+
@pytest.mark.skipif(not HAS_TLE, reason="TLE bucket_sort_topk is unavailable")
140+
@pytest.mark.bucket_sort_topk
141+
@pytest.mark.parametrize(
142+
("starts_list", "ends_list"),
143+
[
144+
([0, 0, 0], [512, 768, 1024]),
145+
([7, 31, 63], [700, 900, 1024]),
146+
],
147+
)
148+
def test_bucket_sort_topk_public_entrypoint_matches_torch_topk(starts_list, ends_list):
149+
batch_size = len(starts_list)
150+
seq_len = 1024
151+
topk = 32
152+
dtype = torch.float32
153+
154+
init_seed(2026)
155+
inputs = torch.randn((batch_size, seq_len), dtype=dtype, device=device)
156+
starts = torch.tensor(starts_list, dtype=torch.int32, device=device)
157+
ends = torch.tensor(ends_list, dtype=torch.int32, device=device)
158+
159+
ref_indices = reference_topk_implementation(
160+
to_reference(inputs), to_reference(starts), to_reference(ends), topk
161+
)
162+
actual_indices = bucket_sort_topk(inputs, starts, ends, topk)
163+
164+
assert actual_indices.shape == (batch_size, topk)
165+
assert actual_indices.dtype == torch.int32
166+
assert_set_similar(actual_indices, ref_indices, dtype)
167+
168+
137169
@pytest.mark.skip(
138170
"RuntimeError: Cannot call @triton.jit'd outside of the scope of a kernel"
139171
)

tests/test_flash_attn_varlen_func.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,6 @@ def ref_paged_attn(
9393
return torch.cat(outputs, dim=0)
9494

9595

96-
@pytest.mark.skipif(True, reason="Dead code on line 221 fails to pass.")
9796
@pytest.mark.skipif(vendor_name == "kunlunxin", reason="RESULT TODOFIX")
9897
@pytest.mark.skipif(vendor_name == "hygon", reason="RESULT TODOFIX")
9998
@pytest.mark.flash_attn_varlen_func
@@ -229,6 +228,10 @@ def test_flash_attn_varlen_func(
229228
softmax_scale=scale,
230229
causal=causal,
231230
window_size=window_size,
231+
block_table=block_tables,
232+
softcap=soft_cap if soft_cap is not None else 0,
233+
alibi_slopes=alibi_slopes,
234+
fa_version=2,
232235
)
233236

234237
ref_output = ref_paged_attn(

tools/test-op.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ coverage run -m pytest -s ${EXTRA_OPTS} ${TEST_CASES[@]}
8181
# Run quick-cpu test if necessary
8282
if [[ ${#TEST_CASES_CPU[@]} -ne 0 ]]; then
8383
echo "Running quick-cpu mode unit tests for ${TEST_CASES_CPU[@]}"
84-
coverage run -m pytest -s ${EXTRA_OPTS} ${TEST_CASES_CPU[@]} --ref=cpu --mode=quick
84+
coverage run -m pytest -s ${EXTRA_OPTS} ${TEST_CASES_CPU[@]} --ref=cpu --quick
8585
fi
8686

8787
# Process coverage data only when full-range testing

0 commit comments

Comments
 (0)