or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

core-assertions.mdformat.mdgbytes.mdgcustom.mdgexec.mdghttp.mdgleak.mdgmeasure.mdgstruct.mdindex.mdmatchers.mdtypes-interfaces.md
tile.json

gmeasure.mddocs/

GMeasure Package

The gmeasure package provides support for benchmarking and measuring code performance. It is designed as a robust replacement for Ginkgo V1's Measure nodes and integrates seamlessly with Ginkgo V2's reporting infrastructure.

Import

import "github.com/onsi/gomega/gmeasure"

Overview

gmeasure is organized around the metaphor of an Experiment that records multiple Measurements. A Measurement is a named collection of data points supporting:

  • Duration measurements (type time.Duration)
  • Value measurements (type float64)
  • Notes (textual annotations)

Experiments allow recording measurements directly or measuring functions automatically. When measuring functions, experiments handle timing (for Duration measurements) and/or recording return values (for Value measurements). Experiments also support sampling - running functions repeatedly to gather statistical data.

Core Types

Experiment Type

The main type for conducting performance measurements.

type Experiment struct {
    Name         string
    Measurements Measurements
    // contains filtered or unexported fields
}

Constructor

func NewExperiment(name string) *Experiment

{ .api }

Creates a new experiment with the specified name.

Example:

experiment := gmeasure.NewExperiment("My Benchmark")
AddReportEntry(experiment.Name, experiment) // Register with Ginkgo

Recording Methods

RecordNote
func (e *Experiment) RecordNote(note string, args ...any)

{ .api }

Records a textual note to annotate the experiment. Notes appear in experiment reports.

Supported decorations: Style()

Example:

experiment.RecordNote("Beginning performance tests", gmeasure.Style("{{blue}}"))
RecordDuration
func (e *Experiment) RecordDuration(name string, duration time.Duration, args ...any)

{ .api }

Records a duration on a Duration Measurement with the given name. Creates the measurement if it doesn't exist.

Supported decorations: Style(), Precision(), Annotation()

Example:

experiment.RecordDuration("response time", 150*time.Millisecond,
    gmeasure.Annotation("first request"))
RecordValue
func (e *Experiment) RecordValue(name string, value float64, args ...any)

{ .api }

Records a float64 value on a Value Measurement with the given name. Creates the measurement if it doesn't exist.

Supported decorations: Style(), Units(), Precision(), Annotation()

Example:

experiment.RecordValue("throughput", 1250.5,
    gmeasure.Units("MB/s"),
    gmeasure.Precision(2))

Measurement Methods

MeasureDuration
func (e *Experiment) MeasureDuration(name string, callback func(), args ...any) time.Duration

{ .api }

Executes the callback and times its duration. Records the result and returns the measured duration.

Supported decorations: Style(), Precision(), Annotation()

Example:

duration := experiment.MeasureDuration("database query", func() {
    db.Query("SELECT * FROM users")
})
MeasureValue
func (e *Experiment) MeasureValue(name string, callback func() float64, args ...any) float64

{ .api }

Executes the callback and records its return value. Returns the measured value.

Supported decorations: Style(), Units(), Precision(), Annotation()

Example:

value := experiment.MeasureValue("items processed", func() float64 {
    return float64(processItems())
}, gmeasure.Units("items"))

Sampling Methods

SampleDuration
func (e *Experiment) SampleDuration(name string, callback func(idx int),
    samplingConfig SamplingConfig, args ...any)

{ .api }

Samples the callback multiple times, timing each execution. The callback receives a zero-based index.

Supported decorations: Style(), Precision(), Annotation() (applied to all samples)

Example:

experiment.SampleDuration("api call", func(idx int) {
    makeAPICall()
}, gmeasure.SamplingConfig{N: 100})
SampleAnnotatedDuration
func (e *Experiment) SampleAnnotatedDuration(name string,
    callback func(idx int) Annotation, samplingConfig SamplingConfig, args ...any)

{ .api }

Samples the callback multiple times, timing each execution. The callback must return an Annotation for each sample.

Supported decorations: Style(), Precision()

Example:

