doc/lib.nvim-time_diff.txt — rendered from the plugin's own vimdoc
*lib.nvim-time_diff.txt* *lib.nvim-time_diff* High-precision time measurement Author: lib.lua.time.diff maintainers License: Same as Neovim
CONTENTS
1. Introduction ............................ |time_diff-introduction| 2. Usage ................................... |time_diff-usage| 3. API Reference ........................... |time_diff-api| 4. Dynamic Properties ...................... |time_diff-properties| 5. Statistics .............................. |time_diff-statistics| 6. Iterator Support ........................ |time_diff-iterator| 7. Examples ................................ |time_diff-examples| 8. Technical Notes ......................... |time_diff-technical|
INTRODUCTION
Thelib.lua.time.diffmodule provides a lightweight, reusable timer for measuring elapsed time between code sections. Each call torequire("lib.lua.time.diff")returns a fresh timer instance with independent state. Key features: - Nanosecond precision viavim.uv.hrtime()(default output unit) - Multiple checkpoints with automatic interval calculation - Dynamic property generation (diff.first, diff.second, ..., diff.last) - Configurable output units: ns (default), us, ms, s - Comprehensive statistics (min, max, avg, median, stddev, CV) - Pretty-printed summary tables with adjustable units - Iterator support with custom labels and index display - Metatable-based callable interface (print(diff),diff()) Default unit: Nanoseconds (ns). All methods accept an optional unit parameter.
USAGE
Basic workflow:
local diff = require("lib.lua.time.diff")
diff.start() -- Optional, timer starts automatically
-- Code block 1 (default: nanoseconds)
local t1 = diff.check()
-- Code block 2 (explicit milliseconds)
local t2 = diff.check("ms")
-- Access via dynamic properties (always in ns)
print(diff.first) -- First checkpoint
print(diff.second) -- Second checkpoint
print(diff.last) -- Last checkpoint
-- Statistics
print(diff.fastest("ms")) -- Fastest interval
print(diff.average("ms")) -- Average interval
-- Output
print(diff("ms")) -- Print all checkpoints in ms
print(diff.pretty("ms")) -- Formatted table with statistics
API REFERENCE
CORE METHODS
diff.start() *diff.start()* Start or reset the timer. Clears all previous checkpoints and sets a new baseline timestamp.
Returns:
nildiff.check({unit}) *diff.check()* Record a checkpoint and return elapsed time sincestart(). Can be called multiple times to measure intermediate intervals.
Parameters:
{unit} string|nil - Output unit: "ns" (default), "us", "ms", "s"
Returns:
number - Elapsed time in specified unit
Errors:
Throws error if start() was not called first.
Throws error if invalid unit is provided.
Example:
local t1 = diff.check() -- Nanoseconds (default)
local t2 = diff.check("ms") -- Milliseconds
diff.result({unit}) *diff.result()*
Get total elapsed time since start. Equivalent to the last checkpoint
if check() was called.
Parameters:
{unit} string|nil - Output unit: "ns" (default), "us", "ms", "s"
Returns:
number|nil- Total time in specified unit, ornilif no checkpoints
Example:
local total = diff.result("ms")
print("Total time:", total, "ms")
diff.get({idx}, {unit}) *diff.get()*
Get elapsed time of a specific checkpoint by index.
Index is 1-based (Lua convention).
Parameters:
{idx} integer - Checkpoint index (1 = first, 2 = second, etc.)
{unit} string|nil - Output unit: "ns" (default), "us", "ms", "s"
Returns:
number|nil- Elapsed time in specified unit, ornilif out of bounds
Example:
local t1 = diff.get(1, "ms") -- First checkpoint in ms
local t3 = diff.get(3, "us") -- Third checkpoint in us
OUTPUT METHODS
diff.results({unit}) *diff.results()*
Generate a summary string with all checkpoint times and statistics.
Parameters:
{unit} string|nil - Output unit: "ns" (default), "us", "ms", "s"
Format:
"Check 1: X.XXXns | ... | Total: Z.ZZZns | Fastest: A.AAAns | ..."
Returns:
string - Human-readable summary
Example:
print(diff.results("ms"))
-- Output: "Check 1: 12.345ms | ... | Fastest: 10.000ms | ..."
diff.pretty({unit}) *diff.pretty()*
Generate a pretty-printed table suitable for :messages or notify windows.
Includes comprehensive statistics section.
Parameters:
{unit} string|nil - Output unit: "ns" (default), "us", "ms", "s"
Columns:
- Index: Checkpoint number
- Elapsed: Time since start
- Delta: Time since previous checkpoint
Statistics Section:
- Fastest Δ: Minimum interval between checkpoints
- Longest Δ: Maximum interval between checkpoints
- Average Δ: Mean interval
- Median Δ: Median interval
- Range: Difference between longest and fastest
- Std Dev: Standard deviation (if >= 2 checkpoints)
- CV: Coefficient of variation in % (if >= 2 checkpoints)
Returns:
string - Multi-line formatted table
Example output (milliseconds):
┌────────┬─────────────────┬─────────────────┐
│ Index │ Elapsed (ms) │ Delta (ms) │
├────────┼─────────────────┼─────────────────┤
│ 1 │ 12.345 │ 12.345 │
│ 2 │ 23.456 │ 11.111 │
├────────┴─────────────────┴─────────────────┤
│ Total: 23.456ms │
├──────────────────────────────────────────────┤
│ Statistics: │
├──────────────────────────────────────────────┤
│ Fastest Δ: 11.111ms │
│ Longest Δ: 12.345ms │
│ Average Δ: 11.728ms │
│ Median Δ: 11.728ms │
│ Range: 1.234ms │
│ Std Dev: 0.617ms │
│ CV: 5.26% │
└──────────────────────────────────────────────┘
METATABLE BEHAVIOR
The timer instance is callable via metatable:
print(diff()) -- Same as diff.results()
print(diff("ms")) -- Same as diff.results("ms")
print(tostring(diff)) -- Same as diff.results()
DYNAMIC PROPERTIES
The timer instance automatically generates properties for all checkpoints. Properties always return values in NANOSECONDS, regardless of the unit specified incheck(). Available properties: diff.first *diff.first* First checkpoint elapsed time (nanoseconds). Same asdiff.get(1). Type:number|nil(nil if checkpoint doesn't exist) diff.second *diff.second* Second checkpoint elapsed time (nanoseconds). Same asdiff.get(2). Type:number|nildiff.third *diff.third* Third checkpoint elapsed time (nanoseconds). Same asdiff.get(3). Type:number|nildiff.fourth through diff.tenth *diff.fourth* *diff.tenth* Fourth through tenth checkpoint elapsed times (nanoseconds). Same asdiff.get(4)throughdiff.get(10). Type:number|nildiff.last *diff.last* Last checkpoint elapsed time (nanoseconds). Same asdiff.result()(without unit parameter). Type:number|nil(nil if no checkpoints recorded)
Example:
local diff = require("lib.lua.time.diff")
diff.check() -- First checkpoint
diff.check() -- Second checkpoint
diff.check() -- Third checkpoint
print(diff.first) -- First checkpoint (ns)
print(diff.second) -- Second checkpoint (ns)
print(diff.third) -- Third checkpoint (ns)
print(diff.last) -- Last checkpoint (same as third, in ns)
-- Calculate delta
local delta_ns = diff.third - diff.first
print("Delta:", delta_ns, "ns")
-- Convert to milliseconds manually if needed
print("Delta:", delta_ns / 1e6, "ms")
Note:
Properties are dynamically generated via metatable __index and do not
create actual table entries. Accessing non-existent checkpoints returns nil.
STATISTICS
The timer provides comprehensive statistical analysis of intervals between
checkpoints. All statistics operate on deltas (differences between consecutive
checkpoints), not cumulative checkpoint times.
diff.fastest({unit}) *diff.fastest()*
Get the minimum interval between any two consecutive checkpoints.
Parameters:
{unit} string|nil - Output unit: "ns" (default), "us", "ms", "s"
Returns:
number|nil - Fastest interval, or nil if fewer than 1 checkpoint
Example:
local min_time = diff.fastest("ms")
print("Fastest operation:", min_time, "ms")
diff.longest({unit}) *diff.longest()*
Get the maximum interval between any two consecutive checkpoints.
Parameters:
{unit} string|nil - Output unit: "ns" (default), "us", "ms", "s"
Returns:
number|nil - Longest interval, or nil if fewer than 1 checkpoint
Example:
local max_time = diff.longest("ms")
print("Longest operation:", max_time, "ms")
diff.average({unit}) *diff.average()*
Get the arithmetic mean of all intervals between checkpoints.
Parameters:
{unit} string|nil - Output unit: "ns" (default), "us", "ms", "s"
Returns:
number|nil - Average interval, or nil if fewer than 1 checkpoint
Formula:
average = sum(all_intervals) / count(intervals)
Example:
local avg = diff.average("ms")
print("Average time:", avg, "ms")
diff.median({unit}) *diff.median()*
Get the median interval between checkpoints.
Parameters:
{unit} string|nil - Output unit: "ns" (default), "us", "ms", "s"
Returns:
number|nil - Median interval, or nil if fewer than 1 checkpoint
Calculation:
For odd count: middle value
For even count: average of two middle values
Example:
local med = diff.median("ms")
print("Median time:", med, "ms")
diff.stddev({unit}) *diff.stddev()*
Get the standard deviation of intervals between checkpoints.
Measures the amount of variation or dispersion.
Parameters:
{unit} string|nil - Output unit: "ns" (default), "us", "ms", "s"
Returns:
number|nil - Standard deviation, or nil if fewer than 2 checkpoints
Formula:
stddev = sqrt(sum((interval - avg)^2) / count)
Example:
local sd = diff.stddev("ms")
print("Standard deviation:", sd, "ms")
diff.cv() *diff.cv()* Get the coefficient of variation (CV) as a percentage. Measures relative variability: lower values indicate more consistent timing.
Returns:
number|nil - CV in percent, or nil if fewer than 2 checkpoints
Formula:
CV = (stddev / mean) * 100
Interpretation:
< 10%: Very consistent performance
10-30%: Moderate variation
> 30%: High variability
Example:
local cv = diff.cv()
if cv < 10 then
print("Very consistent performance:", cv, "%")
end
diff.calc_diff({iv1}, {iv2}, {unit}) *diff.calc_diff()*
Calculate absolute difference between two intervals.
Accepts checkpoint indices, statistical keywords, or raw time values.
Always returns positive difference regardless of argument order.
Parameters:
{iv1} integer|string|number - First interval specifier
{iv2} integer|string|number - Second interval specifier
{unit} string|nil - Output unit: "ns" (default), "us", "ms", "s"
Interval Specifiers:
- Integer (1-10): Checkpoint index
- String keywords: "average"/"avg", "fastest"/"min", "longest"/"max",
"median"/"med"
- Large number: Treated as raw time value in nanoseconds
Returns:
number|nil - Absolute difference, or nil if invalid input
Examples:
-- Between checkpoints
local d1 = diff.calc_diff(1, 3, "ms")
-- Checkpoint vs statistics
local d2 = diff.calc_diff(2, "average", "ms")
local d3 = diff.calc_diff(1, "fastest", "ms")
-- Between statistics
local range = diff.calc_diff("fastest", "longest", "ms")
-- With raw time value
local target = 100000000 -- 100ms in ns
local d4 = diff.calc_diff(1, target, "ms")
Note:
Checkpoints represent cumulative time since start.
Statistics (fastest, longest, average, median) represent intervals (deltas)
between consecutive checkpoints. The calc_diff() function works with both.
ITERATOR SUPPORT
The iterator provides sequential access to checkpoints with optional
formatting.
diff.next({label}, {unit}) *diff.next()*
Iterator: Returns the next checkpoint sequentially.
Resets to the beginning when all checkpoints are exhausted.
Parameters:
{label} string|nil - Custom label for this specific call (overrides
iterator label set by reset_iterator())
{unit} string|nil - Output unit: "ns" (default), "us", "ms", "s"
Returns:
string|number|nil- Formatted string if label is set, raw number otherwise, ornilif no more checkpoints remain
Behavior:
- If no label is set (neither via parameter nor reset_iterator()),
returns raw numeric value
- If label is set, returns formatted string:
- Without index: "Label VALUE"
- With index: "Label INDEX: VALUE"
- Label from parameter overrides iterator label for this call only
Examples:
-- Numeric output
diff.reset_iterator()
local t = diff.next() -- Returns: 12345678 (ns)
-- String output with label
diff.reset_iterator("Checkpoint")
local s = diff.next(nil, "ms") -- Returns: "Checkpoint 12.345ms"
-- String output with label and index
diff.reset_iterator("Step", true)
local s = diff.next(nil, "ms") -- Returns: "Step 1: 12.345ms"
-- Override label for one call
diff.reset_iterator("Default", true)
local s = diff.next("Custom", "ms") -- Returns: "Custom 2: 23.456ms"
diff.reset_iterator({label}, {show_index}) *diff.reset_iterator()*
Reset the iterator to the beginning. Optionally set a custom label and
enable index display for subsequent next() calls.
Parameters:
{label} string|nil - Custom label to prepend to iterator output
{show_index} boolean|nil - Whether to include checkpoint index
Returns:
nil
Examples:
-- Reset without label
diff.reset_iterator()
-- Reset with label only
diff.reset_iterator("Checkpoint")
-- Reset with label and index
diff.reset_iterator("Step", true)
ITERATOR USAGE PATTERNS
Basic numeric iteration:
diff.reset_iterator()
while true do
local t = diff.next() -- Returns number (ns)
if not t then break end
print(t)
end
With label:
diff.reset_iterator("Checkpoint")
while true do
local s = diff.next(nil, "ms") -- Returns string
if not s then break end
print(s) -- "Checkpoint 12.345ms"
end
With label and index:
diff.reset_iterator("Step", true)
while true do
local s = diff.next(nil, "ms")
if not s then break end
print(s) -- "Step 1: 12.345ms", "Step 2: 23.456ms", ...
end
Override label for single call:
diff.reset_iterator("Default", true)
print(diff.next(nil, "ms")) -- "Default 1: 12.345ms"
print(diff.next("Custom", "ms")) -- "Custom 2: 23.456ms"
print(diff.next(nil, "ms")) -- "Default 3: 34.567ms"
EXAMPLES
Example 1: Basic timing
local diff = require("lib.lua.time.diff")
diff.start()
vim.fn.sleep(100) -- Sleep 100ms
local t1 = diff.check("ms")
vim.fn.sleep(200)
local t2 = diff.check("ms")
print(diff.pretty("ms"))
Example 2: Calculating deltas
local diff = require("lib.lua.time.diff")
diff.start()
-- Code block 1
diff.check()
-- Code block 2
diff.check()
-- Code block 3
diff.check()
-- Properties are always in nanoseconds
print("First:", diff.first, "ns")
print("Third:", diff.third, "ns")
print("Delta:", diff.third - diff.first, "ns")
-- Convert to milliseconds manually
print("Delta:", (diff.third - diff.first) / 1e6, "ms")
Example 3: Iterator with labels
local diff = require("lib.lua.time.diff")
-- Create 3 checkpoints
for i = 1, 3 do
vim.fn.sleep(100)
diff.check()
end
-- Iterator with label and index
diff.reset_iterator("Measurement", true)
while true do
local s = diff.next(nil, "ms")
if not s then break end
print(s)
-- Output:
-- "Measurement 1: 100.123ms"
-- "Measurement 2: 200.456ms"
-- "Measurement 3: 300.789ms"
end
Example 4: Multiple independent timers
local timer1 = require("lib.lua.time.diff")
local timer2 = require("lib.lua.time.diff")
timer1.start()
-- ... some code ...
timer1.check()
timer2.start()
-- ... other code ...
timer2.check()
print("Timer 1:", timer1.result("ms"), "ms")
print("Timer 2:", timer2.result("ms"), "ms")
Example 5: Benchmark with unit conversion
local diff = require("lib.lua.time.diff")
diff.start()
-- Benchmark math.sqrt
for i = 1, 1000000 do
math.sqrt(i)
end
local t1 = diff.check("ms")
-- Benchmark math.sin
for i = 1, 1000000 do
math.sin(i)
end
local t2 = diff.check("ms")
print(diff.pretty("ms"))
print("Difference:", t2 - t1, "ms")
-- Or use properties (always in ns)
print("Delta (ns):", diff.second - diff.first)
print("Delta (ms):", (diff.second - diff.first) / 1e6)
Example 6: Statistics and analysis
local diff = require("lib.lua.time.diff")
-- Simulate variable execution times
for i = 1, 10 do
vim.fn.sleep(math.random(50, 150))
diff.check()
end
-- Comprehensive statistics
print(diff.pretty("ms"))
-- Individual values
print("\nDetailed Analysis:")
print("Fastest:", diff.fastest("ms"), "ms")
print("Longest:", diff.longest("ms"), "ms")
print("Average:", diff.average("ms"), "ms")
print("Median:", diff.median("ms"), "ms")
print("StdDev:", diff.stddev("ms"), "ms")
print("CV:", diff.cv(), "%")
-- Calculate differences
print("\nDifferences:")
print("Range:", diff.calc_diff("fastest", "longest", "ms"), "ms")
print("Check 1 vs Avg:", diff.calc_diff(1, "average", "ms"), "ms")
Example 7: Performance comparison
local diff = require("lib.lua.time.diff")
-- Benchmark multiple operations
local ops = {"sqrt", "sin", "cos", "tan"}
for _, op in ipairs(ops) do
for i = 1, 100000 do
math[op](i)
end
diff.check()
end
print(diff.pretty("us"))
-- Find slowest operation
local longest_idx = 1
local longest_val = diff.get(1)
for i = 2, #ops do
local val = diff.get(i)
if val > longest_val then
longest_idx = i
longest_val = val
end
end
print("\nSlowest:", ops[longest_idx])
print("Time:", diff.get(longest_idx, "ms"), "ms")
-- Compare to average
for i, op in ipairs(ops) do
local dev = diff.calc_diff(i, "average", "ms")
print(string.format("%s: %+.3fms from average", op, dev))
end
Example 8: Iterator with override labels
local diff = require("lib.lua.time.diff")
-- Create checkpoints
for i = 1, 5 do
vim.fn.sleep(50)
diff.check()
end
-- Set default label with index
diff.reset_iterator("Checkpoint", true)
-- First three with default label
for i = 1, 3 do
print(diff.next(nil, "ms"))
-- Output:
-- "Checkpoint 1: 50.123ms"
-- "Checkpoint 2: 100.456ms"
-- "Checkpoint 3: 150.789ms"
end
-- Override label for fourth
print(diff.next("Special", "ms"))
-- Output: "Special 4: 200.234ms"
-- Back to default for fifth
print(diff.next(nil, "ms"))
-- Output: "Checkpoint 5: 250.567ms"
TECHNICAL NOTES
Precision
- Uses vim.uv.hrtime() for nanosecond precision
- Default output: nanoseconds (ns)
- Configurable units: ns, us, ms, s
- Properties always return nanoseconds
State Isolation
Each require("lib.lua.time.diff") creates a new instance with independent
state. No shared global variables.
Unit Conversion
- 1 second (s) = 1,000,000,000 ns - 1 millisecond (ms) = 1,000,000 ns - 1 microsecond (us) = 1,000 ns - 1 nanosecond (ns) = 1 ns
Statistics
- Statistics operate on intervals (deltas) between consecutive checkpoints
- Checkpoints represent cumulative time since start
- Example:
Checkpoints: 10ms, 25ms, 50ms
Intervals: 10ms, 15ms, 25ms
Statistics: fastest=10ms, longest=25ms, average=16.67ms
Coefficient of Variation (CV)
- CV = (standard deviation / mean) * 100
- Interpretation:
< 10%: Very consistent performance
10-30%: Moderate variation
> 30%: High variability in timing
calc_diff() Resolution
The function intelligently determines whether a number is an index or a raw time value: - Small integers (1-10): Treated as checkpoint indices - Large numbers: Treated as raw nanosecond values - Strings: Resolved to statistical keywords
Error Handling
- Callingcheck()beforestart()throws an error:
[lib.lua.time.diff] Timer not started. Call `start()` first.
- Invalid unit parameter throws an error:
[lib.lua.time.diff] Invalid unit: xyz
- Statistical functions return nil if insufficient checkpoints exist
Dynamic Properties
- Generated via metatable__index- Up to 10 named checkpoints: first through tenth - Special propertylastfor last checkpoint - Properties return nil if checkpoint doesn't exist - Always return values in nanoseconds
Iterator Behavior
- Maintains internal index for sequential traversal
- Returns nil when exhausted (resets index to 0)
- Label and index display are optional and configurable
- Override label persists for single next() call only
Performance
- Overhead per checkpoint: ~100-200ns - Statistical calculations: O(n) where n = checkpoint count - Suitable for micro-benchmarking and profiling - Minimal memory footprint per instance
Limitations
- No support for pausing/resuming timers - Checkpoints cannot be removed once recorded - Iterator does not support reverse traversal - Maximum 10 named properties (first-tenth) - Statistics require at least 1 checkpoint (2 for stddev/CV)
Metatable Features
-__call: Makes instance callable ->diff()returnsresults()-__tostring: String conversion ->tostring(diff)returnsresults()-__index: Enables dynamic property access (first, second, etc.)