main.odin (3032B)
1 // bench measures jschema generation performance: wall time per iteration, 2 // throughput, and allocation behaviour for a given schema file. 3 // 4 // Usage: bench [schema.json] [iterations] 5 package main 6 7 import "core:fmt" 8 import "core:mem" 9 import "core:os" 10 import "core:path/filepath" 11 import "core:strconv" 12 import "core:time" 13 14 import "../../pkg/jschema" 15 16 DEFAULT_ITERATIONS :: 200 17 18 main :: proc() { 19 os.exit(run()) 20 } 21 22 run :: proc() -> int { 23 schema_path := "testdata/cases/openapi/schema.json" 24 iterations := DEFAULT_ITERATIONS 25 args := os.args[1:] 26 if len(args) > 0 { 27 schema_path = args[0] 28 } 29 if len(args) > 1 { 30 parsed, ok := strconv.parse_int(args[1]) 31 if !ok || parsed <= 0 { 32 fmt.eprintfln("bench: invalid iteration count %q", args[1]) 33 return 2 34 } 35 iterations = parsed 36 } 37 38 data, rerr := os.read_entire_file_from_path(schema_path, context.allocator) 39 if rerr != nil { 40 fmt.eprintfln("bench: cannot read %s: %v", schema_path, rerr) 41 return 1 42 } 43 defer delete(data) 44 base_dir := filepath.dir(schema_path) 45 46 // Warmup, and fail fast on a broken schema. 47 for _ in 0 ..< 3 { 48 source, err := jschema.generate_source(data, base_dir = base_dir) 49 if err != nil { 50 fmt.eprintfln("bench: generate failed: %v", err) 51 return 1 52 } 53 delete(source) 54 } 55 56 total: time.Duration 57 fastest := time.Duration(max(i64)) 58 slowest := time.Duration(0) 59 output_size := 0 60 for _ in 0 ..< iterations { 61 start := time.tick_now() 62 source, _ := jschema.generate_source(data, base_dir = base_dir) 63 elapsed := time.tick_since(start) 64 output_size = len(source) 65 delete(source) 66 67 total += elapsed 68 fastest = min(fastest, elapsed) 69 slowest = max(slowest, elapsed) 70 } 71 average := total / time.Duration(iterations) 72 73 // One tracked run for the allocation report. The tracking allocator only 74 // sees caller-facing allocations plus the arena's block requests, which is 75 // exactly the library's real footprint on the caller's allocator. 76 track: mem.Tracking_Allocator 77 mem.tracking_allocator_init(&track, context.allocator) 78 { 79 context.allocator = mem.tracking_allocator(&track) 80 source, _ := jschema.generate_source(data, base_dir = base_dir) 81 delete(source) 82 } 83 leaks := len(track.allocation_map) 84 total_allocated := track.total_memory_allocated 85 peak := track.peak_memory_allocated 86 allocation_count := track.total_allocation_count 87 bad_frees := len(track.bad_free_array) 88 mem.tracking_allocator_destroy(&track) 89 90 seconds := time.duration_seconds(average) 91 throughput := f64(len(data)) / seconds / mem.Megabyte 92 93 fmt.printfln( 94 "schema: %s (%d bytes in, %d bytes out)", 95 schema_path, 96 len(data), 97 output_size, 98 ) 99 fmt.printfln("iterations: %d", iterations) 100 fmt.printfln("time/op: avg %v min %v max %v", average, fastest, slowest) 101 fmt.printfln("throughput: %.1f MiB/s", throughput) 102 fmt.printfln( 103 "allocations: %d calls, %m total, %m peak", 104 allocation_count, 105 total_allocated, 106 peak, 107 ) 108 fmt.printfln("leaks: %d, bad frees: %d", leaks, bad_frees) 109 if leaks > 0 || bad_frees > 0 { 110 return 1 111 } 112 return 0 113 } 114