[ 
https://issues.apache.org/jira/browse/GROOVY-11947?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18075280#comment-18075280
 ] 

Eric Milles commented on GROOVY-11947:
--------------------------------------

At this point, should we be using a functional interface like Callable instead 
of a Closure for extensions such as this?

> Here you go:  Title: Add timing utility methods (timed, timedNanos) and Timed 
> record to groovy-jdk
> --------------------------------------------------------------------------------------------------
>
>                 Key: GROOVY-11947
>                 URL: https://issues.apache.org/jira/browse/GROOVY-11947
>             Project: Groovy
>          Issue Type: New Feature
>            Reporter: Paul King
>            Priority: Major
>
> h3. Summary
> Add a small set of timing utilities to the groovy-jdk as extension methods, 
> providing a convenient way to measure the elapsed time of a block of code 
> without the caller having to manually record start/end times. Include a 
> {{Timed}} record type for returning a result together with its elapsed 
> duration.
> h3. Motivation
> Timing a block of code is one of the most common ad-hoc measurements 
> developers perform. The current idiom in Groovy is verbose and error-prone:
> {code}
> long start = System.nanoTime()
> def result = computeSomething()
> long elapsedNanos = System.nanoTime() - start
> {code}
> Developers frequently reach for {{System.currentTimeMillis()}} here, which is 
> the wrong tool - wall-clock time is not guaranteed to be monotonic and can 
> produce negative or wildly incorrect durations when the system clock is 
> adjusted (NTP sync, DST, manual change). A built-in utility that uses 
> {{System.nanoTime()}} correctly by default steers users toward the right 
> primitive.
> h3. Proposed API
> Three additions to the groovy-jdk:
> {{long timedNanos(Closure c)}} - runs the closure and returns the elapsed 
> time in nanoseconds. Does not return the closure's result.
> {{long timedMillis(Closure c)}} - same as above but returns milliseconds 
> (computed from nanoTime, not currentTimeMillis, so still monotonic).
> {{<T> Timed<T> timed(Closure<T> c)}} - runs the closure and returns a 
> {{Timed}} record containing both the result and the elapsed nanoseconds.
> The {{Timed<T>}} type would be a record with:
> {{T result}} - the value returned by the closure
> {{long nanos}} - elapsed time in nanoseconds
> {{Duration getDuration()}} - convenience accessor returning 
> {{Duration.ofNanos(nanos)}}
> {{long getMillis()}} - convenience accessor returning {{nanos / 1_000_000}}
> h3. Example Usage
> {code}
> // Just time it
> long elapsed = timedNanos { expensiveOperation() }
> println "took ${elapsed}ns"
> // Time it and keep the result
> def t = timed { computeReport() }
> println "report generated in ${t.millis}ms"
> processReport(t.result)
> // Use the Duration accessor for human-friendly output
> println "took ${timed { loadData() }.duration}"
> {code}
> h3. Implementation Notes
> All three methods must use {{System.nanoTime()}}, not 
> {{System.currentTimeMillis()}}. This is the whole point of the utility.
> Exceptions thrown by the closure should propagate unchanged - the utility 
> does not swallow or transform them. Timing information is lost in the 
> exception case; if users need timing on failure they can use a try/finally 
> around {{timedNanos}} themselves.
> The closure runs synchronously on the calling thread. If the closure submits 
> asynchronous work without waiting for it, only the submission time is 
> measured. This should be documented clearly.
> {{long}} subtraction of two {{nanoTime}} readings handles wraparound 
> correctly as long as the interval is under ~292 years, so no special handling 
> is needed.
> h3. Location
> These are best placed as extension methods in {{DefaultGroovyMethods}} or a 
> dedicated timing extension module. The {{Timed}} record would live alongside 
> them.
> h3. Out of Scope
> Benchmarking utilities (warmup, statistical measurement, JIT handling) - 
> users who need this should use JMH.
> Async / reactive timing helpers - a separate concern, possibly a future 
> enhancement.
> CPU time measurement - {{ThreadMXBean.getCurrentThreadCpuTime()}} has 
> different semantics and should not be conflated with wall-clock elapsed time.
> h3. Related
> Complements the existing time-related sugar in groovy-jdk (e.g. 
> {{use(TimeCategory)}}, {{Date}} extensions) by adding a primitive for the 
> measurement case specifically.



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to