Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gnolang: native println and print could be made more efficient given that they are also reported as a candidate for misuse in gas consumption #3951

Closed
odeke-em opened this issue Mar 16, 2025 · 0 comments · Fixed by #3952

Comments

@odeke-em
Copy link
Contributor

Description

While studying the fix for adding gas metering for print and println, I saw this nice PR #3819

While examining and studying the code I noticed this pattern

for i := 0; i < xvl; i++ {
ev := xv.TV.GetPointerAtIndexInt(m.Store, i).Deref()
ss[i] = ev.Sprint(m)
}
rs := strings.Join(ss, " ") + "\n"
if debug {
println("DEBUG/stdout: " + rs)
}
m.Output.Write([]byte(rs))
},

in which we firstly construct a slice of strings to insert the Sprint-ed/converted values, then concatenate using " " and then convert into a byteslice to then invoke m.Output.Write.

println and print are very important and they should be really fast!

Improvements

  • io.WriteString already exists and its purpose is to avoid that expensive []byte(s) conversion
  • Concatenation with " " is userland consumes RAM and CPU
  • Instead of the slice of strings, we can invoke io.WriteString(m.Output, segment) and then add a space where necessary
  • Add tests and benchmarks

Results

Given this Gno program

                package p
                func main() {
                    for i := 0; i < 1000; i++ {
                        println("abcdeffffffffffffffff1222 11111   11111")
                    }
                }

On my improvements in the suggestions, I can see this performance improvement

$ benchstat before_println.txt after_println3.txt 
name          old time/op    new time/op    delta
GnoPrintln-8    1.94ms ± 2%    1.86ms ± 4%   -4.02%  (p=0.001 n=9+9)

name          old alloc/op   new alloc/op   delta
GnoPrintln-8    1.92MB ± 0%    1.81MB ± 0%   -5.92%  (p=0.000 n=10+10)

name          old allocs/op  new allocs/op  delta
GnoPrintln-8     19.0k ± 0%     16.0k ± 0%  -15.76%  (p=0.002 n=8+10)

which is as efficient as we can get with legible code and will greatly reduce bloat on the VM.

odeke-em added a commit to odeke-em/gno that referenced this issue Mar 16, 2025
This change is the result of seeing print/println being
reported in bugs as needing gas metering but on examination,
noticed that the code causes a RAM and CPU bloat, so this
makes an improvement in all dimensions

```shell
$ benchstat before_println.txt after_println3.txt
name          old time/op    new time/op    delta
GnoPrintln-8    1.94ms ± 2%    1.86ms ± 4%   -4.02%  (p=0.001 n=9+9)

name          old alloc/op   new alloc/op   delta
GnoPrintln-8    1.92MB ± 0%    1.81MB ± 0%   -5.92%  (p=0.000 n=10+10)

name          old allocs/op  new allocs/op  delta
GnoPrintln-8     19.0k ± 0%     16.0k ± 0%  -15.76%  (p=0.002 n=8+10)
```

Fixes gnolang#3951
odeke-em added a commit to odeke-em/gno that referenced this issue Mar 16, 2025
This change is the result of seeing print/println being
reported in bugs as needing gas metering but on examination,
noticed that the code causes a RAM and CPU bloat, so this
makes an improvement in all dimensions

```shell
$ benchstat before_println.txt after_println3.txt
name          old time/op    new time/op    delta
GnoPrintln-8    1.94ms ± 2%    1.86ms ± 4%   -4.02%  (p=0.001 n=9+9)

name          old alloc/op   new alloc/op   delta
GnoPrintln-8    1.92MB ± 0%    1.81MB ± 0%   -5.92%  (p=0.000 n=10+10)

name          old allocs/op  new allocs/op  delta
GnoPrintln-8     19.0k ± 0%     16.0k ± 0%  -15.76%  (p=0.002 n=8+10)
```

Fixes gnolang#3951
odeke-em added a commit to odeke-em/gno that referenced this issue Mar 16, 2025
This change is the result of seeing print/println being
reported in bugs as needing gas metering but on examination,
noticed that the code causes a RAM and CPU bloat, so this
makes an improvement in all dimensions

```shell
$ benchstat before_println.txt after_println3.txt
name          old time/op    new time/op    delta
GnoPrintln-8    1.94ms ± 2%    1.86ms ± 4%   -4.02%  (p=0.001 n=9+9)

name          old alloc/op   new alloc/op   delta
GnoPrintln-8    1.92MB ± 0%    1.81MB ± 0%   -5.92%  (p=0.000 n=10+10)

name          old allocs/op  new allocs/op  delta
GnoPrintln-8     19.0k ± 0%     16.0k ± 0%  -15.76%  (p=0.002 n=8+10)
```

