Software consumes energy – on servers, during builds, in deployment. Every millijoule saved counts: for the environment and your server bills. Until now, we lacked the tools to measure energy consumption at the code level. Common benchmarks track speed, not real-world impact.
Oaklean is our open-source tool built to solve exactly this. It lets us measure energy consumption down to individual lines of code. For the first time, we get hard data on where our software actually uses energy, not just theoretical assumptions.
How does Bun perform in this comparison?
We set out to answer this question by running measurements in a real-world project.
To get precise data, we used the powermetrics tool on macOS, which lets us track energy consumption at the process level. The command to start measurements looks like this:
sudo powermetrics --show-process-energy --sample-rate 500 --buffer-size 0 -f plist -o ~/tmp/energy_measurements_node.plist
Our measurements aren’t based on theoretical benchmarks – they use a real customer project. We launched the application once with Node.js and once with Bun, put it under full load, and used a reproducible dataset. A simple bash script controlled the measurement: it ran the process under these conditions for 30 seconds and saved the data to the plist file.
The critical part is the evaluation. Since powermetrics saves data to a plist file by default, we need to interpret it purposefully.
To convert raw powermetrics data into usable energy values, we use the @oaklean/profiler-core library. Our script reads the plist file and selectively evaluates it:
import fs from 'fs'
import plist from 'plist'
import {
PowerMetricsData,
IPowerMetricsOutputFormat,
MetricsDataCollection,
MetricsDataCollectionType,
NanoSeconds_BigInt
} from '@oaklean/profiler-core'
// Load Plist data
const inputPlist = process.argv[3] || 'power_metrics.plist'
const content = fs.readFileSync(inputPlist).toString()
const contents = content.split('\x00')
const pid = process.argv[2] ? parseInt(process.argv[2]) : 0
const collection = new MetricsDataCollection(
pid,
MetricsDataCollectionType.PowerMetricsPerProcess,
data,
{
startTime: BigInt(0),
stopTime: BigInt(0)
}
)
let totalEnergy = 0
for (const item of collection.items) {
if (item.processIsPresent(pid)) {
// Hier passiert die Magie: Anteil des Prozesses an der CPU-Energie
const energy = item.energyPortionOfProcess(pid) * item.cpuEnergy()
totalEnergy += energy
}
}
console.log(totalEnergy)
This script calculates the exact share of total CPU energy consumption our process uses. Integrating @oaklean/profiler-core abstracts away the complex interpretation of powermetrics data, giving us a comparable energy value.
The numbers speak for themselves. In our tests (30 seconds runtime under load), we got the following values:
| Runtime | Energy Consumption |
|---|---|
| Bun | 1930 mJ |
| Node.js (v20) | 3160 mJ |
| Node.js (v25) | 4726 mJ |
In this project, Bun uses almost 40% less energy than Node.js v20, and even less than half of Node.js v25.
Your choice of runtime directly impacts the ecological footprint of your applications. Bun isn’t just fast with excellent developer experience – it’s a real game changer for energy efficiency.
This raises an exciting question: What would energy measurement built directly into the Bun engine look like, similar to what we implemented for the Node.js engine? Even without native Bun engine measurement, Oaklean already lets you identify energy bottlenecks in Bun projects today.
If you care about Green Coding and want to cut your server costs, switching to Bun is a logical step.