Haiku BStopWatch: Measurement Scope, Laps, and Benchmark Limits
Use Haiku BStopWatch for quick elapsed-time checks while accounting for microseconds, destructor output, lap limits, and profiling bias.
BStopWatch is a lightweight Support Kit utility for measuring elapsed time around a piece of code. It starts when constructed, can be suspended and resumed, reports elapsed time in microseconds, and can record lap values. It is convenient for quick diagnostics and development measurements. It is not a sampling profiler, a production telemetry pipeline, a statistical benchmark harness, or proof that a measured path will meet a deadline under load.
Its behavior has consequences for how you use it. Unless constructed with silent=true, the destructor writes timing output to standard output. The current implementation supports at most ten lap entries, and Lap() returns the time since the watch was started rather than since the previous lap. It does not expose an API for retrieving all lap values as a structured result. These details make it useful for short manual experiments, but unsuitable as an invisible library instrument.
Measure a bounded scope
Place the watch around the specific operation you want to observe. Use a stable name for the measurement and make the silent flag explicit in production code so scope exit does not unexpectedly write to stdout.
bigtime_t elapsed = 0;
{
BStopWatch watch("decode one frame", true);
status_t status = decoder.DecodeNextFrame();
if (status != B_OK)
return status;
elapsed = watch.ElapsedTime();
}
// elapsed is in microseconds.
The destructor ends the scope, but it does not provide a structured result unless output is enabled. If the result must be reported, read ElapsedTime() before destruction and send the value through your application’s metrics or logging path. Keep the measured region free of unrelated work such as UI refresh, file dialogs, and diagnostic printing.
The value is in microseconds. Convert only at presentation boundaries and label the unit. A value of 5000 means five milliseconds, not five seconds. Avoid implicit conversion to float for high-resolution comparisons; preserve the integer bigtime_t value until the presentation layer.
Understand suspend, resume, reset, and lap behavior
Suspend() stops accumulation until Resume(). Use it only when the paused interval should be excluded from the total. If you suspend around I/O, the resulting measurement no longer describes end-to-end latency; it describes active sections excluding the pause. Record that distinction in the metric name.
Resume() restarts the timer after a suspended state. Reset() clears elapsed time and lap data and restarts timing. Do not use reset as a lap boundary if you need a total across phases; preserve the phase measurements separately. Make state transitions explicit in tests by exercising repeated suspend/resume and reset calls.
Lap() stores timing for diagnostic printing, but current documentation says only ten lap values are supported. Additional calls overwrite the last stored value, and the values are not exposed through a getter. A call while suspended returns zero. The returned value is measured from the watch’s start point, not from the previous lap. If your benchmark needs a per-iteration duration, subtract consecutive cumulative readings or use a purpose-built measurement loop.
Avoid destructor output as a data channel
When silent is false, the destructor streams timing information to standard output. That can alter measured latency, interleave with other threads, pollute a command’s machine-readable stdout, and make tests order-dependent. In a GUI app, stdout may not be visible to the user at all. Use silent mode when output is not intended, then report the value through an explicit logger or metric sink after timing has stopped.
Even with silent mode, construction and measurement have overhead. Keep instrumentation outside extremely short operations when that overhead would dominate. If you compare two functions that each run in nanoseconds, the watch’s own calls and scheduler noise can overwhelm the difference. Increase the workload, repeat measurements, and use a profiler designed for the question.
Separate elapsed time from performance diagnosis
One duration cannot explain why a path is slow. It cannot attribute CPU time to a function, distinguish waiting from computation, or show allocation and lock contention. Use the native sampling profiler to understand where time is spent, then use a stopwatch or a dedicated benchmark to compare controlled changes. Keep the profiler and stopwatch evidence distinct in reports.
For benchmark-quality results, warm up caches and runtime state, execute enough iterations to amortize setup, record the build revision and machine, and report distributions rather than only the fastest run. Include a baseline and test under representative system load. Do not present one local measurement as a universal Haiku performance guarantee.
Separate the measurement setup from the operation. If a loop includes allocation, file reads, or view creation, the result describes that complete workflow, not the function name written in a log. If the operation is stateful, reset it consistently before each run or measure independent inputs. Randomize or alternate comparisons when system temperature, background work, or cache state could bias one implementation.
Use multiple observations and report at least a median and a tail percentile when the application cares about user-visible pauses. A mean can conceal a small number of long stalls, and a minimum is especially optimistic. Keep raw samples during investigation so a regression can be traced to a workload or machine rather than summarized away.
If the measured operation can fail or early-return, decide whether failed attempts belong in the latency metric. In the example above, the function returns before copying the elapsed value, so a caller never observes a failed decode duration. If failures matter operationally, capture status and duration separately and report both.
Keep timing scopes thread-confined
The public interface does not define a multi-threaded shared-watch protocol. Treat each watch as owned by one thread and one measurement scope unless you have verified stronger behavior in the target implementation. Do not pass a stack watch pointer to a worker that may outlive the scope. For asynchronous work, create the watch in the worker or instrument the enqueue and completion points as separate measurements.
Nested watches can help isolate setup and execution, but destructor output order follows object destruction order. With output enabled, nested lines can be confusing and may not match the order you expected from the source. Prefer explicit metric names and capture results into structured variables when nesting.
Acceptance checks
Test a watch that runs normally, one that is suspended and resumed, reset during a scope, ten or more laps, a lap while suspended, and both silent settings. Verify units and reported values with a known sleep or bounded operation, but account for scheduler granularity. Confirm that benchmark output does not contaminate stdout consumed by scripts.
Test measurement code with a very short scope and with a deliberately slower scope to estimate instrumentation overhead. Repeat after a system idle interval and while representative background jobs are active. If the measured difference is smaller than ordinary run-to-run variance, report the result as inconclusive rather than claiming one implementation is faster.
Preserve the full bigtime_t resolution in diagnostic records and include the measurement name as a separate field. Avoid formatting each sample to text inside the timed region. If a single run crosses process or machine boundaries, record those boundaries explicitly instead of trying to compare raw elapsed values from different clocks.
For production telemetry, pair duration with operation outcome, workload size, build revision, and relevant system context. Keep high-cardinality names and per-file paths out of metric labels. A useful duration is only actionable if the application can relate it to a concrete operation without leaking user data.
BStopWatch is a practical timing aid for local investigation. Use a profiler for attribution and a controlled benchmark for performance claims; do not treat a stopwatch sample as a complete explanation.
Related:
- Profile Haiku Applications with the Native Sampling Profiler
- Haiku’s Scheduler: Priorities, Real-Time Work, and Responsive Multimedia
Sources: