The Computer Language
24.09 Benchmarks Game

binary-trees Go #3 program

source code

/* The Computer Language Benchmarks Game
 * https://salsa.debian.org/benchmarksgame-team/benchmarksgame/
 *
 * Contributed by Tyler Kropp,
 * based on the C program by Jeremy Zerfas (C gcc #2),
 * which was based on the C++ program from
 * Jon Harrop, Alex Mizrahi, and Bruno Coutinho.
 */

package main

import (
    "fmt"
    "os"
    "strconv"
    "sync"
)

// Node is a binary tree node.
type Node struct {
    Left  *Node
    Right *Node
}

// CreateTree returns the root of a new binary tree with the given depth.
func CreateTree(depth int) *Node {
    if depth <= 0 {
        return &Node{}
    }
    return &Node{
        Left:  CreateTree(depth - 1),
        Right: CreateTree(depth - 1),
    }
}

// CalculateChecksum returns the total number of nodes in the binary tree.
func (n Node) CalculateChecksum() int {
    if n.Left == nil {
        return 1
    }
    return n.Left.CalculateChecksum() + n.Right.CalculateChecksum() + 1
}

// LongLivedTree is an exported variable used to prevent the long lived tree
// from being garbage collected.
var LongLivedTree *Node

func main() {
    // Set minDepth to 4 and maxDepth to the maximum of what was specified as
    // the argument to the program and minDepth+2.
    const minDepth = 4

    if len(os.Args) < 2 {
        panic("not enough arguments")
    }

    maxDepth, err := strconv.Atoi(os.Args[1])
    if err != nil {
        panic("could not parse first argument")
    }

    if maxDepth < minDepth+2 {
        maxDepth = minDepth + 2
    }

    // Create a stretch tree of depth maxDepth+1, compute its checksum, and
    // print its statistics.
    stretch := CreateTree(maxDepth + 1)
    check := stretch.CalculateChecksum()
    fmt.Printf("stretch tree of depth %v\t check: %v\n", maxDepth+1, check)

    // The LongLivedTree will be created while the rest of the trees are also
    // being processed. LongLivedTree will remain valid until the end of the
    // program, because it is exported from the package.
    longLivedTreeChecksum := make(chan int, 1)
    go func() {
        // Have one thread create the LongLivedTree of depth maxDepth, compute
        // the longLivedTreeChecksum, and then just leave the LongLivedTree
        // alone for a while while the rest of the binary trees finish
        // processing (which should have simultaneously been started to be
        // processed by any other available threads).
        LongLivedTree = CreateTree(maxDepth)
        longLivedTreeChecksum <- LongLivedTree.CalculateChecksum()
    }()

    // These will be used to store outputs for the various trees so the
    // statistics can be printed in the correct order later.
    outputBuffer := make([]string, (maxDepth-minDepth)/2+1)
    type depthOutput struct {
        depth int
        s     string
    }

    out := make(chan depthOutput, (maxDepth-minDepth)/2+1)
    done := make(chan struct{})
    wg := &sync.WaitGroup{}

    go func() {
        for o := range out {
            // Record the output for the trees at the given depth.
            i := (o.depth - minDepth) / 2
            outputBuffer[i] = o.s
        }
        done <- struct{}{}
    }()

    for depth := minDepth; depth <= maxDepth; depth += 2 {
        wg.Add(1)
        go func(depth int) {
            iterations := 1 << (maxDepth - depth + minDepth)
            totalChecksum := 0

            for i := 1; i <= iterations; i++ {
                // Create a bunch of binary trees of depth depth, compute each
                // of their checksums, and add them to the totalChecksum.
                tree := CreateTree(depth)
                totalChecksum += tree.CalculateChecksum()
            }

            out <- depthOutput{
                depth,
                fmt.Sprintf("%v\t trees of depth %v\t check: %v",
                    iterations, depth, totalChecksum),
            }
            wg.Done()
        }(depth)
    }

    wg.Wait()
    close(out)
    <-done

    // Print the statistics for all of the various tree depths.
    for _, s := range outputBuffer {
        fmt.Println(s)
    }

    // Print the statistics for the LongLivedTree that was processed earlier.
    // Note that although the LongLivedTree variable isn't used here, it still
    // is in scope and valid to use until the end of the program.
    fmt.Printf("long lived tree of depth %v\t check: %v\n",
        maxDepth, <-longLivedTreeChecksum)
}
    

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:53:33 GMT

MAKE:
/opt/src/go1.23.1/go/bin/go build -o binarytrees.go-3.go_run binarytrees.go-3.go

5.65s to complete and log all make actions

COMMAND LINE:
 ./binarytrees.go-3.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