experiment.SampleAnnotatedDuration("request", func(idx int) gmeasure.Annotation {
    status := makeRequest()
    return gmeasure.Annotation(fmt.Sprintf("status: %d", status))
}, gmeasure.SamplingConfig{N: 50})
SampleValue
func (e *Experiment) SampleValue(name string, callback func(idx int) float64,
    samplingConfig SamplingConfig, args ...any)

{ .api }

Samples the callback multiple times, recording each return value. The callback must return a float64.

Supported decorations: Style(), Units(), Precision(), Annotation() (applied to all samples)

Example:

experiment.SampleValue("memory usage", func(idx int) float64 {
    return float64(getCurrentMemoryMB())
}, gmeasure.SamplingConfig{N: 100}, gmeasure.Units("MB"))
SampleAnnotatedValue
func (e *Experiment) SampleAnnotatedValue(name string,
    callback func(idx int) (float64, Annotation), samplingConfig SamplingConfig, args ...any)

{ .api }

Samples the callback multiple times, recording each return value and annotation.

Supported decorations: Style(), Units(), Precision()

Example:

experiment.SampleAnnotatedValue("cpu usage", func(idx int) (float64, gmeasure.Annotation) {
    usage := getCPUUsage()
    annotation := fmt.Sprintf("cores: %d", runtime.NumCPU())
    return usage, gmeasure.Annotation(annotation)
}, gmeasure.SamplingConfig{Duration: 10*time.Second})
Sample
func (e *Experiment) Sample(callback func(idx int), samplingConfig SamplingConfig)

{ .api }

Generic sampling function that executes the callback repeatedly according to the sampling configuration. The callback receives a zero-based index.

Example:

experiment.Sample(func(idx int) {
    // Custom measurement logic
    duration := measureSomething()
    experiment.RecordDuration("custom", duration)
}, gmeasure.SamplingConfig{N: 100})

Query Methods

Get
func (e *Experiment) Get(name string) Measurement

{ .api }

Returns the Measurement with the specified name. Returns a zero-value Measurement{} if not found.

Example:

measurement := experiment.Get("response time")
if measurement.Type != gmeasure.MeasurementTypeInvalid {
    fmt.Println("Durations:", measurement.Durations)
}
GetStats
func (e *Experiment) GetStats(name string) Stats

{ .api }

Returns statistics for the Measurement with the specified name. Equivalent to experiment.Get(name).Stats().

Example:

stats := experiment.GetStats("response time")
fmt.Printf("Mean: %v\n", stats.DurationFor(gmeasure.StatMean))

Utility Methods

NewStopwatch
func (e *Experiment) NewStopwatch() *Stopwatch

{ .api }

Creates a stopwatch for manual timing operations tied to this experiment.

Example:

stopwatch := experiment.NewStopwatch()
// ... do work ...
stopwatch.Record("phase 1").Reset()
// ... do more work ...
stopwatch.Record("phase 2")
String
func (e *Experiment) String() string

{ .api }

Returns an unformatted summary of the experiment and all measurements.

ColorableString
func (e *Experiment) ColorableString() string

{ .api }

Returns a Ginkgo-formatted summary with styling. Called automatically by Ginkgo's reporting when registered via AddReportEntry.

Measurement Type

Represents a single named measurement with all captured data.

type Measurement struct {
    Type            MeasurementType
    ExperimentName  string
    Note            string
    Name            string
    Style           string
    Units           string
    PrecisionBundle PrecisionBundle
    Durations       []time.Duration
    Values          []float64
    Annotations     []string
}

Fields

  • Type: One of MeasurementTypeNote, MeasurementTypeDuration, or MeasurementTypeValue
  • ExperimentName: Name of the parent experiment
  • Note: Text content (for MeasurementTypeNote)
  • Name: Measurement name (for Duration/Value types)
  • Style: Styling information for reports
  • Units: Unit label ("duration" for Duration measurements)
  • PrecisionBundle: Display precision settings
  • Durations: All recorded durations (for Duration measurements)
  • Values: All recorded values (for Value measurements)
  • Annotations: Per-sample annotations

Methods

Stats
func (m Measurement) Stats() Stats

{ .api }

Calculates and returns statistics for this measurement.

String
func (m Measurement) String() string

{ .api }

Returns an unstyled report including all data points.

ColorableString
func (m Measurement) ColorableString() string

{ .api }

