odin-jsonschema

Implementation of JSON schema for Odin
Log | Files | Refs | LICENSE

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