Go Energy Analysis

Joule analyzes Go code with awareness of goroutines, channels, and Go's concurrency model. The energy cost of spawning goroutines, sending on channels, and slice operations is modeled at the picojoule level.

Quick Start

# Static energy analysis
joulec --lift go main.go

# Execute with energy tracking
joulec --lift-run go main.go

# Execute with energy optimization
joulec --energy-optimize --lift-run go main.go

Supported Features

CategoryFeatures
Typesint, int8/16/32/64, uint, float32/64, string, bool, byte, rune
Variablesvar, := short declaration, const, multiple assignment
Functionsfunc, multiple return values, named returns, closures, variadic ...
Control flowif/else (with init statement), for, for range, switch/case, select
SlicesCreation, append(), len(), cap(), slicing s[a:b], make(), copy()
Mapsmap[K]V, make(map[...]), index, delete, len(), comma-ok pattern
StructsDefinition, field access, methods (value/pointer receiver), embedding
Concurrencygo (goroutine spawn), chan, <- send/receive, make(chan T, N), close()
Deferdefer statement (LIFO cleanup)
Error handlingMultiple return (result, error), if err != nil pattern
Packagesfmt.Println, fmt.Sprintf, math.Sqrt, strings.*, strconv.*

Common Energy Anti-Patterns

1. Unbounded Goroutine Fan-Out

// BAD — spawns 100K goroutines, each has scheduling overhead
for _, item := range items {
    go process(item)  // 100K goroutines
}

// GOOD — bounded worker pool
ch := make(chan Item, 100)
for i := 0; i < runtime.NumCPU(); i++ {
    go func() {
        for item := range ch {
            process(item)
        }
    }()
}
for _, item := range items {
    ch <- item
}
close(ch)

Category: ALLOCATION | Severity: Critical | Savings: ~10x

Each goroutine has a minimum 2KB stack allocation. 100K goroutines = 200MB of stack memory + scheduling overhead.

2. Slice Append Without Pre-Allocation

// BAD — slice grows geometrically, copying data each time
var result []int
for i := 0; i < 10000; i++ {
    result = append(result, i)
}

// GOOD — pre-allocate known capacity
result := make([]int, 0, 10000)
for i := 0; i < 10000; i++ {
    result = append(result, i)
}

Category: ALLOCATION | Severity: Medium | Savings: ~2x

Without pre-allocation, append triggers ~14 reallocations and data copies to grow from 0 to 10,000 elements.

3. Map Iteration with Value Copy

// BAD — copies entire struct on each iteration
type BigStruct struct {
    Data [1024]byte
    Name string
}

for _, v := range bigMap {
    process(v)  // copies 1KB+ per iteration
}

// GOOD — iterate with index, access by reference
for k := range bigMap {
    process(&bigMap[k])
}

Category: MEMORY | Severity: Medium | Savings: ~3x for large values

4. String Concatenation in Loops

// BAD — O(n^2) string building
result := ""
for _, s := range parts {
    result += s
}

// GOOD — O(n) with strings.Builder
var b strings.Builder
for _, s := range parts {
    b.WriteString(s)
}
result := b.String()

Category: STRING | Severity: High | Savings: ~10x

Worked Example

package main

import "fmt"

func counter(id int, ch chan int) {
    sum := 0
    for i := 0; i < 1000; i++ {
        sum += i
    }
    ch <- sum
}

func main() {
    ch := make(chan int, 10)
    for i := 0; i < 10; i++ {
        go counter(i, ch)
    }

    total := 0
    for i := 0; i < 10; i++ {
        total += <-ch
    }
    fmt.Println(total)
}
$ joulec --lift go counter.go
Energy Analysis: counter.go

  counter    12.50 nJ  (confidence: 0.70)
  main        8.30 nJ  (confidence: 0.65)

  Total: 20.80 nJ

  Note: 10 goroutines detected. Energy estimate reflects single-thread
  execution model; actual concurrent execution may differ due to
  scheduling and synchronization overhead.

Limitations

  • No interface method dispatch (interfaces parsed but not resolved dynamically)
  • No struct embedding for method promotion
  • No generics (Go 1.18+ type parameters)
  • No select with complex multi-channel patterns
  • No panic/recover (panic is treated as program exit)
  • No init() functions
  • No package imports beyond fmt, math, strings, strconv
  • Goroutines are analyzed sequentially (no parallel energy modeling)