Fixes gnolang#3951
odeke-em added a commit to odeke-em/gno that referenced this issue Mar 16, 2025
This change is the result of seeing print/println being
reported in bugs as needing gas metering but on examination,
noticed that the code causes a RAM and CPU bloat, so this
makes an improvement in all dimensions by invoking:
* io.WriteString(m.Output, segments) instead of m.Output.Write([]byte(segments))
* in cases where `!debug`, we don't need to construct the concatenated
  string delimited by " " and can instead write the Sprint-ed string
segments as we encounter them

```shell
$ benchstat before_println.txt after_println3.txt
name          old time/op    new time/op    delta
GnoPrintln-8    1.94ms ± 2%    1.86ms ± 4%   -4.02%  (p=0.001 n=9+9)

name          old alloc/op   new alloc/op   delta
GnoPrintln-8    1.92MB ± 0%    1.81MB ± 0%   -5.92%  (p=0.000 n=10+10)

name          old allocs/op  new allocs/op  delta
GnoPrintln-8     19.0k ± 0%     16.0k ± 0%  -15.76%  (p=0.002 n=8+10)
```

Fixes gnolang#3951
odeke-em added a commit to odeke-em/gno that referenced this issue Mar 16, 2025
This change is the result of seeing print/println being
reported in bugs as needing gas metering but on examination,
noticed that the code causes a RAM and CPU bloat, so this
makes an improvement in all dimensions by invoking:
* io.WriteString(m.Output, segments) instead of m.Output.Write([]byte(segments))
* in cases where `!debug`, we don't need to construct the concatenated
  string delimited by " " and can instead write the Sprint-ed string
segments as we encounter them

```shell
$ benchstat before_println.txt after_println3.txt
name          old time/op    new time/op    delta
GnoPrintln-8    1.94ms ± 2%    1.86ms ± 4%   -4.02%  (p=0.001 n=9+9)

name          old alloc/op   new alloc/op   delta
GnoPrintln-8    1.92MB ± 0%    1.81MB ± 0%   -5.92%  (p=0.000 n=10+10)

name          old allocs/op  new allocs/op  delta
GnoPrintln-8     19.0k ± 0%     16.0k ± 0%  -15.76%  (p=0.002 n=8+10)
```

Fixes gnolang#3951
odeke-em added a commit to odeke-em/gno that referenced this issue Mar 18, 2025
This change is the result of seeing print/println being
reported in bugs as needing gas metering but on examination,
noticed that the code causes a RAM and CPU bloat, so this
makes an improvement in all dimensions by invoking:
* io.WriteString(m.Output, segments) instead of m.Output.Write([]byte(segments))
* in cases where `!debug`, we don't need to construct the concatenated
  string delimited by " " and can instead write the Sprint-ed string
segments as we encounter them

```shell
$ benchstat before_println.txt after_println3.txt
name          old time/op    new time/op    delta
GnoPrintln-8    1.94ms ± 2%    1.86ms ± 4%   -4.02%  (p=0.001 n=9+9)

name          old alloc/op   new alloc/op   delta
GnoPrintln-8    1.92MB ± 0%    1.81MB ± 0%   -5.92%  (p=0.000 n=10+10)

name          old allocs/op  new allocs/op  delta
GnoPrintln-8     19.0k ± 0%     16.0k ± 0%  -15.76%  (p=0.002 n=8+10)
```

Fixes gnolang#3951
odeke-em added a commit to odeke-em/gno that referenced this issue Mar 20, 2025
This change is the result of seeing print/println being
reported in bugs as needing gas metering but on examination,
noticed that the code causes a RAM and CPU bloat, so this
makes an improvement in all dimensions by invoking:
* io.WriteString(m.Output, segments) instead of m.Output.Write([]byte(segments))
* in cases where `!debug`, we don't need to construct the concatenated
  string delimited by " " and can instead write the Sprint-ed string
segments as we encounter them

```shell
$ benchstat before_println.txt after_println3.txt
name          old time/op    new time/op    delta
GnoPrintln-8    1.94ms ± 2%    1.86ms ± 4%   -4.02%  (p=0.001 n=9+9)

name          old alloc/op   new alloc/op   delta
GnoPrintln-8    1.92MB ± 0%    1.81MB ± 0%   -5.92%  (p=0.000 n=10+10)

name          old allocs/op  new allocs/op  delta
GnoPrintln-8     19.0k ± 0%     16.0k ± 0%  -15.76%  (p=0.002 n=8+10)
```

Fixes gnolang#3951
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Development

Successfully merging a pull request may close this issue.

1 participant