Returns a styled report. Called by Ginkgo when registered via AddReportEntry.

Stats Type

Statistical summary of a measurement.

type Stats struct {
    Type             StatsType
    ExperimentName   string
    MeasurementName  string
    Units            string
    Style            string
    PrecisionBundle  PrecisionBundle
    N                int
    ValueBundle      map[Stat]float64
    DurationBundle   map[Stat]time.Duration
    AnnotationBundle map[Stat]string
}

Fields

  • Type: StatsTypeValue or StatsTypeDuration
  • ExperimentName: Parent experiment name
  • MeasurementName: Parent measurement name
  • Units: Unit label
  • Style: Styling hint
  • PrecisionBundle: Display precision
  • N: Total number of data points
  • ValueBundle: Float statistics (min, max, mean, median, stddev)
  • DurationBundle: Duration statistics
  • AnnotationBundle: Annotations for min/max values

Methods

ValueFor
func (s Stats) ValueFor(stat Stat) float64

{ .api }

Returns the float64 value for a specific statistic. Use only with StatsTypeValue.

Example:

median := stats.ValueFor(gmeasure.StatMedian)
DurationFor
func (s Stats) DurationFor(stat Stat) time.Duration

{ .api }

Returns the duration for a specific statistic. Use only with StatsTypeDuration.

Example:

mean := stats.DurationFor(gmeasure.StatMean)
FloatFor
func (s Stats) FloatFor(stat Stat) float64

{ .api }

Returns a float64 representation regardless of type. For durations, returns float64(duration).

StringFor
func (s Stats) StringFor(stat Stat) string

{ .api }

Returns a formatted string representation honoring precision settings.

String
func (s Stats) String() string

{ .api }

Returns a summary in the format: "MIN < [MEDIAN] | <MEAN> ±STDDEV < MAX"

Measurements Type

Collection type for managing multiple measurements.

type Measurements []Measurement

Configuration Types

SamplingConfig

Configures sampling behavior for the Sample* family of methods.

type SamplingConfig struct {
    N                   int
    Duration            time.Duration
    MinSamplingInterval time.Duration
    NumParallel         int
}

{ .api }

Fields

  • N: Maximum number of samples to record
  • Duration: Maximum time to spend sampling
  • MinSamplingInterval: Minimum time between samples (cannot be used with NumParallel)
  • NumParallel: Number of parallel workers (cannot be used with MinSamplingInterval)

Notes:

  • At least one of N or Duration must be specified
  • Sampling ends when the first limit is reached
  • MinSamplingInterval and NumParallel are mutually exclusive

Examples:

// Sample exactly 100 times
config := gmeasure.SamplingConfig{N: 100}

// Sample for 10 seconds
config := gmeasure.SamplingConfig{Duration: 10*time.Second}

// Sample 100 times with at least 100ms between samples
config := gmeasure.SamplingConfig{N: 100, MinSamplingInterval: 100*time.Millisecond}

// Sample for 30 seconds using 4 parallel workers
config := gmeasure.SamplingConfig{Duration: 30*time.Second, NumParallel: 4}

Decorator Types

Decorators customize measurement recording by providing additional metadata.

Units

type Units string

Specifies units for value measurements (ignored for durations).

func Units(unit string) Units

{ .api }

Example:

experiment.RecordValue("bandwidth", 125.5, gmeasure.Units("MB/s"))

Note: Units are set only on the first recording of a measurement name.

Annotation

type Annotation string

Adds contextual information to individual data points.

func Annotation(text string) Annotation

{ .api }

Example:

experiment.RecordDuration("query", duration, gmeasure.Annotation("user lookup"))

Style

type Style string

Adds styling/coloring hints for console reports using Ginkgo's format strings.

func Style(style string) Style

{ .api }

Example:

experiment.RecordValue("errors", count, gmeasure.Style("{{red}}{{bold}}"))

Note: Style is set only on the first recording of a measurement name.

Precision

func Precision(p any) PrecisionBundle

{ .api }

Controls display precision for measurements.

Parameters:

  • int: Number of decimal places for value measurements (equivalent to "%.Nf")
  • time.Duration: Rounding precision for duration measurements

Examples:

// Show 2 decimal places for values
experiment.RecordValue("score", 3.14159, gmeasure.Precision(2)) // displays as "3.14"

