| 1 | module main |
| 2 | |
| 3 | import time |
| 4 | import sync |
| 5 | import os |
| 6 | import runtime |
| 7 | import v.util.version |
| 8 | |
| 9 | // Define closure type alias |
| 10 | type ClosureFN = fn () int |
| 11 | |
| 12 | // Test closures with different capture sizes |
| 13 | fn create_closure_small() ClosureFN { |
| 14 | a := 0 |
| 15 | return fn [a] () int { |
| 16 | return a |
| 17 | } |
| 18 | } |
| 19 | |
| 20 | fn create_closure_medium() ClosureFN { |
| 21 | a, b, c, d := 1, 2, 3, 4 |
| 22 | return fn [a, b, c, d] () int { |
| 23 | return a + b - c * d |
| 24 | } |
| 25 | } |
| 26 | |
| 27 | struct LargeData { |
| 28 | array [10]int |
| 29 | } |
| 30 | |
| 31 | fn create_closure_large() ClosureFN { |
| 32 | data := LargeData{ |
| 33 | array: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]! |
| 34 | } |
| 35 | return fn [data] () int { |
| 36 | mut sum := 0 |
| 37 | for i in 0 .. 10 { |
| 38 | sum += data.array[i] |
| 39 | } |
| 40 | return sum |
| 41 | } |
| 42 | } |
| 43 | |
| 44 | // Result structs |
| 45 | struct TestResult { |
| 46 | test_name string |
| 47 | iterations int |
| 48 | time_ms i64 |
| 49 | ops_per_sec f64 // Operations per second |
| 50 | notes string |
| 51 | } |
| 52 | |
| 53 | struct MemoryResult { |
| 54 | test_name string |
| 55 | count int |
| 56 | start_mem_kb int |
| 57 | end_mem_kb int |
| 58 | delta_kb int |
| 59 | bytes_per_closure int |
| 60 | check_sum int |
| 61 | } |
| 62 | |
| 63 | // Benchmark group - returns result structs |
| 64 | fn benchmark_closure_creation(iterations int) []TestResult { |
| 65 | mut results := []TestResult{} |
| 66 | |
| 67 | // Test small closure creation |
| 68 | mut start := time.ticks() |
| 69 | for _ in 0 .. iterations { |
| 70 | _ = create_closure_small() |
| 71 | } |
| 72 | small_time := time.ticks() - start |
| 73 | mut ops_per_sec := f64(iterations) * 1000.0 / f64(small_time) |
| 74 | results << TestResult{'Small Closure Creation', iterations, small_time, ops_per_sec, ''} |
| 75 | |
| 76 | // Test medium closure creation |
| 77 | start = time.ticks() |
| 78 | for _ in 0 .. iterations { |
| 79 | _ = create_closure_medium() |
| 80 | } |
| 81 | medium_time := time.ticks() - start |
| 82 | ops_per_sec = f64(iterations) * 1000.0 / f64(medium_time) |
| 83 | results << TestResult{'Medium Closure Creation', iterations, medium_time, ops_per_sec, ''} |
| 84 | |
| 85 | // Test large closure creation |
| 86 | large_iter := iterations / 10 |
| 87 | start = time.ticks() |
| 88 | for _ in 0 .. large_iter { |
| 89 | _ = create_closure_large() |
| 90 | } |
| 91 | large_time := time.ticks() - start |
| 92 | ops_per_sec = f64(large_iter) * 1000.0 / f64(large_time) |
| 93 | results << TestResult{'Large Closure Creation', large_iter, large_time, ops_per_sec, ''} //, "Equivalent iterations: ${iterations/10}"} |
| 94 | return results |
| 95 | } |
| 96 | |
| 97 | fn benchmark_closure_call(iterations int) []TestResult { |
| 98 | mut results := []TestResult{} |
| 99 | |
| 100 | closure_small := create_closure_small() |
| 101 | closure_medium := create_closure_medium() |
| 102 | closure_large := create_closure_large() |
| 103 | |
| 104 | // Test small closure call |
| 105 | mut start := time.ticks() |
| 106 | for _ in 0 .. iterations { |
| 107 | _ = closure_small() |
| 108 | } |
| 109 | small_time := time.ticks() - start |
| 110 | mut ops_per_sec := f64(iterations) * 1000.0 / f64(small_time) |
| 111 | results << TestResult{'Small Closure Call', iterations, small_time, ops_per_sec, ''} |
| 112 | |
| 113 | // Test medium closure call |
| 114 | start = time.ticks() |
| 115 | for _ in 0 .. iterations { |
| 116 | _ = closure_medium() |
| 117 | } |
| 118 | medium_time := time.ticks() - start |
| 119 | ops_per_sec = f64(iterations) * 1000.0 / f64(medium_time) |
| 120 | results << TestResult{'Medium Closure Call', iterations, medium_time, ops_per_sec, ''} |
| 121 | |
| 122 | // Test large closure call |
| 123 | large_iter := iterations / 10 |
| 124 | start = time.ticks() |
| 125 | for _ in 0 .. large_iter { |
| 126 | _ = closure_large() |
| 127 | } |
| 128 | large_time := time.ticks() - start |
| 129 | ops_per_sec = f64(large_iter) * 1000.0 / f64(large_time) |
| 130 | results << TestResult{'Large Closure Call', large_iter, large_time, ops_per_sec, ''} |
| 131 | |
| 132 | return results |
| 133 | } |
| 134 | |
| 135 | fn benchmark_threaded_creation(threads int, iterations_per_thread int) TestResult { |
| 136 | total_iterations := threads * iterations_per_thread |
| 137 | |
| 138 | mut wg := sync.new_waitgroup() |
| 139 | wg.add(threads) |
| 140 | |
| 141 | start := time.ticks() |
| 142 | |
| 143 | for _ in 0 .. threads { |
| 144 | go fn [mut wg, iterations_per_thread] () { |
| 145 | defer { wg.done() } |
| 146 | for _ in 0 .. iterations_per_thread { |
| 147 | _ = create_closure_medium() |
| 148 | } |
| 149 | }() |
| 150 | } |
| 151 | |
| 152 | wg.wait() |
| 153 | elapsed := time.ticks() - start |
| 154 | ops_per_sec := f64(total_iterations) * 1000.0 / f64(elapsed) |
| 155 | |
| 156 | return TestResult{ |
| 157 | test_name: 'Multi-threaded Creation' |
| 158 | iterations: total_iterations |
| 159 | time_ms: elapsed |
| 160 | ops_per_sec: ops_per_sec |
| 161 | notes: 'Threads: ${threads} Iterations per thread: ${iterations_per_thread}' |
| 162 | } |
| 163 | } |
| 164 | |
| 165 | fn baseline_call_performance(iterations int) TestResult { |
| 166 | start := time.ticks() |
| 167 | for _ in 0 .. iterations { |
| 168 | _ = normal_function() |
| 169 | } |
| 170 | elapsed := time.ticks() - start |
| 171 | ops_per_sec := f64(iterations) * 1000.0 / f64(elapsed) |
| 172 | |
| 173 | return TestResult{ |
| 174 | test_name: 'Normal Function Call' |
| 175 | iterations: iterations |
| 176 | time_ms: elapsed |
| 177 | ops_per_sec: ops_per_sec |
| 178 | notes: 'Baseline' |
| 179 | } |
| 180 | } |
| 181 | |
| 182 | fn benchmark_memory_usage(count int) MemoryResult { |
| 183 | mut closures := []ClosureFN{} |
| 184 | start_mem := runtime.used_memory() or { panic(err) } |
| 185 | |
| 186 | for i in 0 .. count { |
| 187 | the_closure := create_closure_medium() |
| 188 | closures << the_closure |
| 189 | |
| 190 | if i % 1000 == 0 { |
| 191 | _ = the_closure() |
| 192 | } |
| 193 | } |
| 194 | |
| 195 | end_mem := runtime.used_memory() or { panic(err) } |
| 196 | delta := int(end_mem) - int(start_mem) |
| 197 | bytes_per_closure := delta / count |
| 198 | |
| 199 | // Calculate verification sum |
| 200 | mut check_sum := 0 |
| 201 | n := if closures.len < 100 { closures.len } else { 100 } |
| 202 | for idx in 0 .. n { |
| 203 | check_sum += closures[idx]() |
| 204 | } |
| 205 | |
| 206 | return MemoryResult{ |
| 207 | test_name: 'Closure Memory Overhead' |
| 208 | count: count |
| 209 | start_mem_kb: int(start_mem / 1024) |
| 210 | end_mem_kb: int(end_mem / 1024) |
| 211 | delta_kb: delta / 1024 |
| 212 | bytes_per_closure: bytes_per_closure |
| 213 | check_sum: check_sum |
| 214 | } |
| 215 | } |
| 216 | |
| 217 | fn normal_function() int { |
| 218 | return 42 |
| 219 | } |
| 220 | |
| 221 | // Format performance data for readability |
| 222 | fn format_perf(ops_per_sec f64) string { |
| 223 | if ops_per_sec >= 1_000_000 { |
| 224 | return '${ops_per_sec / 1_000_000:5.2f} Mop/s' |
| 225 | } else if ops_per_sec >= 1_000 { |
| 226 | return '${ops_per_sec / 1_000:5.2f} Kop/s' |
| 227 | } else { |
| 228 | return '${ops_per_sec:5.2f} op/s' |
| 229 | } |
| 230 | } |
| 231 | |
| 232 | fn print_results_table(results []TestResult, title string) { |
| 233 | println('|---------------------------|------------|----------|--------------|--------------|') |
| 234 | for res in results { |
| 235 | perf_str := format_perf(res.ops_per_sec) |
| 236 | println('| ${res.test_name:-25} | ${res.iterations:10} | ${res.time_ms:8} | ${perf_str:-12} | ${res.notes:-12} |') |
| 237 | } |
| 238 | } |
| 239 | |
| 240 | fn main() { |
| 241 | println('# V Language Closure Performance Benchmark Report') |
| 242 | |
| 243 | // Configurable test parameters |
| 244 | base_iter := 100_000_000 // 100 million iterations |
| 245 | creation_iter := 10_000_000 // 1 million iterations |
| 246 | mem_count := 100_000 |
| 247 | threads := 8 |
| 248 | thread_iter := 125_000 |
| 249 | |
| 250 | // Execute tests |
| 251 | baseline_result := baseline_call_performance(base_iter) |
| 252 | creation_results := benchmark_closure_creation(creation_iter) |
| 253 | call_results := benchmark_closure_call(base_iter) |
| 254 | thread_result := benchmark_threaded_creation(threads, thread_iter) |
| 255 | mem_result := benchmark_memory_usage(mem_count) |
| 256 | |
| 257 | // Print result tables |
| 258 | println('\n## 1. Closure Performance Analysis') |
| 259 | println('| Test Name | Iterations | Time(ms) | Ops/sec | Notes |') |
| 260 | print_results_table([baseline_result], '1. Performance Baseline') |
| 261 | print_results_table(creation_results, '2. Closure Creation Performance') |
| 262 | print_results_table(call_results, '3. Closure Call Performance') |
| 263 | print_results_table([thread_result], '4. Multi-threaded Performance') |
| 264 | |
| 265 | // Print memory results |
| 266 | println('\n## 2. Memory Overhead Analysis') |
| 267 | println('| Test Name | Closure Count | Start Mem(KB) | End Mem(KB) | Delta(KB) | Bytes/Closure |') |
| 268 | println('|-------------------------|---------------|---------------|------------|-----------|---------------|') |
| 269 | println('| ${mem_result.test_name:-20} | ${mem_result.count:13} | ${mem_result.start_mem_kb:13} | ${mem_result.end_mem_kb:10} | ${mem_result.delta_kb:9} | ${mem_result.bytes_per_closure:13} |') |
| 270 | println('\n**Verification Sum: ${mem_result.check_sum}** (Calculated from random sample of 100 closures)') |
| 271 | |
| 272 | println('\n## Test Environment') |
| 273 | println('- V Language Version: ${version.full_v_version(false)}') |
| 274 | println('- CPU Cores: ${runtime.nr_cpus()}') |
| 275 | println('- System Memory: ${runtime.total_memory()! / 1024 / 1024} MB') |
| 276 | println('- Operating System: ${os.user_os()}') |
| 277 | println('\n> Test Time: ${time.now().format_ss_micro()}') |
| 278 | } |
| 279 | |