`_generate_sample_rand` seeds a Mersenne Twister per Transaction, eagerly, even when unsampled (6.4 µs)
Nessuno ha ancora preso questa issue.
Valutazione
- Difficoltà
- 4/5
- Tempo stimato
- 3-5 giorni
- Idoneità per principianti
- 64/100
- Tipo di issue
- Refactoring
- Chiarezza
- Abbastanza chiara
- Stato di attività
- Attiva
- Stack tecnologico
- python
- Ambito
- performance
Direzione di ricerca
Inizia in sentry_sdk/tracing.py, in Transaction.init, e in sentry_sdk/tracing_utils.py, in _generate_sample_rand; esamina come viene letto _sample_rand e se è richiesta la compatibilità dell'output. Esegui il benchmark della issue per confrontare le alternative, quindi verifica che le transazioni non campionate evitino lavoro non necessario, preservando al contempo il comportamento di campionamento deterministico.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
Summary
Transaction.__init__ unconditionally computes _generate_sample_rand(self.trace_id), and _generate_sample_rand seeds a Mersenne Twister to produce a single float. That is 6.4 µs per call on CPython 3.14 / Apple M2, paid on every request through the ASGI integrations even when tracing is disabled and the value can never be used.
Two independent problems:
1. It is eager. sentry_sdk/tracing.py, Transaction.__init__:
baggage_sample_rand = None if self._baggage is None else self._baggage._sample_rand()
if baggage_sample_rand is not None:
self._sample_rand = baggage_sample_rand
else:
self._sample_rand = _generate_sample_rand(self.trace_id)
_sample_rand is only read when a sampling decision is actually made. With traces_sample_rate unset the transaction is never sampled, so this is pure waste. Making it a lazy property costs nothing.
2. It is expensive. sentry_sdk/tracing_utils.py:
def _generate_sample_rand(trace_id, *, interval=(0.0, 1.0)):
...
rng = Random(trace_id)
sample_rand_scaled = rng.randrange(lower_scaled, upper_scaled)
return sample_rand_scaled / 1_000_000
Random(seed) runs the full MT19937 init_by_array over a 625-word state. Measured with timeit, 50k iterations, best of 5:
| µs | |
|---|---|
Random(trace_id) (32-char hex string) |
6.39 |
Random(int(trace_id, 16)) |
5.90 |
Random(12345) |
5.89 |
int(trace_id, 16) / 2**128 |
0.23 |
The cost is the Mersenne Twister initialisation, not the string hashing - seeding with a small int is just as slow. Deriving a uniformly distributed value in [0, 1) arithmetically from the same trace id is 27x cheaper and just as deterministic.
Impact
On a do-nothing FastAPI endpoint with tracing disabled, making _generate_sample_rand cheap moves the SDK's per-request overhead from +61.3 µs to +53.3 µs (in-process measurement, baseline 15.6 µs/req) - about 13% of the SDK's cost, for a value that is discarded.
Questions
- Is the fix to
Transaction.__init__simply making_sample_randlazy? Happy to open a PR. - Is the exact output of
_generate_sample_randrequired to be bit-compatible across SDKs, or only to be deterministic-from-trace_idand uniformly distributed? If the latter,int(trace_id, 16) / 2**128(scaled into the requested interval) would be a drop-in replacement. If the former, the laziness fix alone still helps.
Repro
import timeit, uuid
from random import Random
tid = uuid.uuid4().hex
n = 50000
for label, fn in [
("Random(hex str)", lambda: Random(tid)),
("Random(int)", lambda: Random(int(tid, 16))),
("Random(12345)", lambda: Random(12345)),
("int(tid,16)/2**128", lambda: int(tid, 16) / 2**128),
]:
print(f"{label:<20} {min(timeit.repeat(fn, number=n, repeat=5)) / n * 1e6:.2f} us")
Environment: CPython 3.14.7, sentry-sdk 2.67.1, Apple M2.
Context: this was found while measuring 7400, where the discarded Transaction is the larger half of the same problem.
- Lingua principale
- Python
- Stelle
- 2.2k
- Fork
- 672
- Merge medio
- 23h 14m
- PR unite (30g)
- 218
Guida per i contributori
Apri la guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di getsentry/sentry-python
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
getsentry/sentry-python#7543 · 2 commenti · 1 assegnatario ·
-
Python
Difficoltà 2/5 1-3 ore Idoneità per principianti 68/100
getsentry/sentry-python#6992 · 1 commento ·
-
Improvement Python
Difficoltà 2/5 1-3 ore Idoneità per principianti 65/100
getsentry/sentry-python#6970 · 1 commento ·
-
Bug Python
Difficoltà 2/5 1-3 ore Idoneità per principianti 74/100
getsentry/sentry-python#6504 · 1 commento ·
-
Improvement Python Spans
Difficoltà 2/5 1-3 ore Idoneità per principianti 85/100
getsentry/sentry-python#5833 · 1 commento ·
Tutte le issue di getsentry/sentry-python
Issue simili
-
Add: hunch Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 74/100
AbdelStark/awesome-typesafe#104 ·
-
enhancement
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 68/100
DiamondLightSource/dodal#2211 ·
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 88/100
openml/openml-python#1749 ·
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 88/100
sipyourdrink-ltd/bernstein#6191 ·