$ go test -ldflags '-checklinkname=0' -bench=. -benchmem . ./...
goos: linux
goarch: amd64
cpu: 11th Gen Intel(R) Core(TM) i7-1165G7 @ 2.80GHz
BenchmarkChan-8 61932 19671 ns/op 536 B/op 10 allocs/op
BenchmarkIter-8 3740444 323.4 ns/op 120 B/op 4 allocs/op
BenchmarkJust-8 5439428 200.8 ns/op 120 B/op 4 allocs/op
BenchmarkDirect-8 7154991 177.1 ns/op 120 B/op 4 allocs/op
BenchmarkCoro2-8 544339 2007 ns/op 360 B/op 13 allocs/op
PASS
Last active
June 30, 2024 15:29
-
-
Save rprtr258/f7af255831dd0b6db3b969c9c1ea7319 to your computer and use it in GitHub Desktop.
golang coroutines benchmark
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import "strings" | |
func chan_fromstring(input string) <-chan byte { | |
ch := make(chan byte) | |
go func() { | |
for _, c := range input { | |
ch <- byte(c) | |
} | |
close(ch) | |
}() | |
return ch | |
} | |
func chan_decode(input <-chan byte) <-chan byte { | |
ch := make(chan byte) | |
go func() { | |
for cc := range input { | |
switch cc { | |
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': | |
d := cc - '0' | |
c := <-input | |
for j := 0; j <= int(d); j++ { | |
ch <- c | |
} | |
case '.': | |
ch <- '.' | |
default: | |
ch <- cc | |
} | |
} | |
close(ch) | |
}() | |
return ch | |
} | |
func chan_chunked(input <-chan byte) <-chan byte { | |
ch := make(chan byte) | |
go func() { | |
n := 0 | |
for cc := range input { | |
ch <- cc | |
n++ | |
if n == 3 { | |
ch <- ' ' | |
n = 0 | |
} | |
} | |
close(ch) | |
}() | |
return ch | |
} | |
func chan_f(input string) string { | |
var sb strings.Builder | |
for c := range chan_chunked(chan_decode(chan_fromstring(input))) { | |
sb.WriteByte(c) | |
} | |
return sb.String() | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"strings" | |
exp_coro "github.com/rprtr258/fun/exp/coro" | |
) | |
func coro_fromstring(input string) exp_coro.Coro[struct{}, byte] { | |
return exp_coro.New(func(yield func(byte) struct{}) { | |
for _, c := range input { | |
yield(byte(c)) | |
} | |
}) | |
} | |
func coro_decode(getchar func(struct{}) (byte, bool)) exp_coro.Coro[struct{}, byte] { | |
return exp_coro.New(func(yield func(byte) struct{}) { | |
for { | |
cc, ok := getchar(struct{}{}) | |
if !ok { | |
return | |
} | |
switch cc { | |
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': | |
d := cc - '0' | |
cc, ok := getchar(struct{}{}) | |
if !ok { | |
return | |
} | |
for j := 0; j <= int(d); j++ { | |
yield(cc) | |
} | |
case '.': | |
yield('.') | |
return | |
default: | |
yield(cc) | |
} | |
} | |
}) | |
} | |
func coro_chunked(getchar func(struct{}) (byte, bool)) exp_coro.Coro[struct{}, byte] { | |
n := 0 | |
return exp_coro.New(func(yield func(byte) struct{}) { | |
for { | |
cc, ok := getchar(struct{}{}) | |
if !ok { | |
return | |
} | |
yield(cc) | |
n++ | |
if n == 3 { | |
yield(' ') | |
n = 0 | |
} | |
} | |
}) | |
} | |
func coro_f(input string) string { | |
var sb strings.Builder | |
c1 := coro_fromstring(input) | |
defer c1.Cancel() | |
c2 := coro_decode(c1.Resume) | |
defer c2.Cancel() | |
c3 := coro_chunked(c2.Resume) | |
defer c3.Cancel() | |
for { | |
c, ok := c3.Resume(struct{}{}) | |
if !ok { | |
break | |
} | |
sb.WriteByte(c) | |
} | |
return sb.String() | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"iter" | |
"strings" | |
_ "unsafe" | |
) | |
func coro2_fromstring(input string) iter.Seq[byte] { | |
return func(yield func(byte) bool) { | |
for _, c := range []byte(input) { | |
if !yield(c) { | |
return | |
} | |
} | |
} | |
} | |
type coro struct{} | |
//go:linkname newcoro runtime.newcoro | |
func newcoro(func(*coro)) *coro | |
//go:linkname coroswitch runtime.coroswitch | |
func coroswitch(*coro) | |
// iter.Pull w/ no panicking | |
func Pull[V any](seq iter.Seq[V]) (next func() (V, bool), stop func()) { | |
var ( | |
v V | |
ok bool | |
done bool | |
) | |
c := newcoro(func(c *coro) { | |
if done { | |
return | |
} | |
defer func() { done = true }() // Invalidate iterator | |
seq(func(v1 V) bool { | |
if done { | |
return false | |
} | |
v, ok = v1, true | |
coroswitch(c) | |
return !done | |
}) | |
v, ok = *new(V), false | |
}) | |
return func() (v1 V, ok1 bool) { | |
if done { | |
return | |
} | |
coroswitch(c) | |
return v, ok | |
}, func() { | |
if !done { | |
done = true | |
coroswitch(c) | |
} | |
} | |
} | |
func coro2_decode(seq iter.Seq[byte]) iter.Seq[byte] { | |
return func(yield func(byte) bool) { | |
getchar, stop := Pull(seq) | |
defer stop() | |
for { | |
cc, ok := getchar() | |
if !ok { | |
return | |
} | |
switch cc { | |
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': | |
d := cc - '0' | |
cc, ok := getchar() | |
if !ok { | |
return | |
} | |
for j := 0; j <= int(d); j++ { | |
if !yield(cc) { | |
return | |
} | |
} | |
case '.': | |
yield('.') | |
return | |
default: | |
if !yield(cc) { | |
return | |
} | |
} | |
} | |
} | |
} | |
func coro2_chunked(seq iter.Seq[byte]) iter.Seq[byte] { | |
n := 0 | |
return func(yield func(byte) bool) { | |
for cc := range seq { | |
if !yield(cc) { | |
return | |
} | |
n++ | |
if n == 3 { | |
if !yield(' ') { | |
return | |
} | |
n = 0 | |
} | |
} | |
} | |
} | |
func coro2_f(input string) string { | |
var sb strings.Builder | |
for c := range coro2_chunked(coro2_decode(coro2_fromstring(input))) { | |
sb.WriteByte(c) | |
} | |
return sb.String() | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import "strings" | |
func just_f(input string) string { | |
var sb strings.Builder | |
state := byte(0) | |
n := 0 | |
// iterating over, so no lookahead | |
for _, c := range []byte(input) { | |
switch state { | |
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': | |
d := state - '0' | |
for j := 0; j <= int(d); j++ { | |
sb.WriteByte(c) | |
n++ | |
if n == 3 { | |
sb.WriteByte(' ') | |
n = 0 | |
} | |
} | |
state = 0 | |
case '.': | |
sb.WriteByte('.') | |
n++ | |
if n == 3 { | |
sb.WriteByte(' ') | |
n = 0 | |
} | |
default: | |
switch c { | |
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': | |
state = c | |
default: | |
sb.WriteByte(c) | |
n++ | |
if n == 3 { | |
sb.WriteByte(' ') | |
n = 0 | |
} | |
} | |
} | |
} | |
return sb.String() | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import "strings" | |
// with lookahead | |
func direct_f(input string) string { | |
var sb strings.Builder | |
chunkSize := 0 | |
i := 0 | |
LOOP: | |
var c byte | |
if i == len(input) { | |
goto LOOP_END | |
} | |
c = input[i] | |
if '0' <= c && c <= '9' { | |
times := c - '0' | |
i++ | |
c = input[i] // lookahead | |
LOOP2: | |
if times == 0 { | |
goto LOOP_END2 | |
} | |
sb.WriteByte(c) | |
chunkSize++ | |
if chunkSize == 3 { | |
sb.WriteByte(' ') | |
chunkSize = 0 | |
} | |
times-- | |
goto LOOP2 | |
LOOP_END2: | |
} | |
sb.WriteByte(c) | |
chunkSize++ | |
if chunkSize == 3 { | |
sb.WriteByte(' ') | |
chunkSize = 0 | |
} | |
i++ | |
goto LOOP | |
LOOP_END: | |
return sb.String() | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import "testing" | |
const input = "A2B5E3426FG0ZYW3210PQ89R." | |
const output = "ABB BEE EEE E44 446 66F GZY W22 220 0PQ 999 999 999 R." | |
func BenchmarkChan(b *testing.B) { | |
for i := 0; i < b.N; i++ { | |
if chan_f(input) != output { | |
b.FailNow() | |
} | |
} | |
} | |
func BenchmarkIter(b *testing.B) { | |
for i := 0; i < b.N; i++ { | |
if iter_f(input) != output { | |
b.FailNow() | |
} | |
} | |
} | |
func BenchmarkJust(b *testing.B) { | |
for i := 0; i < b.N; i++ { | |
if actual := just_f(input); actual != output { | |
b.Logf("expected %q, got %q", output, actual) | |
b.FailNow() | |
} | |
} | |
} | |
func BenchmarkDirect(b *testing.B) { | |
for i := 0; i < b.N; i++ { | |
if actual := direct_f(input); actual != output { | |
b.Logf("expected %q, got %q", output, actual) | |
b.FailNow() | |
} | |
} | |
} | |
func BenchmarkCoro(b *testing.B) { | |
b.SkipNow() // panics | |
for i := 0; i < b.N; i++ { | |
if actual := coro_f(input); actual != output { | |
b.Logf("expected %q, got %q", output, actual) | |
b.FailNow() | |
} | |
} | |
} | |
func BenchmarkCoro2(b *testing.B) { | |
for i := 0; i < b.N; i++ { | |
if actual := coro2_f(input); actual != output { | |
b.Logf("expected %q, got %q", output, actual) | |
b.FailNow() | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module a | |
go 1.23rc1 | |
require github.com/rprtr258/fun v0.0.17-0.20240630142417-330a75aa6b36 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import "strings" | |
type iterator = func(func(byte)) | |
func iter_fromstring(input string) iterator { | |
return func(yield func(byte)) { | |
for _, c := range input { | |
yield(byte(c)) | |
} | |
} | |
} | |
func iter_decode(input iterator) iterator { | |
return func(yield func(byte)) { | |
state := byte(0) | |
input(func(cc byte) { | |
switch state { | |
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': | |
d := state - '0' | |
for j := 0; j <= int(d); j++ { | |
yield(cc) | |
} | |
state = 0 | |
case '.': | |
yield('.') | |
default: | |
switch cc { | |
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': | |
state = cc | |
default: | |
yield(cc) | |
} | |
} | |
}) | |
} | |
} | |
func iter_chunked(input iterator) iterator { | |
return func(yield func(byte)) { | |
n := 0 | |
input(func(cc byte) { | |
yield(cc) | |
n++ | |
if n == 3 { | |
yield(' ') | |
n = 0 | |
} | |
}) | |
} | |
} | |
func iter_f(input string) string { | |
var sb strings.Builder | |
iter_chunked(iter_decode(iter_fromstring(input)))(func(c byte) { | |
sb.WriteByte(c) | |
}) | |
return sb.String() | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment