Skip to content

Instantly share code, notes, and snippets.

@sithumonline
Last active May 8, 2025 18:13
Show Gist options
  • Select an option

  • Save sithumonline/4d53b055b269ae2f74798790f5f23846 to your computer and use it in GitHub Desktop.

Select an option

Save sithumonline/4d53b055b269ae2f74798790f5f23846 to your computer and use it in GitHub Desktop.
Go vs Java v1
import json
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import seaborn as sns
from matplotlib.ticker import FuncFormatter
# Set the style
plt.style.use('ggplot')
sns.set_palette("Set2")
plt.rcParams['font.family'] = 'DejaVu Sans'
plt.rcParams['font.size'] = 12
plt.rcParams['axes.labelsize'] = 14
plt.rcParams['axes.titlesize'] = 16
plt.rcParams['figure.titlesize'] = 20
# Load the benchmark data
with open('hyperfines-01.json', 'r') as file:
benchmark_data = json.load(file)
# Extract relevant data
go_data = benchmark_data['results'][0]
java_data = benchmark_data['results'][1]
# Calculate percentage difference
def calc_percentage_diff(go_val, java_val):
return ((go_val - java_val) / go_val) * 100
# Prepare data for key metrics
metrics = ['Mean', 'Median', 'Min', 'Max', 'StdDev']
go_values = [go_data['mean'], go_data['median'], go_data['min'], go_data['max'], go_data['stddev']]
java_values = [java_data['mean'], java_data['median'], java_data['min'], java_data['max'], java_data['stddev']]
differences = [calc_percentage_diff(go_val, java_val) for go_val, java_val in zip(go_values, java_values)]
# Prepare data for CPU metrics
cpu_metrics = ['User CPU', 'System CPU']
go_cpu = [go_data['user'], go_data['system']]
java_cpu = [java_data['user'], java_data['system']]
cpu_differences = [calc_percentage_diff(go_val, java_val) for go_val, java_val in zip(go_cpu, java_cpu)]
# 1. Create main metrics comparison chart
fig, ax = plt.subplots(figsize=(12, 8))
# Set up bar positions
x = np.arange(len(metrics))
width = 0.35
# Create bars
go_bars = ax.bar(x - width/2, go_values, width, label='Go', color='#2563eb', alpha=0.9, edgecolor='black', linewidth=1)
java_bars = ax.bar(x + width/2, java_values, width, label='Java', color='#dc2626', alpha=0.9, edgecolor='black', linewidth=1)
# Customize chart
ax.set_ylabel('Time (seconds)', fontweight='bold')
ax.set_title('Go vs Java: Performance Metrics Comparison', fontweight='bold', pad=20)
ax.set_xticks(x)
ax.set_xticklabels(metrics, fontweight='bold')
ax.legend(fontsize=12)
# Format y-axis to show seconds
def seconds_formatter(x, pos):
return f'{x:.1f}s'
ax.yaxis.set_major_formatter(FuncFormatter(seconds_formatter))
# Add value labels on the bars
def add_labels(bars):
for bar in bars:
height = bar.get_height()
ax.annotate(f'{height:.1f}s',
xy=(bar.get_x() + bar.get_width() / 2, height),
xytext=(0, 3), # 3 points vertical offset
textcoords="offset points",
ha='center', va='bottom',
fontsize=10, fontweight='bold')
add_labels(go_bars)
add_labels(java_bars)
# Add percentage difference as text
for i, diff in enumerate(differences):
ax.annotate(f'{diff:.1f}% faster',
xy=(x[i], min(go_values[i], java_values[i]) / 2),
xytext=(0, 0),
textcoords="offset points",
ha='center', va='center',
fontsize=12, fontweight='bold',
color='green' if diff > 0 else 'red',
bbox=dict(boxstyle="round,pad=0.3", fc='white', alpha=0.8))
plt.grid(axis='y', linestyle='--', alpha=0.7)
plt.tight_layout()
plt.savefig('go_vs_java_metrics.png', dpi=300, bbox_inches='tight')
# 2. Create CPU usage comparison chart
fig, ax = plt.subplots(figsize=(10, 6))
# Set up bar positions
x = np.arange(len(cpu_metrics))
width = 0.35
# Create bars
go_cpu_bars = ax.bar(x - width/2, go_cpu, width, label='Go', color='#2563eb', alpha=0.9, edgecolor='black', linewidth=1)
java_cpu_bars = ax.bar(x + width/2, java_cpu, width, label='Java', color='#dc2626', alpha=0.9, edgecolor='black', linewidth=1)
# Customize chart
ax.set_ylabel('CPU Time (seconds)', fontweight='bold')
ax.set_title('Go vs Java: CPU Usage Comparison', fontweight='bold', pad=20)
ax.set_xticks(x)
ax.set_xticklabels(cpu_metrics, fontweight='bold')
ax.legend(fontsize=12)
ax.yaxis.set_major_formatter(FuncFormatter(seconds_formatter))
# Add value labels on the bars
add_labels(go_cpu_bars)
add_labels(java_cpu_bars)
# Add percentage difference as text
for i, diff in enumerate(cpu_differences):
ax.annotate(f'{diff:.1f}% less',
xy=(x[i], min(go_cpu[i], java_cpu[i]) / 2),
xytext=(0, 0),
textcoords="offset points",
ha='center', va='center',
fontsize=12, fontweight='bold',
color='green' if diff > 0 else 'red',
bbox=dict(boxstyle="round,pad=0.3", fc='white', alpha=0.8))
plt.grid(axis='y', linestyle='--', alpha=0.7)
plt.tight_layout()
plt.savefig('go_vs_java_cpu.png', dpi=300, bbox_inches='tight')
# 3. Create run-by-run comparison chart
fig, ax = plt.subplots(figsize=(14, 8))
# Prepare data for individual runs
runs = list(range(1, len(go_data['times']) + 1))
go_times = go_data['times']
java_times = java_data['times']
# Plot the lines
ax.plot(runs, go_times, 'o-', linewidth=3, markersize=10, label='Go', color='#2563eb')
ax.plot(runs, java_times, 'o-', linewidth=3, markersize=10, label='Java', color='#dc2626')
# Fill the area between lines
ax.fill_between(runs, go_times, java_times, where=(np.array(go_times) > np.array(java_times)),
color='#dc2626', alpha=0.2, interpolate=True)
ax.fill_between(runs, go_times, java_times, where=(np.array(go_times) <= np.array(java_times)),
color='#2563eb', alpha=0.2, interpolate=True)
# Add horizontal lines for means
ax.axhline(y=go_data['mean'], color='#2563eb', linestyle='--', alpha=0.7, linewidth=2)
ax.axhline(y=java_data['mean'], color='#dc2626', linestyle='--', alpha=0.7, linewidth=2)
# Annotate the means
ax.annotate(f"Go Mean: {go_data['mean']:.1f}s",
xy=(runs[-1], go_data['mean']),
xytext=(10, 0),
textcoords="offset points",
ha='left', va='center',
fontsize=12, fontweight='bold',
color='#2563eb',
bbox=dict(boxstyle="round,pad=0.3", fc='white', alpha=0.8))
ax.annotate(f"Java Mean: {java_data['mean']:.1f}s",
xy=(runs[-1], java_data['mean']),
xytext=(10, 0),
textcoords="offset points",
ha='left', va='center',
fontsize=12, fontweight='bold',
color='#dc2626',
bbox=dict(boxstyle="round,pad=0.3", fc='white', alpha=0.8))
# Customize chart
ax.set_xlabel('Run Number', fontweight='bold')
ax.set_ylabel('Execution Time (seconds)', fontweight='bold')
ax.set_title('Go vs Java: Run-by-Run Performance Comparison', fontweight='bold', pad=20)
ax.legend(fontsize=14, loc='upper right')
ax.set_xticks(runs)
ax.yaxis.set_major_formatter(FuncFormatter(seconds_formatter))
# Add additional annotations
plt.annotate(f'Java is {calc_percentage_diff(go_data["mean"], java_data["mean"]):.1f}% faster on average',
xy=(0.5, 0.05), xycoords='figure fraction',
ha='center', va='center',
fontsize=14, fontweight='bold',
bbox=dict(boxstyle="round,pad=0.5", fc='#dcfce7', ec='green', alpha=0.8))
plt.grid(True, linestyle='--', alpha=0.7)
plt.tight_layout()
plt.savefig('go_vs_java_runs.png', dpi=300, bbox_inches='tight')
# 4. Create summary card with key metrics
fig, ax = plt.subplots(figsize=(10, 6))
ax.axis('off')
# Create a table with the summary data
summary_data = {
'Metric': ['Mean Time', 'Median Time', 'Min Time', 'Max Time', 'Std Dev', 'User CPU', 'System CPU'],
'Go': [f'{go_data["mean"]:.2f}s', f'{go_data["median"]:.2f}s', f'{go_data["min"]:.2f}s',
f'{go_data["max"]:.2f}s', f'{go_data["stddev"]:.2f}s', f'{go_data["user"]:.2f}s', f'{go_data["system"]:.2f}s'],
'Java': [f'{java_data["mean"]:.2f}s', f'{java_data["median"]:.2f}s', f'{java_data["min"]:.2f}s',
f'{java_data["max"]:.2f}s', f'{java_data["stddev"]:.2f}s', f'{java_data["user"]:.2f}s', f'{java_data["system"]:.2f}s'],
'Java Advantage': [f'{calc_percentage_diff(go_data["mean"], java_data["mean"]):.2f}%',
f'{calc_percentage_diff(go_data["median"], java_data["median"]):.2f}%',
f'{calc_percentage_diff(go_data["min"], java_data["min"]):.2f}%',
f'{calc_percentage_diff(go_data["max"], java_data["max"]):.2f}%',
f'{calc_percentage_diff(go_data["stddev"], java_data["stddev"]):.2f}%',
f'{calc_percentage_diff(go_data["user"], java_data["user"]):.2f}%',
f'{calc_percentage_diff(go_data["system"], java_data["system"]):.2f}%']
}
# Convert to pandas DataFrame and create a styled table
df = pd.DataFrame(summary_data)
table = ax.table(cellText=df.values, colLabels=df.columns, cellLoc='center', loc='center', colWidths=[0.3, 0.2, 0.2, 0.3])
table.auto_set_font_size(False)
table.set_fontsize(12)
table.scale(1, 2)
# Style the table headers
for (i, j), cell in table.get_celld().items():
if i == 0: # Header row
cell.set_text_props(fontweight='bold', color='white')
cell.set_facecolor('#4b5563')
elif j == 0: # Metric column
cell.set_text_props(fontweight='bold')
elif j == 3: # Java Advantage column
value = float(cell.get_text().get_text().strip('%'))
if value > 0:
cell.set_facecolor('#dcfce7') # Light green for positive advantage
else:
cell.set_facecolor('#fee2e2') # Light red for negative advantage
# Add a title
plt.figtext(0.5, 0.95, 'Go vs Java: Performance Summary',
ha='center', fontsize=18, fontweight='bold')
plt.figtext(0.5, 0.02, 'Note: Positive percentages indicate Java is faster/more efficient',
ha='center', fontsize=10, style='italic')
plt.tight_layout()
plt.savefig('go_vs_java_summary.png', dpi=300, bbox_inches='tight')
# 5. Create a radar/spider chart comparing multiple metrics
fig = plt.figure(figsize=(10, 8))
ax = fig.add_subplot(111, polar=True)
# Prepare data - normalize all metrics to 0-1 scale where 1 is better
# For time metrics, lower is better, so we invert them
metrics_names = ['Mean Time', 'Median Time', 'Min Time', 'Max Time', 'Std Dev', 'User CPU', 'System CPU']
go_norm = []
java_norm = []
for i, metric in enumerate(metrics_names):
if i < 5: # Time metrics
# Higher is better for the radar chart, so invert (use reciprocal)
max_val = max(1/go_values[i], 1/java_values[i])
go_norm.append((1/go_values[i]) / max_val)
java_norm.append((1/java_values[i]) / max_val)
else: # CPU metrics
# Higher is better for the radar chart, so invert (use reciprocal)
idx = i - 5
max_val = max(1/go_cpu[idx], 1/java_cpu[idx])
go_norm.append((1/go_cpu[idx]) / max_val)
java_norm.append((1/java_cpu[idx]) / max_val)
# Complete the loop for the radar chart
go_norm.append(go_norm[0])
java_norm.append(java_norm[0])
metrics_names.append(metrics_names[0])
# Set up angle for each metric
angles = np.linspace(0, 2*np.pi, len(metrics_names)-1, endpoint=False).tolist()
angles += [angles[0]] # Close the loop
# Plot data
ax.plot(angles, go_norm, 'o-', linewidth=2, markersize=8, label='Go', color='#2563eb')
ax.plot(angles, java_norm, 'o-', linewidth=2, markersize=8, label='Java', color='#dc2626')
ax.fill(angles, go_norm, alpha=0.1, color='#2563eb')
ax.fill(angles, java_norm, alpha=0.1, color='#dc2626')
# Set labels and customize
ax.set_thetagrids(np.degrees(angles[:-1]), metrics_names[:-1])
ax.set_rlabel_position(0)
ax.set_rticks([0.25, 0.5, 0.75, 1])
ax.set_rmax(1)
ax.set_axisbelow(True)
ax.grid(True, linestyle='--', alpha=0.7)
# Add title and legend
plt.title('Go vs Java: Performance Metrics Comparison', size=18, y=1.1, fontweight='bold')
plt.legend(loc='upper right', bbox_to_anchor=(0.1, 0.1))
# Add note explaining the chart
plt.figtext(0.5, 0.01, 'Note: All metrics normalized. Further from center = better performance',
ha='center', fontsize=10, style='italic')
plt.tight_layout()
plt.savefig('go_vs_java_radar.png', dpi=300, bbox_inches='tight')
print("All visualizations have been generated successfully!")
{
"results": [
{
"command": "./workerpool",
"mean": 647.7689493191599,
"stddev": 147.3561668256607,
"median": 594.71315645256,
"user": 3954.229811139999,
"system": 33.14184544,
"min": 484.93092318106005,
"max": 865.30768430606,
"times": [
595.8115210150601,
706.99645072306,
865.30768430606,
821.85377455706,
834.34622589006,
512.45318218206,
500.89188443206,
561.48305501506,
484.93092318106005,
593.61479189006
],
"exit_codes": [
0,
0,
0,
0,
0,
0,
0,
0,
0,
0
]
},
{
"command": "java WorkerPool",
"mean": 485.65776722736007,
"stddev": 46.38658353813867,
"median": 491.95819580656007,
"user": 3447.20835794,
"system": 17.186345940000002,
"min": 416.95270722406,
"max": 544.8545639730601,
"times": [
467.48910147306003,
544.8545639730601,
501.64080172306,
451.20588126506004,
419.78766022306,
489.91337226506005,
494.00301934806004,
535.39166005606,
416.95270722406,
535.3389047230601
],
"exit_codes": [
0,
0,
0,
0,
0,
0,
0,
0,
0,
0
]
}
]
}
package main
import (
"log"
"sync"
"time"
)
// simulateWork performs a time-consuming calculation (e.g., Fibonacci).
func simulateWork(n int) int {
if n <= 1 {
return n
}
return simulateWork(n-1) + simulateWork(n-2)
}
// worker processes jobs from its dedicated jobs channel, does some work,
// then sends the result on the results channel.
func worker(id int, jobs <-chan int, results chan<- int, wg *sync.WaitGroup) {
defer wg.Done()
log.Printf("Worker %d: starting\n", id)
for j := range jobs {
log.Printf("Worker %d: started job %d\n", id, j)
result := simulateWork(45) // Example: Calculate the 45th Fibonacci number
log.Printf("Worker %d: finished job %d with result %d\n", id, j, result)
results <- result
}
}
func main() {
start := time.Now() // Start timer
const numJobs = 5 * 100
const numWorkers = 3 * 100
// Create a dedicated jobs channel for each worker
workerJobs := make([]chan int, numWorkers)
for i := range workerJobs {
workerJobs[i] = make(chan int, numJobs)
}
results := make(chan int, numJobs)
var wg sync.WaitGroup
// Start the worker goroutines
for w := 1; w <= numWorkers; w++ {
wg.Add(1)
go worker(w, workerJobs[w-1], results, &wg)
}
log.Println("Main: All workers started")
// Send jobs to specific workers
for j := 1; j <= numJobs; j++ {
workerID := (j - 1) % numWorkers // Assign jobs in a round-robin manner
workerJobs[workerID] <- j
log.Printf("Main: sent job %d to worker %d\n", j, workerID+1)
}
// Close all worker job channels
for _, ch := range workerJobs {
close(ch)
}
// Wait for all workers to finish, then close results
wg.Wait()
close(results)
// Collect and print results
for res := range results {
log.Printf("Result received: %d\n", res)
}
elapsed := time.Since(start) // End timer
log.Printf("Execution time: %s\n", elapsed)
}
import java.util.concurrent.*;
import java.util.logging.Logger;
import java.util.logging.Level;
import java.util.ArrayList;
import java.util.List;
public class WorkerPool {
private static final Logger logger = Logger.getLogger(WorkerPool.class.getName());
// simulateWork performs a time-consuming calculation (e.g., Fibonacci).
private static int simulateWork(int n) {
if (n <= 1) {
return n;
}
return simulateWork(n - 1) + simulateWork(n - 2);
}
// Worker class processes jobs from a blocking queue, does some work,
// then puts the result on the results queue.
static class Worker implements Runnable {
private final int id;
private final BlockingQueue<Integer> jobs;
private final BlockingQueue<Integer> results;
private final CountDownLatch latch;
public Worker(int id, BlockingQueue<Integer> jobs, BlockingQueue<Integer> results, CountDownLatch latch) {
this.id = id;
this.jobs = jobs;
this.results = results;
this.latch = latch;
}
@Override
public void run() {
try {
logger.info("Worker " + id + ": starting");
while (true) {
Integer job = jobs.take(); // Wait for a job
if (job == -1) { // Poison pill to signal no more jobs
break;
}
logger.info("Worker " + id + ": started job " + job);
int result = simulateWork(45); // Example: Calculate the 45th Fibonacci number
logger.info("Worker " + id + ": finished job " + job + " with result " + result);
results.put(result);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
logger.log(Level.SEVERE, "Worker interrupted", e);
} finally {
latch.countDown();
logger.info("Worker " + id + ": finished all jobs");
}
}
}
public static void main(String[] args) {
long startTime = System.nanoTime(); // Start timer
final int numJobs = 5 * 100;
final int numWorkers = 3 * 100;
// Create a dedicated jobs queue for each worker
List<BlockingQueue<Integer>> workerJobs = new ArrayList<>();
for (int i = 0; i < numWorkers; i++) {
workerJobs.add(new LinkedBlockingQueue<>());
}
// Create results queue
BlockingQueue<Integer> results = new LinkedBlockingQueue<>();
// Create a latch to wait for all workers to complete
CountDownLatch latch = new CountDownLatch(numWorkers);
// Start the worker threads
for (int w = 1; w <= numWorkers; w++) {
Thread workerThread = new Thread(new Worker(w, workerJobs.get(w - 1), results, latch));
workerThread.start();
}
logger.info("Main: All workers started");
// Send jobs to specific workers
for (int j = 1; j <= numJobs; j++) {
int workerID = (j - 1) % numWorkers; // Assign jobs in a round-robin manner
workerJobs.get(workerID).add(j);
logger.info("Main: sent job " + j + " to worker " + (workerID + 1));
}
// Send poison pills to signal workers to stop
for (int i = 0; i < numWorkers; i++) {
workerJobs.get(i).add(-1); // -1 is the poison pill
}
// Wait for all workers to finish
try {
latch.await();
// Collect and print results
int receivedResults = 0;
while (receivedResults < numJobs) {
Integer result = results.poll(1, TimeUnit.SECONDS); // Wait for results
if (result != null) {
logger.info("Result received: " + result);
receivedResults++;
}
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
logger.log(Level.SEVERE, "Main thread interrupted", e);
} finally {
logger.info("Main: All workers have completed their tasks");
}
long endTime = System.nanoTime(); // End timer
long elapsedTime = endTime - startTime;
logger.info("Execution time: " + (elapsedTime / 1_000_000) + " ms");
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment