diff --git a/run_all.sh b/run_all.sh index c29f866..17320e8 100755 --- a/run_all.sh +++ b/run_all.sh @@ -123,33 +123,38 @@ run_program() { local result local peak_memory=0 local peak_cpu=0 + local timeline_dir="timelines/$name" + + # Create timeline directory + mkdir -p "$timeline_dir" # Run 4 times, discard first run (warmup) for i in 1 2 3 4; do - local mem_profile_file="/tmp/${name}_mem_$$_$i" - local cpu_profile_file="/tmp/${name}_cpu_$$_$i" + local timeline_file="$timeline_dir/run_$i.tsv" local start=$(date +%s%N) # Run program and capture PID for resource profiling - if [ "$i" -gt 1 ]; then - # For runs 2-4, profile memory and CPU - "$@" 2>/dev/null & - local pid=$! + "$@" 2>/dev/null & + local pid=$! + + # Profile resources in background + local resources=$(profile_resources "$pid" "/dev/null" "/dev/null" "$timeline_file") + local peak_mem=$(echo "$resources" | awk '{print $1}') + local peak_cpu_val=$(echo "$resources" | awk '{print $2}') + + # Wait for process to complete + wait "$pid" 2>/dev/null + local exit_code=$? + + local end=$(date +%s%N) + local elapsed=$(( (end - start) / 1000000 )) + + if [ $exit_code -eq 0 ]; then + # Get result for verification + result=$("$@" 2>/dev/null) - # Profile resources in background - local resources=$(profile_resources "$pid" "$mem_profile_file" "$cpu_profile_file") - local peak_mem=$(echo "$resources" | awk '{print $1}') - local peak_cpu_val=$(echo "$resources" | awk '{print $2}') - - # Wait for process to complete - wait "$pid" 2>/dev/null - local exit_code=$? - - if [ $exit_code -eq 0 ]; then - result=$("$@" 2>/dev/null) - local end=$(date +%s%N) - local elapsed=$(( (end - start) / 1000000 )) - + if [ "$i" -gt 1 ]; then + # Only count runs 2-4 for averages total_time=$((total_time + elapsed)) total_memory=$((total_memory + peak_mem)) total_cpu=$((total_cpu + peak_cpu_val)) @@ -164,23 +169,11 @@ run_program() { if verify "$result" "$DECIMALS"; then ((success_count++)) fi - else - echo -e "${RED}ERROR${NC}" - results+=("999999 $name ERROR") - rm -f "$mem_profile_file" "$cpu_profile_file" - return fi - - rm -f "$mem_profile_file" "$cpu_profile_file" else - # First run (warmup) - just execute without profiling - if result=$("$@" 2>/dev/null); then - : - else - echo -e "${RED}ERROR${NC}" - results+=("999999 $name ERROR") - return - fi + echo -e "${RED}ERROR${NC}" + results+=("999999 $name ERROR") + return fi done @@ -194,10 +187,10 @@ run_program() { local peak_memory_mb=$((peak_memory / 1024)) if [ $success_count -eq 3 ]; then - echo -e "${GREEN}SUCCESS${NC} $avg_time ms, ${BLUE}${avg_memory_mb}MB mem, ${YELLOW}${avg_cpu}% CPU avg, ${peak_cpu}% CPU peak${NC}" + echo -e "${GREEN}SUCCESS${NC} $avg_time ms, ${BLUE}${avg_memory_mb}MB avg / ${peak_memory_mb}MB peak, ${YELLOW}${avg_cpu}% CPU avg / ${peak_cpu}% CPU peak${NC}" results+=("$avg_time $name SUCCESS $avg_memory $peak_memory $avg_cpu $peak_cpu") else - echo -e "${RED}FAILED${NC} $avg_time ms, ${BLUE}${avg_memory_mb}MB mem, ${YELLOW}${avg_cpu}% CPU avg, ${peak_cpu}% CPU peak${NC}" + echo -e "${RED}FAILED${NC} $avg_time ms, ${BLUE}${avg_memory_mb}MB avg / ${peak_memory_mb}MB peak, ${YELLOW}${avg_cpu}% CPU avg / ${peak_cpu}% CPU peak${NC}" results+=("$avg_time $name FAILED $avg_memory $peak_memory $avg_cpu $peak_cpu") fi } diff --git a/timelines/Bash/run_1.tsv b/timelines/Bash/run_1.tsv new file mode 100644 index 0000000..29177d6 --- /dev/null +++ b/timelines/Bash/run_1.tsv @@ -0,0 +1 @@ +13 2000 0 diff --git a/timelines/Bash/run_2.tsv b/timelines/Bash/run_2.tsv new file mode 100644 index 0000000..1c33258 --- /dev/null +++ b/timelines/Bash/run_2.tsv @@ -0,0 +1 @@ +11 2000 0 diff --git a/timelines/Bash/run_3.tsv b/timelines/Bash/run_3.tsv new file mode 100644 index 0000000..90f4ad3 --- /dev/null +++ b/timelines/Bash/run_3.tsv @@ -0,0 +1 @@ +16 2000 0 diff --git a/timelines/Bash/run_4.tsv b/timelines/Bash/run_4.tsv new file mode 100644 index 0000000..1c33258 --- /dev/null +++ b/timelines/Bash/run_4.tsv @@ -0,0 +1 @@ +11 2000 0 diff --git a/timelines/Brainfuck/run_1.tsv b/timelines/Brainfuck/run_1.tsv new file mode 100644 index 0000000..107e706 --- /dev/null +++ b/timelines/Brainfuck/run_1.tsv @@ -0,0 +1,3 @@ +11 2000 0 +35 2016 0 +59 2016 0 diff --git a/timelines/Brainfuck/run_2.tsv b/timelines/Brainfuck/run_2.tsv new file mode 100644 index 0000000..065aa86 --- /dev/null +++ b/timelines/Brainfuck/run_2.tsv @@ -0,0 +1,2 @@ +8 2000 0 +29 0 0 diff --git a/timelines/Brainfuck/run_3.tsv b/timelines/Brainfuck/run_3.tsv new file mode 100644 index 0000000..5e24c3a --- /dev/null +++ b/timelines/Brainfuck/run_3.tsv @@ -0,0 +1,2 @@ +9 2000 0 +34 2000 diff --git a/timelines/Brainfuck/run_4.tsv b/timelines/Brainfuck/run_4.tsv new file mode 100644 index 0000000..71f3190 --- /dev/null +++ b/timelines/Brainfuck/run_4.tsv @@ -0,0 +1,2 @@ +8 2000 0 +30 2000 0 diff --git a/timelines/C++/run_1.tsv b/timelines/C++/run_1.tsv new file mode 100644 index 0000000..2bddd60 --- /dev/null +++ b/timelines/C++/run_1.tsv @@ -0,0 +1 @@ +11 240 diff --git a/timelines/C++/run_2.tsv b/timelines/C++/run_2.tsv new file mode 100644 index 0000000..e69de29 diff --git a/timelines/C++/run_3.tsv b/timelines/C++/run_3.tsv new file mode 100644 index 0000000..e69de29 diff --git a/timelines/C++/run_4.tsv b/timelines/C++/run_4.tsv new file mode 100644 index 0000000..e69de29 diff --git a/timelines/C/run_1.tsv b/timelines/C/run_1.tsv new file mode 100644 index 0000000..dc38ba0 --- /dev/null +++ b/timelines/C/run_1.tsv @@ -0,0 +1 @@ +7 0 0 diff --git a/timelines/C/run_2.tsv b/timelines/C/run_2.tsv new file mode 100644 index 0000000..e69de29 diff --git a/timelines/C/run_3.tsv b/timelines/C/run_3.tsv new file mode 100644 index 0000000..e69de29 diff --git a/timelines/C/run_4.tsv b/timelines/C/run_4.tsv new file mode 100644 index 0000000..e69de29 diff --git a/timelines/CSharp/run_1.tsv b/timelines/CSharp/run_1.tsv new file mode 100644 index 0000000..64e3c39 --- /dev/null +++ b/timelines/CSharp/run_1.tsv @@ -0,0 +1,5 @@ +13 2032 0 +40 2032 0 +64 2032 0 +90 2032 0 +116 2032 0 diff --git a/timelines/CSharp/run_2.tsv b/timelines/CSharp/run_2.tsv new file mode 100644 index 0000000..df566b3 --- /dev/null +++ b/timelines/CSharp/run_2.tsv @@ -0,0 +1,2 @@ +9 2016 0 +33 2016 0 diff --git a/timelines/CSharp/run_3.tsv b/timelines/CSharp/run_3.tsv new file mode 100644 index 0000000..f2467a9 --- /dev/null +++ b/timelines/CSharp/run_3.tsv @@ -0,0 +1,2 @@ +8 2032 0 +33 2032 0 diff --git a/timelines/CSharp/run_4.tsv b/timelines/CSharp/run_4.tsv new file mode 100644 index 0000000..a219299 --- /dev/null +++ b/timelines/CSharp/run_4.tsv @@ -0,0 +1,2 @@ +8 2000 0 +33 2016 0 diff --git a/timelines/Crystal/run_1.tsv b/timelines/Crystal/run_1.tsv new file mode 100644 index 0000000..c91bbbd --- /dev/null +++ b/timelines/Crystal/run_1.tsv @@ -0,0 +1 @@ +12 2288 diff --git a/timelines/Crystal/run_2.tsv b/timelines/Crystal/run_2.tsv new file mode 100644 index 0000000..f1b74c3 --- /dev/null +++ b/timelines/Crystal/run_2.tsv @@ -0,0 +1 @@ +6 0 0 diff --git a/timelines/Crystal/run_3.tsv b/timelines/Crystal/run_3.tsv new file mode 100644 index 0000000..f1b74c3 --- /dev/null +++ b/timelines/Crystal/run_3.tsv @@ -0,0 +1 @@ +6 0 0 diff --git a/timelines/Crystal/run_4.tsv b/timelines/Crystal/run_4.tsv new file mode 100644 index 0000000..2e535b7 --- /dev/null +++ b/timelines/Crystal/run_4.tsv @@ -0,0 +1 @@ +5 0 0 diff --git a/timelines/D/run_1.tsv b/timelines/D/run_1.tsv new file mode 100644 index 0000000..ed4b6fe --- /dev/null +++ b/timelines/D/run_1.tsv @@ -0,0 +1 @@ +10 464 0 diff --git a/timelines/D/run_2.tsv b/timelines/D/run_2.tsv new file mode 100644 index 0000000..eb875bb --- /dev/null +++ b/timelines/D/run_2.tsv @@ -0,0 +1 @@ +8 496 diff --git a/timelines/D/run_3.tsv b/timelines/D/run_3.tsv new file mode 100644 index 0000000..eb875bb --- /dev/null +++ b/timelines/D/run_3.tsv @@ -0,0 +1 @@ +8 496 diff --git a/timelines/D/run_4.tsv b/timelines/D/run_4.tsv new file mode 100644 index 0000000..eb875bb --- /dev/null +++ b/timelines/D/run_4.tsv @@ -0,0 +1 @@ +8 496 diff --git a/timelines/Dart/run_1.tsv b/timelines/Dart/run_1.tsv new file mode 100644 index 0000000..a4286af --- /dev/null +++ b/timelines/Dart/run_1.tsv @@ -0,0 +1,2 @@ +12 5344 0 +50 0 0 diff --git a/timelines/Dart/run_2.tsv b/timelines/Dart/run_2.tsv new file mode 100644 index 0000000..445f605 --- /dev/null +++ b/timelines/Dart/run_2.tsv @@ -0,0 +1 @@ +12 9088 0 diff --git a/timelines/Dart/run_3.tsv b/timelines/Dart/run_3.tsv new file mode 100644 index 0000000..98d9120 --- /dev/null +++ b/timelines/Dart/run_3.tsv @@ -0,0 +1 @@ +13 10624 0 diff --git a/timelines/Dart/run_4.tsv b/timelines/Dart/run_4.tsv new file mode 100644 index 0000000..72016b4 --- /dev/null +++ b/timelines/Dart/run_4.tsv @@ -0,0 +1 @@ +12 10016 0 diff --git a/timelines/Elixir/run_1.tsv b/timelines/Elixir/run_1.tsv new file mode 100644 index 0000000..b4a5aac --- /dev/null +++ b/timelines/Elixir/run_1.tsv @@ -0,0 +1,16 @@ +15 2032 0 +42 2032 0 +65 2032 0 +94 2032 0 +119 2032 0 +146 2032 0 +169 2032 0 +193 2032 0 +221 2032 0 +246 2032 0 +270 2032 0 +296 2032 0 +319 2032 0 +343 2032 0 +367 2032 0 +393 2032 0 diff --git a/timelines/Elixir/run_2.tsv b/timelines/Elixir/run_2.tsv new file mode 100644 index 0000000..b7b74f5 --- /dev/null +++ b/timelines/Elixir/run_2.tsv @@ -0,0 +1,15 @@ +9 2032 0 +34 2032 0 +62 2032 0 +85 2032 0 +109 2032 0 +135 2032 0 +158 2032 0 +181 2032 0 +213 2032 0 +238 2032 0 +262 2032 0 +288 2032 0 +311 2032 0 +336 2032 0 +359 2032 0 diff --git a/timelines/Elixir/run_3.tsv b/timelines/Elixir/run_3.tsv new file mode 100644 index 0000000..05e1142 --- /dev/null +++ b/timelines/Elixir/run_3.tsv @@ -0,0 +1,13 @@ +9 2032 0 +31 2032 0 +57 2032 0 +79 2032 0 +102 2032 0 +126 2032 0 +155 2032 0 +179 2032 0 +202 2032 0 +227 2032 0 +249 2032 0 +272 2032 0 +296 2032 0 diff --git a/timelines/Elixir/run_4.tsv b/timelines/Elixir/run_4.tsv new file mode 100644 index 0000000..a5f74f4 --- /dev/null +++ b/timelines/Elixir/run_4.tsv @@ -0,0 +1,12 @@ +8 2048 0 +31 2048 0 +57 2048 0 +82 2048 0 +106 2048 0 +130 2048 0 +155 2048 0 +179 2048 0 +206 2048 0 +230 2048 0 +256 2048 0 +279 2048 0 diff --git a/timelines/Erlang/run_1.tsv b/timelines/Erlang/run_1.tsv new file mode 100644 index 0000000..7551e0d --- /dev/null +++ b/timelines/Erlang/run_1.tsv @@ -0,0 +1,6 @@ +12 2048 0 +36 2048 0 +60 2048 0 +84 2048 0 +108 2048 0 +132 0 0 diff --git a/timelines/Erlang/run_2.tsv b/timelines/Erlang/run_2.tsv new file mode 100644 index 0000000..f840e90 --- /dev/null +++ b/timelines/Erlang/run_2.tsv @@ -0,0 +1,10 @@ +8 2032 0 +33 2032 0 +56 2032 0 +79 2032 0 +102 2032 0 +128 2032 0 +151 2032 0 +177 2032 0 +206 2032 0 +227 2032 0 diff --git a/visualize_timelines.py b/visualize_timelines.py new file mode 100755 index 0000000..0704089 --- /dev/null +++ b/visualize_timelines.py @@ -0,0 +1,155 @@ +#!/usr/bin/env python3 +""" +Visualize resource usage timelines from benchmark data. +Generates SVG charts showing memory and CPU usage over time for each language. +""" + +import os +import sys +import matplotlib.pyplot as plt +import matplotlib +matplotlib.use('Agg') # Non-interactive backend +from pathlib import Path +import numpy as np + +def read_timeline_data(filepath): + """Read timeline TSV file and return time, memory, cpu arrays.""" + times = [] + memories = [] + cpus = [] + + with open(filepath, 'r') as f: + for line in f: + parts = line.strip().split() + if len(parts) >= 3: + try: + times.append(int(parts[0])) + memories.append(int(parts[1]) / 1024) # Convert KB to MB + cpus.append(int(parts[2])) + except ValueError: + continue + + return np.array(times), np.array(memories), np.array(cpus) + +def create_timeline_chart(language, times, memories, cpus, output_dir): + """Create a dual-axis chart showing memory and CPU over time.""" + if len(times) == 0: + print(f"No data for {language}") + return + + fig, ax1 = plt.subplots(figsize=(10, 6)) + + # Memory on left axis + color1 = '#2E86AB' # Blue + ax1.set_xlabel('Tid (ms)', fontsize=12) + ax1.set_ylabel('Minne (MB)', color=color1, fontsize=12) + ax1.plot(times, memories, color=color1, linewidth=2, label='Minne') + ax1.tick_params(axis='y', labelcolor=color1) + ax1.grid(True, alpha=0.3) + + # CPU on right axis + ax2 = ax1.twinx() + color2 = '#E94F37' # Red + ax2.set_ylabel('CPU (%)', color=color2, fontsize=12) + ax2.plot(times, cpus, color=color2, linewidth=2, linestyle='--', label='CPU') + ax2.tick_params(axis='y', labelcolor=color2) + + # Title and legend + plt.title(f'{language} - Resursanvändning över tid', fontsize=14, fontweight='bold') + + # Combine legends + lines1, labels1 = ax1.get_legend_handles_labels() + lines2, labels2 = ax2.get_legend_handles_labels() + ax1.legend(lines1 + lines2, labels1 + labels2, loc='upper right') + + plt.tight_layout() + + # Save + output_file = output_dir / f'{language}_timeline.svg' + plt.savefig(output_file, format='svg', dpi=300) + plt.close() + + print(f"Created {output_file}") + +def create_comparison_chart(languages_data, output_dir, metric='memory'): + """Create a comparison chart for multiple languages.""" + fig, ax = plt.subplots(figsize=(14, 8)) + + colors = plt.cm.tab20(np.linspace(0, 1, len(languages_data))) + + for idx, (language, times, values) in enumerate(languages_data): + if len(times) > 0: + ax.plot(times, values, label=language, linewidth=2, color=colors[idx]) + + if metric == 'memory': + ax.set_ylabel('Minne (MB)', fontsize=12) + ax.set_title('Minnesanvändning över tid - Jämförelse', fontsize=14, fontweight='bold') + else: + ax.set_ylabel('CPU (%)', fontsize=12) + ax.set_title('CPU-användning över tid - Jämförelse', fontsize=14, fontweight='bold') + + ax.set_xlabel('Tid (ms)', fontsize=12) + ax.grid(True, alpha=0.3) + ax.legend(loc='upper right', fontsize=10) + + plt.tight_layout() + + output_file = output_dir / f'comparison_{metric}.svg' + plt.savefig(output_file, format='svg', dpi=300) + plt.close() + + print(f"Created {output_file}") + +def main(): + if len(sys.argv) < 2: + print("Usage: python visualize_timelines.py [output_dir]") + print("Example: python visualize_timelines.py timelines charts") + sys.exit(1) + + timelines_dir = Path(sys.argv[1]) + output_dir = Path(sys.argv[2]) if len(sys.argv) > 2 else Path('charts') + + if not timelines_dir.exists(): + print(f"Error: Directory {timelines_dir} does not exist") + sys.exit(1) + + output_dir.mkdir(exist_ok=True) + + # Collect data for all languages + all_languages = [] + + # Process each language + for lang_dir in sorted(timelines_dir.iterdir()): + if lang_dir.is_dir(): + language = lang_dir.name + + # Find the best run (usually run_2.tsv) + timeline_file = lang_dir / 'run_2.tsv' + + if timeline_file.exists(): + times, memories, cpus = read_timeline_data(timeline_file) + + if len(times) > 0: + # Create individual chart + create_timeline_chart(language, times, memories, cpus, output_dir) + + # Store for comparison + all_languages.append((language, times, memories)) + + # Create comparison charts (top 10 fastest languages) + if len(all_languages) > 0: + # Sort by execution time and take top 10 + all_languages.sort(key=lambda x: x[1][-1] if len(x[1]) > 0 else float('inf')) + top_10 = all_languages[:10] + + create_comparison_chart(top_10, output_dir, 'memory') + + # Create comparison for slowest languages + if len(all_languages) > 10: + slowest = all_languages[-5:] + create_comparison_chart(slowest, output_dir, 'memory') + + print(f"\nAll charts saved to {output_dir}/") + +if __name__ == '__main__': + main() \ No newline at end of file