source code
/* The Computer Language Benchmarks Game
* https://salsa.debian.org/benchmarksgame-team/benchmarksgame/
*
* contributed by The Go Authors.
* based on C program by Kevin Carson
* flag.Arg hack by Isaac Gouy
* goroutines by Atom
* *reset*
*/
package main
import (
"flag"
"fmt"
"runtime"
"strconv"
)
const LOG2_N_CPU = 2
const N_CPU = (1 << LOG2_N_CPU)
const LOG2_WORK_UNIT = 8
type Node struct {
left, right *Node
}
func bottomUpTree(depth int) *Node {
if depth <= 0 {
return &Node{}
}
return &Node{bottomUpTree(depth-1), bottomUpTree(depth-1)}
}
func go_bottomUpTree(depth int, goroutine_depth int) *Node {
if depth <= 0 {
return &Node{}
}
var left, right *Node
if goroutine_depth <= 0 {
left = bottomUpTree(depth-1)
right = bottomUpTree(depth-1)
} else {
left_chan := make(chan *Node)
right_chan := make(chan *Node)
go func() {
left_chan <- go_bottomUpTree(depth-1, goroutine_depth-1)
}()
go func() {
right_chan <- go_bottomUpTree(depth-1, goroutine_depth-1)
}()
left, right = <-left_chan, <-right_chan
}
return &Node{left, right}
}
func Go_bottomUpTree(depth int) *Node {
// Not enough work per goroutine to amortize goroutine creation
if depth < LOG2_N_CPU+LOG2_WORK_UNIT {
return bottomUpTree(depth)
}
return go_bottomUpTree(depth, LOG2_N_CPU)
}
func (n *Node) itemCheck() int {
if n.left == nil {
return 1
}
return 1 + n.left.itemCheck() + n.right.itemCheck()
}
func (n *Node) go_itemCheck(goroutine_depth int) int {
if n.left == nil {
return 1
}
var left, right int
if goroutine_depth <= 0 {
left = n.left.itemCheck()
right = n.right.itemCheck()
} else {
left_chan := make(chan int)
right_chan := make(chan int)
go func() {
left_chan <- n.left.go_itemCheck(goroutine_depth - 1)
}()
go func() {
right_chan <- n.right.go_itemCheck(goroutine_depth - 1)
}()
left, right = <-left_chan, <-right_chan
}
return 1 + left + right
}
func (n *Node) Go_itemCheck() int {
return n.go_itemCheck(LOG2_N_CPU)
}
var total_goroutines uint32 = 0
const minDepth = 4
func main() {
runtime.GOMAXPROCS(N_CPU)
n := 0
flag.Parse()
if flag.NArg() > 0 {
n, _ = strconv.Atoi(flag.Arg(0))
}
maxDepth := n
if minDepth+2 > n {
maxDepth = minDepth + 2
}
stretchDepth := maxDepth + 1
{
check := Go_bottomUpTree(stretchDepth).Go_itemCheck()
fmt.Printf("stretch tree of depth %d\t check: %d\n", stretchDepth, check)
}
longLivedTree := Go_bottomUpTree(maxDepth)
outputs := make(map[int]chan string)
control := make(chan byte, N_CPU) // This 'control' also puts a cap on memory usage
for _depth := minDepth; _depth <= maxDepth; _depth += 2 {
outputs[_depth] = make(chan string, 1)
go func(depth int) {
control <- 0
iterations := 1 << uint(maxDepth-depth+minDepth)
check := 0
// Avoid creating a lot of short-lived goroutines
if depth <= LOG2_N_CPU+LOG2_WORK_UNIT {
// No goroutines
for i := 1; i <= iterations; i++ {
check += bottomUpTree(depth).itemCheck()
}
} else {
// Use goroutines
for i := 1; i <= iterations; i++ {
check += Go_bottomUpTree(depth).Go_itemCheck()
}
}
outputs[depth] <- fmt.Sprintf("%d\t trees of depth %d\t check: %d\n",
iterations, depth, check)
<-control
}(_depth)
}
for depth := minDepth; depth <= maxDepth; depth += 2 {
fmt.Print(<-outputs[depth])
}
fmt.Printf("long lived tree of depth %d\t check: %d\n", maxDepth, longLivedTree.Go_itemCheck())
}
notes, command-line, and program output
NOTES:
64-bit Ubuntu quad core
go version go1.23.1 linux/amd64
GOAMD64=v2
Thu, 05 Sep 2024 23:58:31 GMT
MAKE:
/opt/src/go1.23.1/go/bin/go build -o binarytrees.go-5.go_run binarytrees.go-5.go
5.58s to complete and log all make actions
COMMAND LINE:
./binarytrees.go-5.go_run 21
PROGRAM OUTPUT:
stretch tree of depth 22 check: 8388607
2097152 trees of depth 4 check: 65011712
524288 trees of depth 6 check: 66584576
131072 trees of depth 8 check: 66977792
32768 trees of depth 10 check: 67076096
8192 trees of depth 12 check: 67100672
2048 trees of depth 14 check: 67106816
512 trees of depth 16 check: 67108352
128 trees of depth 18 check: 67108736
32 trees of depth 20 check: 67108832
long lived tree of depth 21 check: 4194303