sgl-project/sglang
Languages
SGLang is a high-performance serving framework for large language models and multimodal models.
Harness | Input / Output Cost | ||||||
|---|---|---|---|---|---|---|---|
1 | 46 / 59 | $0.49 | $2/$12 | 2m42s | |||
2 | 45 / 59 | $0.12 | $0.2/$1.2 | 4m27s | |||
3 | 45 / 59 | $2.28 | $5/$30 | 5m39s | |||
4 | 45 / 59 | $0.77 | $1.4/$4.4 | 13m46s | |||
5 | 45 / 59 | $7.11 | $10/$50 | 14m58s | |||
6 | 44 / 59 | $2.41 | $5/$30 | 8m06s | |||
7 | 43 / 59 | $0.66 | $2/$12 | 2m40s | |||
8 | 43 / 59 | $0.74 | $2/$6 | 3m46s | |||
9 | 43 / 59 | $8.84 | $3/$15 | 13m12s | |||
10 | 43 / 59 | $4.25 | $5/$25 | 14m15s | |||
11 | 42 / 59 | $1.50 | $1.5/$9 | 7m39s | |||
12 | 42 / 59 | $4.69 | $3/$15 | 18m05s | |||
13 | 41 / 59 | $4.06 | $5/$25 | 10m59s | |||
14 | 33 / 59 | $0.37 | $1/$5 | 3m50s |
Key Takeaways
- Claude Fable 5, GLM 5.2 (Fireworks), GPT 5.5, and GPT-5.6 Luna with Mini-SWE-agent each resolve 45 tasks, one behind GPT-5.6 Terra.
- GPT-5.6 Luna with Mini-SWE-agent has the lowest cost per test among the 45-task results at $0.12; Gemini 3.1 Pro Preview (02/26) is fastest among the 43-task results at 159.84 seconds.
- GPT-5.6 Terra with Mini-SWE-agent is also faster than every other result resolving at least 44 tasks, at 162.20 seconds.
Model Comparison
Accuracy
77.97%
GPT-5.6 Terra
76.27%
GPT-5.6 Luna
Task outcomes
59 tasks
Cost / test
$0.49
GPT-5.6 Terra
$0.12
GPT-5.6 Luna
Cost distribution
Latency
2m 42s
GPT-5.6 Terra
4m 27s
GPT-5.6 Luna
Latency distribution
Cost Analysis
Average Token Use / Test
Cost is the clearest tradeoff in this comparison. GPT-5.6 Terra leads at 77.97% for $0.49 per test. GPT-5.6 Luna is the lower-cost option at 76.27% for $0.12 per test.
Latency Analysis
Average Response Time / Test
Latency separates several models with similarly strong scores. GPT-5.6 Terra leads at 77.97%, while Gemini 3.1 Pro Preview (02/26) is fastest at 2m 40s with 72.88% accuracy.
Tasks with failures
| Models | |||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| GPT-5.6 Terra | |||||||||||||||||||||||||||||
| GPT-5.6 Luna | |||||||||||||||||||||||||||||
| GLM 5.2 | |||||||||||||||||||||||||||||
| GPT 5.5 | |||||||||||||||||||||||||||||
| Claude Fable 5 | |||||||||||||||||||||||||||||
| GPT-5.6 Sol | |||||||||||||||||||||||||||||
| Gemini 3.1 Pro Preview (02/26) | |||||||||||||||||||||||||||||
| Grok 4.5 | |||||||||||||||||||||||||||||
| Claude Opus 4.8 | |||||||||||||||||||||||||||||
| Kimi K3 | |||||||||||||||||||||||||||||
| Gemini 3.5 Flash | |||||||||||||||||||||||||||||
| Claude Sonnet 5 | |||||||||||||||||||||||||||||
| Claude Opus 4.7 | |||||||||||||||||||||||||||||
| Claude Haiku 4.5 (Nonthinking) |
Task detail
f8eac99Issue statement
HiCache reports two Prometheus metrics for KV-cache load-back operations: the number of tokens moved from host (CPU) memory back to device (GPU) memory (sglang:load_back_num_tokens) and the time each load-back takes (sglang:load_back_duration_seconds).
Today the duration is measured on the host with time.perf_counter() around the code that enqueues the asynchronous host-to-device copy. Because the copy runs on a separate device stream and completes later, this wall-clock span only covers the time spent submitting the work, not the time the transfer actually takes. The reported duration is therefore misleading and the token counter is updated at enqueue time rather than when the load actually completes.
The load-back duration should instead be measured on the device around the actual copy, and both metrics should be recorded when the load-back operation is confirmed complete (i.e. when its completion event is processed), not when it is submitted.
Requirements:
-
Add a helper in
sglang/srt/managers/cache_controller.pythat creates a pair of device timing events for a load-back operation:- A cached predicate
_timing_events_supported()that returns whether the active device backend'sEventtype acceptsenable_timing=True. It must probe by constructingdevice_module.Event(enable_timing=True)and returnFalse(logging a warning) when that raisesTypeErrororNotImplementedError, andTrueotherwise. The result must be cached so the probe runs at most once, and the cache must be clearable. - A function
make_timing_event_pair()that returns a 3-tuple(start_event, finish_event, timing_enabled). When timing is supported both events are created withenable_timing=True; otherwise they are created with no timing kwargs. The two returned events must be distinct objects, andtiming_enabledmust reflect whether timing is supported.
- A cached predicate
-
Extend the
HiCacheAckrecord with two new fields carrying the completion information for a load-back:num_tokens(defaulting to0) andtiming_enabled(defaulting toFalse). Existing construction ofHiCacheAckwith onlystart_event,finish_event, andnode_idsmust continue to work. -
Record the load-back metrics when completed load acks are processed in
HiRadixCache.loading_check()(rather than at submission time). For each completed ack, and only when a metrics collector is present, always increment the load-back token counter by the ack'snum_tokens. Additionally, when the ack has timing enabled, compute the elapsed device time between its start and finish events (start_event.elapsed_time(finish_event), which returns milliseconds) and observe the load-back duration in seconds. When timing is not enabled, the duration must not be observed and the events' elapsed time must not be queried.
The submission-time time.perf_counter() based duration/token bookkeeping for load-back should be removed so the metrics are only recorded on completion.
View Hidden Tests
diff --git a/test/registered/unit/mem_cache/test_hicache_load_back_metrics.py b/test/registered/unit/mem_cache/test_hicache_load_back_metrics.pynew file mode 100644index 0000000..a4333b5--- /dev/null+++ b/test/registered/unit/mem_cache/test_hicache_load_back_metrics.py@@ -0,0 +1,152 @@+"""Unit tests for the HiCache load-back duration/token metrics.++These tests exercise the device-event based instrumentation that HiCache uses to+report how long a KV-cache load-back took and how many tokens it moved. They run+on any backend (including CPU-only environments) by using mock device events, so+they do not require a real GPU.+"""++import unittest+from types import SimpleNamespace+from unittest.mock import MagicMock, patch+++class TestMakeTimingEventPair(unittest.TestCase):+ def setUp(self):+ from sglang.srt.managers import cache_controller as cc++ cc._timing_events_supported.cache_clear()+ self.cc = cc++ def tearDown(self):+ self.cc._timing_events_supported.cache_clear()++ def test_timing_supported_uses_enable_timing(self):+ created = []++ def create_event(*, enable_timing=False):+ created.append(enable_timing)+ return MagicMock()++ with patch.object(self.cc.device_module, "Event", side_effect=create_event):+ self.cc._timing_events_supported.cache_clear()+ start, finish, timing_enabled = self.cc.make_timing_event_pair()++ self.assertTrue(timing_enabled)+ self.assertIsNot(start, finish)+ # Every event (probe + the returned pair) was created with enable_timing.+ self.assertTrue(created and all(created))++ def test_timing_fallback_uses_dedicated_events(self):+ events = []++ def create_event(*, enable_timing=False):+ if enable_timing:+ raise TypeError("Event() takes no arguments")+ event = MagicMock()+ events.append(event)+ return event++ with patch.object(self.cc.device_module, "Event", side_effect=create_event):+ self.cc._timing_events_supported.cache_clear()+ start, finish, timing_enabled = self.cc.make_timing_event_pair()++ self.assertFalse(timing_enabled)+ self.assertIs(start, events[0])+ self.assertIs(finish, events[1])+ self.assertIsNot(start, finish)+++class TestHiCacheAckFields(unittest.TestCase):+ def test_num_tokens_and_timing_defaults(self):+ from sglang.srt.managers.cache_controller import HiCacheAck++ ack = HiCacheAck(start_event=None, finish_event=None, node_ids=[1])+ self.assertEqual(ack.num_tokens, 0)+ self.assertFalse(ack.timing_enabled)++ def test_fields_carry_supplied_values(self):+ from sglang.srt.managers.cache_controller import HiCacheAck++ ack = HiCacheAck(+ start_event=None,+ finish_event=None,+ node_ids=[3, 4],+ num_tokens=128,+ timing_enabled=True,+ )+ self.assertEqual(ack.num_tokens, 128)+ self.assertTrue(ack.timing_enabled)+++def _make_load_stub(ack):+ return SimpleNamespace(+ cache_controller=SimpleNamespace(ack_load_queue=[ack]),+ ongoing_load_back={i: object() for i in ack.node_ids},+ dec_lock_ref=MagicMock(),+ metrics_collector=MagicMock(),+ pp_rank=0,+ _all_reduce=MagicMock(),+ )+++class TestLoadingCheckMetrics(unittest.TestCase):+ def test_loading_check_records_duration_and_tokens(self):+ from sglang.srt.managers.cache_controller import HiCacheAck+ from sglang.srt.mem_cache.hiradix_cache import HiRadixCache++ finish_event = MagicMock()+ finish_event.query.return_value = True+ start_event = MagicMock()+ start_event.elapsed_time.return_value = 2000.0 # milliseconds++ ack = HiCacheAck(+ start_event=start_event,+ finish_event=finish_event,+ node_ids=[1, 2],+ num_tokens=1024,+ timing_enabled=True,+ )+ stub = _make_load_stub(ack)++ HiRadixCache.loading_check(stub)++ stub.metrics_collector.increment_load_back_num_tokens.assert_called_once_with(+ 1024+ )+ stub.metrics_collector.observe_load_back_duration.assert_called_once()+ (observed,), _ = stub.metrics_collector.observe_load_back_duration.call_args+ # Duration is reported in seconds (2000 ms -> 2.0 s).+ self.assertAlmostEqual(observed, 2.0)+ self.assertEqual(stub.cache_controller.ack_load_queue, [])+ self.assertEqual(stub.ongoing_load_back, {})++ def test_loading_check_skips_duration_when_timing_unsupported(self):+ from sglang.srt.managers.cache_controller import HiCacheAck+ from sglang.srt.mem_cache.hiradix_cache import HiRadixCache++ finish_event = MagicMock()+ finish_event.query.return_value = True+ start_event = MagicMock()++ ack = HiCacheAck(+ start_event=start_event,+ finish_event=finish_event,+ node_ids=[7],+ num_tokens=512,+ timing_enabled=False,+ )+ stub = _make_load_stub(ack)++ HiRadixCache.loading_check(stub)++ stub.metrics_collector.increment_load_back_num_tokens.assert_called_once_with(+ 512+ )+ stub.metrics_collector.observe_load_back_duration.assert_not_called()+ start_event.elapsed_time.assert_not_called()+ self.assertEqual(stub.cache_controller.ack_load_queue, [])+++if __name__ == "__main__":+ unittest.main()