#!/bin/bash # Run all pi calculation programs and measure performance and memory # Run each program 3 times and take the average set -e SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" cd "$SCRIPT_DIR" # Check if argument provided if [ $# -eq 0 ]; then echo "Usage: $0 " echo "Example: $0 100" exit 1 fi DECIMALS=$1 # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color # Function to verify result against facit verify() { local result="$1" local decimals="$2" local expected=$(head -c $((decimals + 2)) facit.txt) if [ "$result" = "$expected" ]; then return 0 else return 1 fi } # Function to get memory usage using /usr/bin/time (more reliable for fast programs) get_memory_with_time() { local output_file=$1 # Use /usr/bin/time -l (macOS) or -v (Linux) to get maximum resident set size # This works even for very fast programs if command -v /usr/bin/time >/dev/null 2>&1; then # Try macOS format first (time -l) if grep -q "maximum resident set size" "$output_file" 2>/dev/null; then # macOS format: "1622016 maximum resident set size" (already in bytes) grep "maximum resident set size" "$output_file" 2>/dev/null | \ awk '{print $1}' || echo "0" # Try Linux format (time -v) elif grep -q "Maximum resident set size" "$output_file" 2>/dev/null; then # Linux format: "Maximum resident set size (kbytes): 1622" (in KB) grep "Maximum resident set size" "$output_file" 2>/dev/null | \ awk '{print $NF}' | \ awk '{print $1 * 1024}' || echo "0" else echo "0" fi else echo "0" fi } # Function to profile memory and CPU during execution (fallback for slower programs) profile_resources() { local pid=$1 local mem_output_file=$2 local cpu_output_file=$3 local timeline_file=$4 local peak_mem=0 local peak_cpu=0 local current_mem local current_cpu local start_time=$(date +%s%N) local sample_count=0 # Clear output files > "$mem_output_file" > "$cpu_output_file" > "$timeline_file" # Sample resources every 1ms while process is running while kill -0 "$pid" 2>/dev/null; do # Use ps to get RSS (resident set size) in KB, then convert to bytes local kb=$(ps -o rss= -p "$pid" 2>/dev/null || echo "0") # Ensure kb is a valid number if ! [[ "$kb" =~ ^[0-9]+$ ]]; then kb=0 fi current_mem=$((kb * 1024)) # Use ps to get CPU percentage local cpu_raw=$(ps -o %cpu= -p "$pid" 2>/dev/null | awk '{print int($1)}' || echo "0") # Ensure cpu is a valid number if ! [[ "$cpu_raw" =~ ^[0-9]+$ ]]; then cpu_raw=0 fi current_cpu=$cpu_raw current_time=$(date +%s%N) elapsed_ms=$(( (current_time - start_time) / 1000000 )) if [ "$current_mem" -gt "$peak_mem" ]; then peak_mem=$current_mem fi if [ "$current_cpu" -gt "$peak_cpu" ]; then peak_cpu=$current_cpu fi # Store time-series data echo "$elapsed_ms $current_mem" >> "$mem_output_file" echo "$elapsed_ms $current_cpu" >> "$cpu_output_file" echo "$elapsed_ms $current_mem $current_cpu" >> "$timeline_file" sample_count=$((sample_count + 1)) sleep 0.001 2>/dev/null || sleep 0.01 done echo "$peak_mem $peak_cpu $sample_count" } echo "=== Pi-beräkning med $DECIMALS decimaler (4 körningar, genomsnitt av 3 efter warmup) ===" echo "" # Array to store results declare -a results # Run each program 4 times, discard first run (warmup), take average of last 3 run_program() { local name="$1" shift printf "%-12s " "$name" local total_time=0 local total_memory=0 local total_cpu=0 local success_count=0 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 timeline_file="$timeline_dir/run_$i.tsv" local time_output_file="/tmp/time_output_$$_$i.txt" local start=$(date +%s%N) # Run program with /usr/bin/time -l (macOS) or -v (Linux) for accurate memory measurement # This works for all programs, including very fast ones if command -v /usr/bin/time >/dev/null 2>&1; then # Detect if we're on macOS or Linux if [[ "$OSTYPE" == "darwin"* ]]; then # macOS uses -l flag /usr/bin/time -l "$@" > /dev/null 2> "$time_output_file" & local pid=$! else # Linux uses -v flag /usr/bin/time -v "$@" > /dev/null 2> "$time_output_file" & local pid=$! fi else # Fallback to regular execution if /usr/bin/time not available "$@" 2>/dev/null & local pid=$! fi # Profile resources in background (for CPU and timeline) local resources=$(profile_resources "$pid" "/dev/null" "/dev/null" "$timeline_file") local peak_mem_ps=$(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 )) # Get memory from /usr/bin/time -v output (more reliable for fast programs) local peak_mem_time=$(get_memory_with_time "$time_output_file") # Use the larger of the two memory measurements (time -v is more reliable) local peak_mem=$peak_mem_time if [ "$peak_mem_ps" -gt "$peak_mem" ]; then peak_mem=$peak_mem_ps fi # Clean up temp file rm -f "$time_output_file" if [ $exit_code -eq 0 ]; then # Get result for verification result=$("$@" 2>/dev/null) 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)) if [ "$peak_mem" -gt "$peak_memory" ]; then peak_memory=$peak_mem fi if [ "$peak_cpu_val" -gt "$peak_cpu" ]; then peak_cpu=$peak_cpu_val fi if verify "$result" "$DECIMALS"; then ((success_count++)) fi fi else echo -e "${RED}ERROR${NC}" results+=("999999 $name ERROR") return fi done # Calculate averages local avg_time=$((total_time / 3)) local avg_memory=$((total_memory / 3)) local avg_cpu=$((total_cpu / 3)) if [ $success_count -eq 3 ]; then echo -e "${GREEN}SUCCESS${NC} $avg_time ms, ${BLUE}${avg_memory} bytes avg / ${YELLOW}${peak_memory} bytes 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} bytes avg / ${YELLOW}${peak_memory} bytes 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 } # Run all programs (no timeouts) run_program Bash bash bash/bin/print_hej "$DECIMALS" run_program Brainfuck brainfuck/bin/print_hej "$DECIMALS" run_program C c/bin/print_hej "$DECIMALS" run_program C++ cpp/bin/print_hej "$DECIMALS" run_program Crystal crystal/bin/print_hej "$DECIMALS" run_program CSharp csharp/bin/print_hej "$DECIMALS" run_program D d/bin/print_hej "$DECIMALS" run_program Dart dart/bin/print_hej "$DECIMALS" run_program Elixir elixir/bin/print_hej "$DECIMALS" run_program Erlang erlang/bin/print_hej "$DECIMALS" run_program Fortran fortran/bin/print_hej "$DECIMALS" run_program Go go/bin/print_hej "$DECIMALS" run_program Haskell haskell/bin/print_hej "$DECIMALS" run_program Java java/bin/print_hej "$DECIMALS" run_program JavaScript javascript/bin/print_hej "$DECIMALS" run_program Julia julia/bin/print_hej "$DECIMALS" run_program Kotlin kotlin/bin/print_hej "$DECIMALS" run_program Objective-C objective-c/bin/print_hej "$DECIMALS" run_program Scala scala/bin/print_hej "$DECIMALS" run_program TypeScript typescript/bin/print_hej "$DECIMALS" run_program Lua lua/bin/print_hej "$DECIMALS" run_program Nim nim/bin/print_hej "$DECIMALS" run_program Odin odin/bin/print_hej "$DECIMALS" run_program Perl perl/bin/print_hej "$DECIMALS" run_program PHP php/bin/print_hej "$DECIMALS" run_program Python python/bin/print_hej "$DECIMALS" run_program R r/bin/print_hej "$DECIMALS" run_program Ruby ruby/bin/print_hej "$DECIMALS" run_program Rust rust/bin/print_hej "$DECIMALS" run_program Swift swift/bin/print_hej "$DECIMALS" run_program Zig zig/bin/print_hej "$DECIMALS" run_program Assembly assembly/bin/print_hej "$DECIMALS" run_program Wolfram wolfram/bin/print_hej "$DECIMALS" run_program Vimscript vimscript/bin/print_hej "$DECIMALS" echo "" echo "=== Sammanfattning (sorterad efter prestanda) ===" echo "" # Sort and print results IFS=$'\n' sorted=($(printf '%s\n' "${results[@]}" | sort -n)) unset IFS echo "Språk Status Tid (ms) Minne (bytes) CPU (%)" echo "================================================================" for entry in "${sorted[@]}"; do time_ms=$(echo "$entry" | awk '{print $1}') name=$(echo "$entry" | awk '{print $2}') status=$(echo "$entry" | awk '{print $3}') avg_mem=$(echo "$entry" | awk '{print $4}') peak_mem=$(echo "$entry" | awk '{print $5}') avg_cpu=$(echo "$entry" | awk '{print $6}') peak_cpu=$(echo "$entry" | awk '{print $7}') printf "%-12s " "$name" if [ "$status" = "SUCCESS" ]; then echo -e "${GREEN}$status${NC} $time_ms ms ${BLUE}${avg_mem} bytes avg / ${YELLOW}${peak_mem} bytes peak ${avg_cpu}% avg / ${peak_cpu}% peak${NC}" else echo -e "${RED}$status${NC} $time_ms ms ${BLUE}${avg_mem} bytes avg / ${YELLOW}${peak_mem} bytes peak ${avg_cpu}% avg / ${peak_cpu}% peak${NC}" fi done echo ""