// Round durations to nearest 100ms
experiment.RecordDuration("time", 3214*time.Millisecond,
    gmeasure.Precision(100*time.Millisecond)) // displays as "3.2s"

Note: Precision is set only on the first recording of a measurement name.

PrecisionBundle

type PrecisionBundle struct {
    Duration    time.Duration
    ValueFormat string
}

{ .api }

Stores precision settings for both duration and value measurements.

Default:

var DefaultPrecisionBundle = PrecisionBundle{
    Duration:    100 * time.Microsecond,
    ValueFormat: "%.3f",
}

Enum Types

MeasurementType

type MeasurementType uint

const (
    MeasurementTypeInvalid  MeasurementType = iota
    MeasurementTypeNote
    MeasurementTypeDuration
    MeasurementTypeValue
)

{ .api }

Identifies the type of measurement.

Stat

type Stat uint

const (
    StatInvalid Stat = iota
    StatMin
    StatMax
    StatMean
    StatMedian
    StatStdDev
)

{ .api }

Statistical measures available from Stats.

StatsType

type StatsType uint

const (
    StatsTypeInvalid  StatsType = iota
    StatsTypeValue
    StatsTypeDuration
)

{ .api }

Identifies the type of statistics.

RankingCriteria

type RankingCriteria uint

const (
    LowerMeanIsBetter    RankingCriteria = iota
    HigherMeanIsBetter
    LowerMedianIsBetter
    HigherMedianIsBetter
    LowerMinIsBetter
    HigherMinIsBetter
    LowerMaxIsBetter
    HigherMaxIsBetter
)

{ .api }

Criteria for ranking statistics.

Additional Types

Stopwatch

Manual timing utility for measuring durations across code sections.

type Stopwatch struct {
    Experiment *Experiment
    // contains filtered or unexported fields
}

Creating Stopwatches

Stopwatches are created from experiments:

stopwatch := experiment.NewStopwatch()

A stopwatch can create additional stopwatches pointing to the same experiment (useful for goroutines):

stopwatch2 := stopwatch.NewStopwatch()

Note: Individual stopwatches are not thread-safe. Create separate stopwatches for different goroutines.

Methods

Record
func (s *Stopwatch) Record(name string, args ...any) *Stopwatch

{ .api }

Records elapsed time since creation or last Reset(). Does not reset the stopwatch.

Supported decorations: Same as RecordDuration

Example:

stopwatch := experiment.NewStopwatch()
setupDatabase()
stopwatch.Record("setup").Reset()
runQueries()
stopwatch.Record("queries").Reset()
Reset
func (s *Stopwatch) Reset() *Stopwatch

{ .api }

Resets the stopwatch timer. Unpauses if paused. Returns *Stopwatch for chaining.

Pause
func (s *Stopwatch) Pause() *Stopwatch

{ .api }

Pauses time accumulation. Must be running when called.

Resume
func (s *Stopwatch) Resume() *Stopwatch

{ .api }

Resumes time accumulation after a pause. Must be paused when called.

Example:

stopwatch := experiment.NewStopwatch()
// Phase 1
stopwatch.Pause()
// Expensive setup we don't want to measure
setupTest()
stopwatch.Resume()
// Phase 2
stopwatch.Record("actual work") // Only measures Phase 1 + Phase 2
NewStopwatch
func (s *Stopwatch) NewStopwatch() *Stopwatch

{ .api }

Creates a new stopwatch pointing to the same experiment.

ExperimentCache

Provides directory-based caching of experiments to avoid re-running expensive benchmarks.

type ExperimentCache struct {
    Path string
}

Constructor

func NewExperimentCache(path string) (ExperimentCache, error)

{ .api }

Creates a cache at the specified directory path. Creates the directory if it doesn't exist.

Methods

Load
func (cache ExperimentCache) Load(name string, version int) *Experiment

{ .api }

Loads an experiment from cache by name. Returns the experiment only if the cached version is >= the requested version. Returns nil if not found or version is too old.

Save
func (cache ExperimentCache) Save(name string, version int, experiment *Experiment) error

{ .api }

Saves an experiment to the cache with the specified name and version.

List
func (cache ExperimentCache) List() ([]CachedExperimentHeader, error)

