Skip to content

Commit a9af1c4

Browse files
authored
Merge pull request #17 from RustedBytes/clean-api
clean up
2 parents 037dd70 + 54eb793 commit a9af1c4

6 files changed

Lines changed: 47 additions & 92 deletions

File tree

README.md

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@ Or manage the session manually:
236236
```python
237237
import rsloop
238238

239-
rsloop.start_profiler(frequency=999)
239+
rsloop.start_profiler()
240240
try:
241241
rsloop.run(main())
242242
finally:
@@ -247,10 +247,6 @@ This starts a Tracy client inside the process. Build a release binary, open
247247
`tracy-profiler.exe`, then connect to the running process while the profiled
248248
code is executing.
249249

250-
The `frequency` argument is accepted for compatibility with the old `pprof`
251-
integration but is ignored by Tracy. `path` / `format` are also retained as
252-
compatibility arguments on `stop_profiler()` / `profile()` and are ignored.
253-
254250
The current Tracy feature set is aimed at local Windows profiling:
255251
`enable`, `only-localhost`, `sampling`, and `flush-on-exit`. The last one helps
256252
short-lived runs flush data before exit.

benchmarks/README.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ uv run --with uvloop python benchmarks/compare_event_loops.py \
3333
```
3434

3535
To launch an unmeasured Tracy session for each `rsloop` workload before the
36-
measured runs, add a profile directory placeholder:
36+
measured runs, add a label directory:
3737

3838
```bash
3939
uv run --with maturin maturin develop --release --features profiler
@@ -43,9 +43,10 @@ uv run --with uvloop python benchmarks/compare_event_loops.py \
4343
--profile-rsloop-dir benchmarks/profiles
4444
```
4545

46-
No files are written by Tracy. The directory argument is only used to label the
47-
unmeasured profiling pass before the warmup and measured runs. Open the Tracy
48-
desktop profiler and connect while that pass is running.
46+
No files are written by Tracy. The directory argument is only used to derive a
47+
human-readable label for the unmeasured profiling pass before the warmup and
48+
measured runs. Open the Tracy desktop profiler and connect while that pass is
49+
running.
4950

5051
The runner executes each loop/workload in a fresh subprocess and reports:
5152

benchmarks/compare_event_loops.py

Lines changed: 17 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
import subprocess
1414
import sys
1515
import time
16-
from dataclasses import dataclass
16+
from dataclasses import dataclass, replace
1717
from typing import Callable
1818

1919

@@ -122,16 +122,10 @@ def parse_args() -> argparse.Namespace:
122122
default=None,
123123
help="Optional directory placeholder used to label one rsloop Tracy run per workload before measured runs",
124124
)
125-
parser.add_argument(
126-
"--profile-frequency",
127-
type=int,
128-
default=999,
129-
help="Compatibility option retained from the old pprof profiler; ignored by Tracy",
130-
)
131125
parser.add_argument("--child", action="store_true", help=argparse.SUPPRESS)
132126
parser.add_argument("--loop", choices=LOOP_CHOICES, help=argparse.SUPPRESS)
133127
parser.add_argument("--workload", choices=WORKLOAD_CHOICES, help=argparse.SUPPRESS)
134-
parser.add_argument("--profile-output", help=argparse.SUPPRESS)
128+
parser.add_argument("--profile-label", help=argparse.SUPPRESS)
135129
return parser.parse_args()
136130

137131

@@ -160,8 +154,6 @@ def validate_args(args: argparse.Namespace) -> None:
160154
raise SystemExit("--tcp-roundtrips must be > 0")
161155
if args.payload_size <= 0:
162156
raise SystemExit("--payload-size must be > 0")
163-
if args.profile_frequency <= 0:
164-
raise SystemExit("--profile-frequency must be > 0")
165157

166158

167159
def env_flag(name: str) -> bool:
@@ -421,12 +413,12 @@ def child_main(args: argparse.Namespace) -> int:
421413
else: # pragma: no cover - parser guards this
422414
raise AssertionError(f"unsupported workload: {args.workload}")
423415

