vbauerster / mpb

multi progress bar for Go cli applications
The Unlicense
2.29k stars 123 forks source link

Invalid rate calculation #135

Open jtolio opened 9 months ago

jtolio commented 9 months ago

Here is a modified version of this example, to use a ProxyWriter. It does not show the right rate:

package main

import (
    "io"
    "time"

    "github.com/vbauerster/mpb/v8"
    "github.com/vbauerster/mpb/v8/decor"
)

func main() {
    var total int64 = 1024 * 1024 * 500

    p := mpb.New(
        mpb.WithWidth(60),
        mpb.WithRefreshRate(180*time.Millisecond),
    )

    bar := p.New(total,
        mpb.BarStyle().Rbound("|"),
        mpb.PrependDecorators(
            decor.Counters(decor.SizeB1000(0), "% .2f / % .2f"),
        ),
        mpb.AppendDecorators(
            decor.EwmaETA(decor.ET_STYLE_GO, 30),
            decor.Name(" ] "),
            decor.EwmaSpeed(decor.SizeB1024(0), "% .2f", 30),
        ),
    )

    bar.SetTotal(total, false)
    bar.EnableTriggerComplete()

    var data [64 * 1024]byte

    pw := bar.ProxyWriter(io.Discard)

    for i := 0; i < 1024; i++ {
        pw.Write(data[:])
        time.Sleep(time.Second / 10)
    }
    pw.Close()

    p.Wait()
}

As you can see, this is doing 64KiB of writes every tenth of a second, so that means the speed here is 640KiB/s. Right?

Running this command consistently shows me a rate output of somewhere between 60 and 90 GiB/s. What?

vbauerster commented 8 months ago

Implementation of Ewma prefixed decorators is delegated to ewma lib. So if you decided to use it I kindly recommend to read its documentation thoroughly. If you need a quick fix I recommend to use decor.AverageSpeed instead. In your example you use ProxyWriter(io.Discard) so you could end up measuring write speed of io.Discard. By using ProxyReader instead I've got adequate results with decor.EwmaSpeed. Try io example with following patch:

diff --git a/_examples/io/main.go b/_examples/io/main.go
index 252a967..eeae8d9 100644
--- a/_examples/io/main.go
+++ b/_examples/io/main.go
@@ -11,8 +11,20 @@ import (
 )

 func main() {
-   var total int64 = 1024 * 1024 * 500
-   reader := io.LimitReader(rand.Reader, total)
+   var total int64 = 64 * 1024 * 1024
+
+   r, w := io.Pipe()
+
+   go func() {
+       for i := 0; i < 1024; i++ {
+           _, err := io.Copy(w, io.LimitReader(rand.Reader, 64*1024))
+           if err != nil {
+               panic(err)
+           }
+           time.Sleep(time.Second / 10)
+       }
+       w.Close()
+   }()

    p := mpb.New(
        mpb.WithWidth(60),
@@ -27,12 +39,12 @@ func main() {
        mpb.AppendDecorators(
            decor.EwmaETA(decor.ET_STYLE_GO, 30),
            decor.Name(" ] "),
-           decor.EwmaSpeed(decor.SizeB1024(0), "% .2f", 30),
+           decor.EwmaSpeed(decor.SizeB1024(0), "% .2f", 60),
        ),
    )

    // create proxy reader
-   proxyReader := bar.ProxyReader(reader)
+   proxyReader := bar.ProxyReader(r)
    defer proxyReader.Close()

    // copy from proxyReader, ignoring errors
jtolio commented 8 months ago

Hmm, I'm surprised that whether you use ProxyReader or ProxyWriter makes a difference - does this mean that the Proxy measurements are starting and also stopping clocks that only run during the Read or Write call? How might I reliably obtain the overall io.Copy speed, considering that either the Reader or Writer might be slow?