{ .api }

Returns a list of all cached experiments.

Delete
func (cache ExperimentCache) Delete(name string) error

{ .api }

Removes an experiment from the cache by name.

Clear
func (cache ExperimentCache) Clear() error

{ .api }

Removes all cached experiments. Use with caution.

CachedExperimentHeader

type CachedExperimentHeader struct {
    Name    string
    Version int
}

{ .api }

Metadata about a cached experiment.

Usage Example

const EXPERIMENT_VERSION = 1 // Bump to bust cache

var _ = Describe("benchmarks", func() {
    var cache gmeasure.ExperimentCache
    var experiment *gmeasure.Experiment

    BeforeEach(func() {
        cache, _ = gmeasure.NewExperimentCache("./cache")
        name := CurrentSpecReport().LeafNodeText
        experiment = cache.Load(name, EXPERIMENT_VERSION)
        if experiment != nil {
            AddReportEntry(experiment.Name, experiment)
            Skip("cached")
        }
        experiment = gmeasure.NewExperiment(name)
        AddReportEntry(experiment.Name, experiment)
    })

    It("measures API performance", func() {
        experiment.SampleDuration("api call", func(idx int) {
            makeAPICall()
        }, gmeasure.SamplingConfig{N: 100})
    })

    AfterEach(func() {
        if !CurrentSpecReport().State.Is(types.SpecStateSkipped) {
            cache.Save(experiment.Name, EXPERIMENT_VERSION, experiment)
        }
    })
})

Ranking

Ranks multiple statistics by specified criteria for comparison.

type Ranking struct {
    Criteria RankingCriteria
    Stats    []Stats
}

Constructor

func RankStats(criteria RankingCriteria, stats ...Stats) Ranking

{ .api }

Creates a ranking of the provided stats according to the criteria.

Example:

stats1 := experiment1.GetStats("response time")
stats2 := experiment2.GetStats("response time")
stats3 := experiment3.GetStats("response time")

ranking := gmeasure.RankStats(gmeasure.LowerMeanIsBetter, stats1, stats2, stats3)
AddReportEntry("Performance Ranking", ranking)

Methods

Winner
func (r Ranking) Winner() Stats

{ .api }

Returns the top-ranked Stats according to the ranking criteria.

String
func (r Ranking) String() string

{ .api }

Returns an unstyled report with rank-ordered statistics.

ColorableString
func (r Ranking) ColorableString() string

{ .api }

Returns a styled report. Called by Ginkgo when registered via AddReportEntry.

Table Building Utilities

The table sub-package (github.com/onsi/gomega/gmeasure/table) provides utilities for building formatted tables to display measurement results. These are used internally by Experiment, Measurement, and Ranking types for their String() and ColorableString() methods.

NewTable

func NewTable() *Table

Creates a new empty table for displaying formatted output.

Usage Example:

import "github.com/onsi/gomega/gmeasure/table"

t := table.NewTable()
t.AppendRow(table.R(table.C("Name"), table.C("Value")))
t.AppendRow(table.R(table.C("Test1"), table.C("42")))
fmt.Println(t.Render())

R

func R(args ...any) *Row

Creates a new table row with the specified cells and options. Arguments can be:

  • Cell objects created with C()
  • Style strings for the entire row
  • Divider for row dividers
  • AlignType for row-level alignment

Usage Example:

import "github.com/onsi/gomega/gmeasure/table"

// Simple row with cells
row := table.R(table.C("col1"), table.C("col2"))

// Row with styling
row := table.R(table.C("Name"), table.C("Value"), "{{bold}}")

// Row with divider
row := table.R(table.C("Header"), table.Divider("="))

C

func C(contents string, args ...any) Cell

Creates a table cell with the specified content and optional formatting. Arguments can be:

  • Style strings (e.g., "{{bold}}", "{{red}}")
  • AlignType for cell alignment (AlignTypeLeft, AlignTypeRight, AlignTypeCenter)

Usage Example:

import "github.com/onsi/gomega/gmeasure/table"

// Simple cell
cell := table.C("Hello")

// Styled cell
cell := table.C("Important", "{{bold}}")

// Right-aligned cell
cell := table.C("123.45", table.AlignTypeRight)

