{"id":19063305,"url":"https://github.com/jamiealquiza/tachymeter","last_synced_at":"2025-08-21T04:30:45.707Z","repository":{"id":56026933,"uuid":"68336311","full_name":"jamiealquiza/tachymeter","owner":"jamiealquiza","description":"A Go library for timing things and yielding rates, percentiles, and histograms","archived":false,"fork":false,"pushed_at":"2020-11-30T12:45:08.000Z","size":175,"stargazers_count":131,"open_issues_count":10,"forks_count":9,"subscribers_count":4,"default_branch":"master","last_synced_at":"2024-12-11T12:24:06.544Z","etag":null,"topics":["golang","histogram","latency","metrics","percentiles","performance","telemetry"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jamiealquiza.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-09-15T22:28:55.000Z","updated_at":"2024-12-03T09:13:38.000Z","dependencies_parsed_at":"2022-08-15T11:40:42.923Z","dependency_job_id":null,"html_url":"https://github.com/jamiealquiza/tachymeter","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jamiealquiza%2Ftachymeter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jamiealquiza%2Ftachymeter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jamiealquiza%2Ftachymeter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jamiealquiza%2Ftachymeter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jamiealquiza","download_url":"https://codeload.github.com/jamiealquiza/tachymeter/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":230487844,"owners_count":18233865,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["golang","histogram","latency","metrics","percentiles","performance","telemetry"],"created_at":"2024-11-09T00:29:36.840Z","updated_at":"2024-12-19T19:08:30.280Z","avatar_url":"https://github.com/jamiealquiza.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![GoDoc](https://godoc.org/github.com/jamiealquiza/tachymeter?status.svg)](https://godoc.org/github.com/jamiealquiza/tachymeter)\n\n# tachymeter\n\nTachymeter captures event timings and returns latency and rate statistics: _\"In a loop with 1,000 database calls, what was the 95%ile and lowest observed latency? What was the per-second rate?\"_\n\nTachymeter stores data in a lossless sliding window. This means it's accurate but take o(n) space in relation to the desired sample size.\n\n# Examples\n\nCode [examples](https://github.com/jamiealquiza/tachymeter/tree/master/example). Tachymeter is also suitable for general purpose use, such as [load testing tools](https://github.com/jamiealquiza/sangrenel).\n\n# Usage\n\nAfter initializing a `tachymeter`, event durations in the form of [`time.Duration`](https://golang.org/pkg/time/#Duration) are added using the `AddTime(t time.Duration)` call. Once all desired timing have been collected, the data is summarized by calling the `Calc()`, returning a [`*Metrics`](https://godoc.org/github.com/jamiealquiza/tachymeter#Metrics)). `*Metrics` fields can be accessed directly or via other [output methods](https://github.com/jamiealquiza/tachymeter#output-methods).\n\n```golang\nimport \"github.com/jamiealquiza/tachymeter\"\n\nfunc main() {\n    // Initialize a tachymeter with a sample window\n    // size of 50 events.\n    t := tachymeter.New(\u0026tachymeter.Config{Size: 50})\n\n    for i := 0; i \u003c 100; i++ {\n        start := time.Now()\n        doSomeWork()\n        // We add the time that\n        // each doSomeWork() call took.\n        t.AddTime(time.Since(start))\n    }\n\n    // The timing summaries are calculated\n    // and printed to console.\n    fmt.Println(t.Calc())\n}\n```\n\n```\n50 samples of 100 events\nCumulative:\t671.871ms\nHMean:\t\t125.38µs\nAvg.:\t\t13.43742ms\np50: \t\t13.165ms\np75:\t\t20.058ms\np95:\t\t27.536ms\np99:\t\t30.043ms\np999:\t\t30.043ms\nLong 5%:\t29.749ms\nShort 5%:\t399.666µs\nMax:\t\t30.043ms\nMin:\t\t4µs\nRange:\t\t30.039ms\nStdDev:\t\t8.385117ms\nRate/sec.:\t74.42\n```\n\n### Output Descriptions\n\n- `Cumulative`: Aggregate of all sample durations.\n- `HMean`: Event duration harmonic mean.\n- `Avg.`: Average event duration per sample.\n- `p\u003cN\u003e`: Nth %ile.\n- `Long 5%`: Average event duration of the longest 5%.\n- `Short 5%`: Average event duration of the shortest 5%.\n- `Max`: Max observed event duration.\n- `Min`: Min observed event duration.\n- `Range`: The delta between the max and min sample time\n- `StdDev`: The population standard deviation\n- `Rate/sec.`: Per-second rate based on cumulative time and sample count.\n\n\n# Output Methods\n\nTachymeter output is stored in two primary forms:\n\n- A [`*Metrics`](https://godoc.org/github.com/jamiealquiza/tachymeter#Metrics), which holds the calculated percentiles, rates and other information detailed in the [Output Descriptions](https://github.com/jamiealquiza/tachymeter#output-descriptions) section\n- A [`*Histogram`](https://godoc.org/github.com/jamiealquiza/tachymeter#Histogram) of all measured event durations, nested in the `Metrics.Histogram` field\n\n`t` represents a tachymeter instance. Calling `t.Calc()` returns a `*Metrics`. `Metrics` and the nested `Histogram` types can be access in several ways:\n\n### `Metrics`: raw struct\n```golang\nmetrics := t.Calc()\nfmt.Printf(\"Median latency: %s\\n\", metrics.Time.P50)\n```\n\nOutput:\n```\nMedian latency: 13.165ms\n```\n\n### `Metrics`: JSON string\n ```golang\nfmt.Printf(\"%s\\n\\\", metrics.JSON())\n```\nOutput:\n```\n{\"Time\":{\"Cumulative\":\"671.871ms\",\"HMean\":\"125.38µs\",\"Avg\":\"13.43742ms\",\"P50\":\"13.165ms\",\"P75\":\"20.058ms\",\"P95\":\"27.536ms\",\"P99\":\"30.043ms\",\"P999\":\"30.043ms\",\"Long5p\":\"29.749ms\",\"Short5p\":\"399.666µs\",\"Max\":\"30.043ms\",\"Min\":\"4µs\",\"Range\":\"30.039ms\",\"StdDev\":\"8.385117ms\"},\"Rate\":{\"Second\":74.41904770409796},\"Samples\":50,\"Count\":100,\"Histogram\":[{\"4µs - 3.007ms\":5},{\"3.007ms - 6.011ms\":4},{\"6.011ms - 9.015ms\":10},{\"9.015ms - 12.019ms\":6},{\"12.019ms - 15.023ms\":7},{\"15.023ms - 18.027ms\":3},{\"18.027ms - 21.031ms\":4},{\"21.031ms - 24.035ms\":3},{\"24.035ms - 27.039ms\":3},{\"27.039ms - 30.043ms\":5}]}\n```\n\n### `Metrics`: pre-formatted, multi-line string\n ```golang\nfmt.Println(metrics)\n ```\n\n Output:\n ```\n50 samples of 100 events\nCumulative:\t671.871ms\nHMean:\t\t125.38µs\nAvg.:\t\t13.43742ms\np50: \t\t13.165ms\np75:\t\t20.058ms\np95:\t\t27.536ms\np99:\t\t30.043ms\np999:\t\t30.043ms\nLong 5%:\t29.749ms\nShort 5%:\t399.666µs\nMax:\t\t30.043ms\nMin:\t\t4µs\nRange:\t\t30.039ms\nStdDev:\t\t8.385117ms\nRate/sec.:\t74.42\n ```\n\n### `Histogram`: text\nThe `Histogram.String(int)` method generates a text version of the histogram. Histogram bar scaling is specified with width `int`.\n```golang\nfmt.Println(metrics.Histogram.String(25))\n```\n\nOutput:\n```\n       4µs - 3.007ms -----\n   3.007ms - 6.011ms ---\n   6.011ms - 9.015ms ---------------\n  9.015ms - 12.019ms -------\n 12.019ms - 15.023ms ---------\n 15.023ms - 18.027ms -\n 18.027ms - 21.031ms ---\n 21.031ms - 24.035ms -\n 24.035ms - 27.039ms -\n 27.039ms - 30.043ms -----\n```\n\n### `Histogram`: HTML\nA `Histogram` can be written as HTML histograms. The `Metrics.WriteHTML(p string)` method is called where `p` is an output path where the HTML file should be written.\n\n ```golang\n err := metrics.WriteHTML(\".\")\n ```\n\n Output:\n![ss](https://user-images.githubusercontent.com/4108044/37558972-a40374f2-29e2-11e8-9df2-60b2927a8fa4.png)\n\nTachymeter also provides a `Timeline` type that's used to gather a series of `*Metrics` (each `*Metrics` object holding data summarizing a series of measured events). `*Metrics` are added to a `*Timeline` using the `AddEvent(m *Metrics)` method. Once the desired number of `*Metrics` has been collected, `WriteHTML` can be called on the `*Timeline`, resulting in an single HTML page with a histogram for each captured `*Metrics`. An example use case may be a benchmark where tachymeter is used to summarize the timing results of a loop, but several iterations of the loop are to be called in series. See the [tachymeter-graphing example](https://github.com/jamiealquiza/tachymeter/tree/master/example/tachymeter-graphing) for further details.\n\n### Configuration\n\nTachymeter is initialized with a `Size` parameter that specifies the maximum sample count that can be held. This is done to set bounds on tachymeter memory usage (since it's a lossless storage structure). The `AddTime` method is o(1) and typically sub-microsecond  modern hardware. If the actual event count is smaller than or equal to the configured tachymeter size, all of the measured events will be included in the calculated results. If the event count exceeds the tachymeter size, the oldest data will be overwritten. In this scenario, the last window of `Size` events will be used for output calculations.\n\n# Accurate Rates With Parallelism\n\nBy default, tachymeter calculates rate based on the number of events possible per-second according to average event duration. This model doesn't work in asynchronous or parallelized scenarios since events may be overlapping in time. For example, with many Goroutines writing durations to a shared tachymeter in parallel, the global rate must be determined by using the total event count over the total wall time elapsed.\n\nTachymeter exposes a `SetWallTime` method for these scenarios.\n\nExample:\n\n```golang\n\u003c...\u003e\n\nfunc main() {\n    // Initialize tachymeter.\n    c := tachymeter.New(\u0026tachymeter.Config{Size: 50})\n\n    // Start wall time for all Goroutines.\n    wallTimeStart := time.Now()\n    var wg sync.WaitGroup\n\n    // Run tasks.\n    for i := 0; i \u003c 5; i++ {\n        wg.Add(1)\n        go someTask(t, wg)\n    }\n\n    wg.Wait()\n\n    // When finished, set elapsed wall time.\n    t.SetWallTime(time.Since(wallTimeStart))\n\n    // Rate outputs will be accurate.\n    fmt.Println(t.Calc().String())\n}\n\nfunc someTask(t *tachymeter.Tachymeter, wg *sync.WaitGroup) {\n    defer wg.Done()\n    start := time.Now()\n\n    // doSomeSlowDbCall()\n\n    // Task we're timing added here.\n    t.AddTime(time.Since(start))\n}\n\n\u003c...\u003e\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjamiealquiza%2Ftachymeter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjamiealquiza%2Ftachymeter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjamiealquiza%2Ftachymeter/lists"}