# Guava's Stopwatch for Accurate Benchmarking and Timing: A Complete Guide

> Master Guava's Stopwatch for precise benchmarking and timing. Learn to measure elapsed time effectively with System.nanoTime or a custom Ticker for efficient logging and metrics. Get started now!

- Repository: [Google/guava](https://github.com/google/guava)
- Tags: tutorial
- Published: 2026-08-08

---

**Guava's `Stopwatch` provides a lightweight, process-local utility for measuring elapsed time using `System.nanoTime` or a custom `Ticker`, offering a readable API for logging and metrics without thread-safety overhead.**

If you need to measure code execution time in Java, the Google Guava library offers a purpose-built solution that abstracts away the complexity of raw nanosecond arithmetic. This article explores how `Stopwatch` in `google/guava` delivers accurate benchmarking capabilities while remaining simple to test and integrate into production code.

## How Stopwatch Works Under the Hood

The implementation in [`guava/src/com/google/common/base/Stopwatch.java`](https://github.com/google/guava/blob/main/guava/src/com/google/common/base/Stopwatch.java) centers on three core fields: `isRunning`, `elapsedNanos`, and `startTick`. This design tracks accumulated time across multiple start/stop cycles while delegating actual time measurement to a pluggable `Ticker` abstraction.

### Time Source Abstraction with Ticker

By default, `Stopwatch` uses `Ticker.systemTicker()`, which wraps `System.nanoTime()`. However, the constructor accepts any `Ticker` implementation, enabling deterministic testing and platform-specific customization. The `read()` method on the ticker returns a `long` value representing ticks, which `Stopwatch` converts to meaningful durations via `elapsed(TimeUnit)` or `elapsed()` (returning `java.time.Duration` on non-J2KT platforms).

### State Management and Lifecycle

The class maintains strict state transitions through four primary operations:

1. **Construction** – `Stopwatch.createUnstarted()` or `Stopwatch.createStarted()` optionally accepts a custom `Ticker`
2. **Start** – `start()` records the current tick and sets `isRunning = true`
3. **Stop** – `stop()` computes `tick - startTick`, adds the delta to `elapsedNanos`, and clears the running flag
4. **Reset** – `reset()` clears accumulated time and forces the stopwatch into a stopped state

Mutating methods are intentionally **not idempotent**. Calling `start()` on an already running stopwatch throws `IllegalStateException`, preventing accidental measurement corruption.

### Thread Safety Design

`Stopwatch` is deliberately **not thread-safe**. According to the source code, callers must confine instances to a single thread to avoid synchronization overhead. This design choice keeps the implementation lightweight and suitable for high-frequency logging scenarios where lock contention would degrade performance.

## Core API Methods for Timing Operations

Beyond basic start/stop functionality, `Stopwatch` provides several utilities for converting and displaying elapsed time.

### Duration Conversion

The `elapsed(TimeUnit)` method returns a truncated `long` value in the requested unit, while `elapsed()` returns a full-precision `Duration` object. Both methods calculate total elapsed time by summing `elapsedNanos` with the current delta if the stopwatch is running.

### Human-Readable String Output

The `toString()` method automatically selects an appropriate time unit via the private `chooseUnit()` method, formats the value using `Platform.formatCompact4Digits`, and appends a compact abbreviation (`ns`, `μs`, `ms`, `s`, `min`, `h`, `d`). This produces readable output like `"12.3 ms"` without manual formatting code.

## Practical Examples for Benchmarking

### Basic Code Block Timing

For simple timing scenarios, create a started stopwatch, execute your code, and stop:

```java
Stopwatch sw = Stopwatch.createStarted();
doWork();  // Your method to benchmark
sw.stop();
System.out.println("Elapsed: " + sw);  // e.g., "45.2 ms"

```

### Reusing a Stopwatch for Multiple Intervals

You can accumulate timing across multiple operations by starting and stopping without resetting:

```java
Stopwatch sw = Stopwatch.createUnstarted();

sw.start();
taskA();
sw.stop();
System.out.println("Task A: " + sw.elapsed(TimeUnit.MILLISECONDS) + " ms");

sw.start();  // Resumes from previous elapsed time
taskB();
sw.stop();
System.out.println("Total (A+B): " + sw.elapsed(TimeUnit.MILLISECONDS) + " ms");

```

### Testing with FakeTicker

The test suite in [`guava-tests/test/com/google/common/base/StopwatchTest.java`](https://github.com/google/guava/blob/main/guava-tests/test/com/google/common/base/StopwatchTest.java) demonstrates deterministic testing using `FakeTicker` from `guava-testlib`:

```java
Ticker fake = new FakeTicker();
Stopwatch sw = new Stopwatch(fake);

sw.start();
((FakeTicker) fake).advance(500_000_000L);  // Advance 0.5 seconds
sw.stop();

assert sw.elapsed(TimeUnit.MILLISECONDS) == 500;

```

This approach eliminates flakiness caused by system load or clock granularity during test execution.

### Android-Specific Timing

On Android devices, the default ticker may pause during sleep. The source documentation recommends using `android.os.SystemClock.elapsedRealtimeNanos()`:

```java
Stopwatch sw = Stopwatch.createStarted(
    new Ticker() {
      @Override public long read() {
        return android.os.SystemClock.elapsedRealtimeNanos();
      }
    });

```

This ensures continuous measurement regardless of device sleep state.

## Performance Characteristics and Limitations

While `System.nanoTime` offers sub-nanosecond resolution, the `Stopwatch` class adds allocation and method call overhead. The [`StopwatchBenchmark.java`](https://github.com/google/guava/blob/main/StopwatchBenchmark.java) file in `guava-tests/benchmark/` measures this overhead across creation, starting, stopping, and resetting operations.

For ultra-high-precision profiling (e.g., method-level hot spot analysis), specialized profilers outperform `Stopwatch`. However, for application-level logging, metrics collection, and macro-benchmarking, the overhead is negligible compared to the readability and maintainability benefits.

## Summary

- **`Stopwatch`** in `google/guava` wraps `System.nanoTime` via a `Ticker` abstraction to provide readable, testable timing utilities.
- The class is **not thread-safe** by design, optimizing for single-threaded performance without synchronization overhead.
- **State management** tracks `isRunning`, `elapsedNanos`, and `startTick` to support multiple start/stop cycles.
- **Testing** is simplified through `FakeTicker`, allowing deterministic control over time advancement.
- **Android** requires special handling using `SystemClock.elapsedRealtimeNanos()` to handle sleep states correctly.
- Use `toString()` for automatic human-readable formatting or `elapsed(TimeUnit)` for programmatic duration access.

## Frequently Asked Questions

### Is Guava's Stopwatch thread-safe?

No, `Stopwatch` is explicitly not thread-safe. The implementation in [`guava/src/com/google/common/base/Stopwatch.java`](https://github.com/google/guava/blob/main/guava/src/com/google/common/base/Stopwatch.java) omits synchronization to minimize overhead, requiring callers to confine instances to a single thread or provide external synchronization.

### How does Stopwatch handle time unit conversion?

The `elapsed(TimeUnit)` method converts accumulated nanoseconds to your specified unit, truncating fractional values. On platforms supporting J2KT-incompatible APIs, the parameterless `elapsed()` method returns a `java.time.Duration` preserving full precision without truncation.

### Can I use Stopwatch for micro-benchmarking?

While `Stopwatch` works for coarse-grained benchmarking, the allocation overhead and method call costs make it unsuitable for micro-benchmarking individual method invocations. For high-precision profiling, use dedicated tools like JMH, reserving `Stopwatch` for logging, metrics, and timing blocks of code longer than a few microseconds.

### How do I create a deterministic Stopwatch for unit tests?

Inject a `FakeTicker` from Guava's testlib when constructing the `Stopwatch`. The `FakeTicker.advance(long nanos)` method allows you to programmatically control time progression, enabling repeatable test assertions without depending on system clock behavior or `Thread.sleep()`.