// Styled and aligned cell
cell := table.C("Error", "{{red}}", table.AlignTypeCenter)

Complete Table Example:

import (
    "fmt"
    "github.com/onsi/gomega/gmeasure/table"
)

t := table.NewTable()

// Header row with divider
t.AppendRow(table.R(
    table.C("Test Name"),
    table.C("Duration"),
    table.C("Status"),
    table.Divider("="),
))

// Data rows
t.AppendRow(table.R(
    table.C("TestA"),
    table.C("1.23s", table.AlignTypeRight),
    table.C("PASS", "{{green}}"),
))

t.AppendRow(table.R(
    table.C("TestB"),
    table.C("0.45s", table.AlignTypeRight),
    table.C("FAIL", "{{red}}"),
))

fmt.Println(t.Render())

Complete Usage Example

package mypackage_test

import (
    "time"
    . "github.com/onsi/ginkgo/v2"
    . "github.com/onsi/gomega"
    "github.com/onsi/gomega/gmeasure"
)

var _ = Describe("Performance Tests", func() {
    var experiment *gmeasure.Experiment

    BeforeEach(func() {
        experiment = gmeasure.NewExperiment("API Performance")
        AddReportEntry(experiment.Name, experiment)
    })

    It("measures response times", func() {
        // Record single duration
        experiment.RecordDuration("warmup", 50*time.Millisecond)

        // Measure function duration
        experiment.MeasureDuration("single request", func() {
            makeAPIRequest()
        }, gmeasure.Annotation("GET /users"))

        // Sample multiple times
        experiment.SampleDuration("load test", func(idx int) {
            makeAPIRequest()
        }, gmeasure.SamplingConfig{
            N:        100,
            Duration: 30 * time.Second,
        }, gmeasure.Precision(time.Millisecond))

        // Record custom values
        experiment.RecordValue("requests/sec", 1250.5,
            gmeasure.Units("req/s"),
            gmeasure.Precision(1),
            gmeasure.Style("{{green}}"))

        // Use stopwatch for detailed timing
        stopwatch := experiment.NewStopwatch()
        setupDatabase()
        stopwatch.Record("setup").Reset()
        runQueries()
        stopwatch.Record("queries").Reset()
        cleanup()
        stopwatch.Record("cleanup")

        // Get statistics
        stats := experiment.GetStats("load test")
        Expect(stats.DurationFor(gmeasure.StatMean)).To(BeNumerically("<", 100*time.Millisecond))
    })

    It("compares multiple implementations", func() {
        // Test implementation A
        experimentA := gmeasure.NewExperiment("Implementation A")
        experimentA.SampleDuration("execution", func(idx int) {
            implementationA()
        }, gmeasure.SamplingConfig{N: 50})

        // Test implementation B
        experimentB := gmeasure.NewExperiment("Implementation B")
        experimentB.SampleDuration("execution", func(idx int) {
            implementationB()
        }, gmeasure.SamplingConfig{N: 50})

        // Rank them
        statsA := experimentA.GetStats("execution")
        statsB := experimentB.GetStats("execution")
        ranking := gmeasure.RankStats(gmeasure.LowerMeanIsBetter, statsA, statsB)

        AddReportEntry("Performance Ranking", ranking)
        Expect(ranking.Winner().ExperimentName).To(Equal("Implementation A"))
    })
})

Integration with Ginkgo

gmeasure integrates seamlessly with Ginkgo V2:

Report Entries

Register experiments, measurements, or rankings as report entries:

AddReportEntry(experiment.Name, experiment)           // Full experiment
AddReportEntry("Response Times", measurement)         // Single measurement
AddReportEntry("Performance Comparison", ranking)     // Ranking

JSON Export

All gmeasure types support JSON marshaling for test report export:

data, _ := json.Marshal(experiment)

Styling

Use Ginkgo's format strings in Style() decorators:

  • {{red}}, {{green}}, {{blue}}, {{yellow}}, {{cyan}}, {{gray}}
  • {{bold}}, {{underline}}
  • {{/}} to close styling

Thread Safety

  • Experiment is thread-safe; all methods can be called from multiple goroutines
  • Stopwatch is NOT thread-safe; create separate stopwatches per goroutine using stopwatch.NewStopwatch()