424-
profile_requested = args.profile_output is not None or (
416+
profile_requested = args.profile_label is not None or (
425417
args.loop == "rsloop" and env_flag(RSLOOP_PROFILE_ENV)
426418
)
427419
loop_factory_for(args.loop)
428420
baseline_rss_bytes = get_current_rss_bytes()
429-
if profile_requested and not args.profile_output:
421+
if profile_requested and not args.profile_label:
430422
print(
431423
f"[profile] Tracy enabled via {RSLOOP_PROFILE_ENV}=1 for {args.loop}/{args.workload}",
432424
flush=True,
@@ -435,29 +427,23 @@ def child_main(args: argparse.Namespace) -> int:
435427
if args.loop != "rsloop":
436428
raise RuntimeError("profiling is only supported for rsloop")
437429
rsloop = importlib.import_module("rsloop")
438-
with rsloop.profile(args.profile_output, frequency=args.profile_frequency):
430+
if args.profile_label:
431+
print(f"[profile] Tracy session label: {args.profile_label}", flush=True)
432+
with rsloop.profile():
439433
result = run_with_loop(args.loop, coro)
440434
else:
441435
result = run_with_loop(args.loop, coro)
442436
finally:
443437
gc.enable()
444438

445-
result = ChildResult(
446-
loop=result.loop,
447-
workload=result.workload,
448-
seconds=result.seconds,
449-
operations=result.operations,
439+
result = replace(
440+
result,
450441
baseline_rss_bytes=baseline_rss_bytes,
451442
peak_rss_bytes=get_peak_rss_bytes(),
452443
peak_rss_delta_bytes=0,
453444
)
454-
result = ChildResult(
455-
loop=result.loop,
456-
workload=result.workload,
457-
seconds=result.seconds,
458-
operations=result.operations,
459-
baseline_rss_bytes=result.baseline_rss_bytes,
460-
peak_rss_bytes=result.peak_rss_bytes,
445+
result = replace(
446+
result,
461447
peak_rss_delta_bytes=max(0, result.peak_rss_bytes - result.baseline_rss_bytes),
462448
)
463449

@@ -484,7 +470,7 @@ def run_child(
484470
workload: str,
485471
args: argparse.Namespace,
486472
*,
487-
profile_output: str | None = None,
473+
profile_label: str | None = None,
488474
) -> ChildResult:
489475
cmd = [
490476
sys.executable,
@@ -504,11 +490,9 @@ def run_child(
504490
str(args.tcp_roundtrips),
505491
"--payload-size",
506492
str(args.payload_size),
507-
"--profile-frequency",
508-
str(args.profile_frequency),
509493
]
510-
if profile_output is not None:
511-
cmd.extend(["--profile-output", profile_output])
494+
if profile_label is not None:
495+
cmd.extend(["--profile-label", profile_label])
512496
env = os.environ.copy()
513497
if loop_name == "rsloop":
514498
env["RSLOOP_USE_FAST_STREAMS"] = "1" if args.rsloop_fast_streams else "0"
@@ -662,16 +646,14 @@ def parent_main(args: argparse.Namespace) -> int:
662646
print(f"Running {workload} on {loop_name}...")
663647
if profile_rsloop_dir and loop_name == "rsloop":
664648
os.makedirs(profile_rsloop_dir, exist_ok=True)
665-
profile_output = os.path.join(
666-
profile_rsloop_dir, f"rsloop-{workload}.svg"
667-
)
668-
print(f" starting Tracy session labeled {profile_output}")
649+
profile_label = os.path.join(profile_rsloop_dir, f"rsloop-{workload}")
650+
print(f" starting Tracy session labeled {profile_label}")
669651
run_child(
670652
script_path,
671653
loop_name,
672654
workload,
673655
args,
674-
profile_output=profile_output,
656+
profile_label=profile_label,
675657
)
676658
for _ in range(args.warmups):
677659
run_child(script_path, loop_name, workload, args)

justfile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,9 @@ set shell := ["bash", "-euo", "pipefail", "-c"]
33
tls-test-certs outdir="tests/fixtures/tls":
44
./scripts/generate-test-tls-certs.sh {{outdir}}
55

6+
fmt:
7+
uv run ruff format .
8+
cargo fmt --all
9+
610
test:
711
uv run python -m unittest discover -s tests

python/rsloop/__init__.py

Lines changed: 11 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -105,32 +105,24 @@ def profiler_running() -> bool:
105105
return __profiler_running()
106106

107107

108-
def start_profiler(*, frequency: int = 999) -> None:
109-
__start_profiler(frequency=frequency)
108+
def start_profiler() -> None:
109+
"""Start a Tracy profiling session."""
110+
__start_profiler()
110111

111112

112-
def stop_profiler(
113-
path: str | __os.PathLike[str] | None = None,
114-
*,
115-
format: str | None = None,
116-
) -> str | None:
117-
resolved = None if path is None else __os.fspath(path)
118-
return __stop_profiler(resolved, format=format)
113+
def stop_profiler() -> None:
114+
"""Stop the active Tracy profiling session."""
115+
__stop_profiler()
119116

120117

121118
@__contextlib.contextmanager
122-
def profile(
123-
path: str | __os.PathLike[str] | None = None,
124-
*,
125-
frequency: int = 999,
126-
format: str | None = None,
127-
) -> __typing.Iterator[str | None]:
128-
resolved_path = None if path is None else __os.fspath(path)
129-
start_profiler(frequency=frequency)
119+
def profile() -> __typing.Iterator[None]:
120+
"""Context manager wrapper around ``start_profiler()`` / ``stop_profiler()``."""
121+
start_profiler()
130122
try:
131-
yield resolved_path
123+
yield None
132124
finally:
133-
stop_profiler(resolved_path, format=format)
125+
stop_profiler()
134126

135127

136128
__ORIG_OPEN_CONNECTION = __asyncio.open_connection

src/profiler.rs

Lines changed: 9 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -21,31 +21,15 @@ mod imp {
2121
ACTIVE_PROFILER.get_or_init(|| Mutex::new(None))
2222
}
2323

24-
fn ensure_ignored_format(format: Option<&str>) -> PyResult<()> {
25-
if let Some(format) = format {
26-
return Err(PyValueError::new_err(format!(
27-
"Tracy does not support file output formats; got format={format:?}"
28-
)));
29-
}
30-
Ok(())
31-
}
32-
33-
#[pyfunction(signature = (*, frequency = 999))]
34-
pub fn start_profiler(frequency: i32) -> PyResult<()> {
35-
if frequency <= 0 {
36-
return Err(PyValueError::new_err(
37-
"profiler frequency must be greater than zero",
38-
));
39-
}
40-
24+
#[pyfunction]
25+
pub fn start_profiler() -> PyResult<()> {
4126
let mut active = active_profiler()
4227
.lock()
4328
.map_err(|_| PyRuntimeError::new_err("profiler state mutex is poisoned"))?;
4429
if active.is_some() {
4530
return Err(PyRuntimeError::new_err("profiler is already running"));
4631
}
4732

48-
let _ = frequency;
4933
let client = Client::start();
5034
client.set_thread_name("python-main");
5135
let session_span = client.clone().span_alloc(
@@ -70,10 +54,8 @@ mod imp {
7054
.unwrap_or(false)
7155
}
7256

73-
#[pyfunction(signature = (path = None, *, format = None))]
74-
pub fn stop_profiler(path: Option<String>, format: Option<&str>) -> PyResult<Option<String>> {
75-
ensure_ignored_format(format)?;
76-
57+
#[pyfunction]
58+
pub fn stop_profiler() -> PyResult<()> {
7759
let active = active_profiler()
7860
.lock()
7961
.map_err(|_| PyRuntimeError::new_err("profiler state mutex is poisoned"))?
@@ -83,7 +65,7 @@ mod imp {
8365
slot.borrow_mut().take();
8466
});
8567
drop(active.client);
86-
Ok(path)
68+
Ok(())
8769
}
8870
}
8971

@@ -100,15 +82,13 @@ mod imp {
10082
false
10183
}
10284

103-
#[pyfunction(signature = (*, frequency = 999))]
104-
pub fn start_profiler(frequency: i32) -> PyResult<()> {
105-
let _ = frequency;
85+
#[pyfunction]
86+
pub fn start_profiler() -> PyResult<()> {
10687
Err(PyRuntimeError::new_err(PROFILER_DISABLED_MESSAGE))
10788
}
10889

109-
#[pyfunction(signature = (path = None, *, format = None))]
110-
pub fn stop_profiler(path: Option<String>, format: Option<&str>) -> PyResult<Option<String>> {
111-
let _ = (path, format);
90+
#[pyfunction]
91+
pub fn stop_profiler() -> PyResult<()> {
11292
Err(PyRuntimeError::new_err(PROFILER_DISABLED_MESSAGE))
11393
}
11494
}

0 commit comments

Comments
 (0)