# Progress and time remaining

By R. S. Doiel, 2022-11-05

I often find myself logging output when I’m developing tools. This is
typically the case where I am iterating over data and transforming it.
Overtime I’ve come to realize I really want a few specific pieces of
information for non-error logging (e.g. `-verbose`

which
monitors progress as well as errors).

- percentage completed
- estimated time allocated (i.e. time remaining)

To do that I need three pieces of information.

- the count of the current iteration(e.g.
`i`

) - the total number of iterations required (e.g.
`tot`

) - The time just before I started iterating(e.g.
`t0`

)

The values for `i`

and `tot`

let me compute the
percent completed. The percent completed is trivial
`(i/tot) * 100.0`

. Note on the first pass
(i.e. `i == 0`

) you can skip the percentage calculation.

```
import (
"time"
"fmt"
)
// Show progress with amount of time running
func progress(t0 time.Time, i int, tot int) string {
if i == 0 {
return ""
}
percent := (float64(i) / float64(tot)) * 100.0
t1 := time.Now()
// NOTE: Truncating the duration to seconds
return fmt.Sprintf("%.2f%% %v", percent, t1.Sub(t0).Truncate(time.Second))
}
```

Here’s how you might use it.

```
tot := len(ids)
t0 := time.Now()
for i, id := range ids {
// ... processing stuff here ... and display progress every 1000 records
if (i % 1000) == 0 {
log.Printf("%s records processed", progress(t0, i, tot))
}
}
```

An improvement on this is to include an time remaining. I need to
calculated the estimated time allocation (i.e. ETA). I know
`t0`

so I can estimate that with this formula
`estimated time allocation = (((current running time since t0)/ the number of items processed) * total number of items)`

^{1}. ETA adjusted for time running gives
us time remaining^{2}. The first pass of the function
progress has a trivial optimization since we don’t have enough delta t0
to compute an estimate. Calls after that are computed using our
formula.

```
func progress(t0 time.Time, i int, tot int) string {
if i == 0 {
return "0.00 ETR Unknown"
}
// percent completed
percent := (float64(i) / float64(tot)) * 100.0
// running time
rt := time.Now().Sub(t0)
// estimated time allocation - running time = time remaining
eta := time.Duration((float64(rt)/float64(i)*float64(tot)) - float64(rt))
return fmt.Sprintf("%.2f%% ETR %v", percent, eta.Truncate(time.Second))
}
```