From 2fbff25a18672597a6c6b4a73733f649ccdc8648 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Wed, 28 Jul 2021 15:23:48 +0200 Subject: [PATCH] big: Improve `int_bitfield_extract`. --- core/math/big/example.odin | 48 ++++++++++++++++++++++++++------ core/math/big/helpers.odin | 57 +++++++++++++++++++++++++------------- core/math/big/radix.odin | 2 +- 3 files changed, 78 insertions(+), 29 deletions(-) diff --git a/core/math/big/example.odin b/core/math/big/example.odin index c95fde549..60e2a4447 100644 --- a/core/math/big/example.odin +++ b/core/math/big/example.odin @@ -12,6 +12,8 @@ package big import "core:fmt" import "core:mem" +import "core:time" +import rnd "core:math/rand" print_configation :: proc() { fmt.printf( @@ -40,32 +42,51 @@ _SQR_TOOM_CUTOFF, ); } +Category :: enum { + itoa, + atoi, +}; +Event :: struct { + t: time.Duration, + c: int, +} +Timings := [Category]Event{}; + print :: proc(name: string, a: ^Int, base := i8(10)) { + s := time.tick_now(); as, err := itoa(a, base); + Timings[.itoa].t += time.tick_since(s); Timings[.itoa].c += 1; + defer delete(as); cb, _ := count_bits(a); fmt.printf("%v (base: %v, bits used: %v): %v\n", name, base, cb, as); if err != .None { fmt.printf("%v (error: %v | %v)\n", name, err, a); } - } -@thread_local string_buffer: [1024]u8; - demo :: proc() { err: Error; + as: string; + + r := &rnd.Rand{}; + rnd.init(r, 12345); destination, source, quotient, remainder, numerator, denominator := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}, &Int{}; defer destroy(destination, source, quotient, remainder, numerator, denominator); - for i in 1..=10 { - err = rand(destination, 1200); // 1200 random bits + err = rand(destination, 120, r); + // print("destination", destination, 10); + for _ in 0 ..< 10_000 { if err != .None { - fmt.printf("rand error: %v\n", err); + fmt.printf("set error: %v\n", err); } else { - fmt.printf("#%3d: ", i); - print("", destination); + s := time.tick_now(); + as, err = itoa(destination, 16); + e := time.tick_since(s); + Timings[.itoa].t += e; Timings[.itoa].c += 1; + assert(as == "B38677A01B8EF75CF434CA68677495", as); + delete(as); } } } @@ -75,9 +96,18 @@ main :: proc() { mem.tracking_allocator_init(&ta, context.allocator); context.allocator = mem.tracking_allocator(&ta); - print_configation(); + // print_configation(); demo(); + fmt.printf("\nTimings:\n"); + for v, i in Timings { + if v.c > 0 { + avg := time.duration_milliseconds(time.Duration(f64(v.t) / f64(v.c))); + total := time.duration_milliseconds(time.Duration(v.t)); + fmt.printf("%v: %.3f ms (avg), %.3f (total, %v calls)\n", i, avg, total, v.c); + } + } + if len(ta.allocation_map) > 0 { for _, v in ta.allocation_map { fmt.printf("Leaked %v bytes @ %v\n", v.size, v.location); diff --git a/core/math/big/helpers.odin b/core/math/big/helpers.odin index a15929d2e..dc4fc697d 100644 --- a/core/math/big/helpers.odin +++ b/core/math/big/helpers.odin @@ -11,7 +11,7 @@ package big import "core:mem" import "core:intrinsics" -import "core:math/rand" +import rnd "core:math/rand" /* Deallocates the backing memory of one or more `Int`s. @@ -177,7 +177,7 @@ neg :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error) { /* Helpers to extract values from the `Int`. */ -extract_bit :: proc(a: ^Int, bit_offset: int) -> (bit: DIGIT, err: Error) { +int_extract_bit :: proc(a: ^Int, bit_offset: int) -> (bit: DIGIT, err: Error) { /* Check that `a`is usable. */ @@ -195,10 +195,7 @@ extract_bit :: proc(a: ^Int, bit_offset: int) -> (bit: DIGIT, err: Error) { return 1 if ((a.digit[limb] & i) != 0) else 0, .None; } -/* - TODO: Optimize. -*/ -extract_bits :: proc(a: ^Int, offset, count: int) -> (res: _WORD, err: Error) { +int_bitfield_extract :: proc(a: ^Int, offset, count: int) -> (res: _WORD, err: Error) { /* Check that `a`is usable. */ @@ -210,18 +207,40 @@ extract_bits :: proc(a: ^Int, offset, count: int) -> (res: _WORD, err: Error) { return 0, .Invalid_Argument; } - v: DIGIT; - e: Error; - for shift := 0; shift < count; shift += 1 { - o := offset + shift; - v, e = extract_bit(a, o); - if e != .None { - break; - } - res = res + _WORD(v) << uint(shift); + limb_lo := offset / _DIGIT_BITS; + bits_lo := offset % _DIGIT_BITS; + limb_hi := (offset + count) / _DIGIT_BITS; + bits_hi := (offset + count) % _DIGIT_BITS; + + if limb_lo < 0 || limb_lo >= a.used || limb_hi < 0 || limb_hi >= a.used { + return 0, .Invalid_Argument; } - return res, e; + for i := limb_hi; i >= limb_lo; i -= 1 { + res <<= _DIGIT_BITS; + + /* + Determine which bits to extract from each DIGIT. The whole DIGIT's worth by default. + */ + bit_count := _DIGIT_BITS; + bit_offset := 0; + if i == limb_lo { + bit_count -= bits_lo; + bit_offset = _DIGIT_BITS - bit_count; + } else if i == limb_hi { + bit_count = bits_hi; + bit_offset = 0; + } + + d := a.digit[i]; + + v := (d >> uint(bit_offset)) & DIGIT(1 << uint(bit_count - 1)); + m := DIGIT(1 << uint(bit_count-1)); + r := v & m; + + res |= _WORD(r); + } + return res, .None; } /* @@ -507,9 +526,9 @@ count_lsb :: proc(a: ^Int) -> (count: int, err: Error) { return count, .None; } -int_random_digit :: proc(r: ^rand.Rand = nil) -> (res: DIGIT) { +int_random_digit :: proc(r: ^rnd.Rand = nil) -> (res: DIGIT) { when _DIGIT_BITS == 60 { // DIGIT = u64 - return DIGIT(rand.uint64(r)) & _MASK; + return DIGIT(rnd.uint64(r)) & _MASK; } else when _DIGIT_BITS == 28 { // DIGIT = u32 return DIGIT(rand.uint32(r)) & _MASK; } else { @@ -519,7 +538,7 @@ int_random_digit :: proc(r: ^rand.Rand = nil) -> (res: DIGIT) { return 0; // We shouldn't get here. } -int_rand :: proc(dest: ^Int, bits: int, r: ^rand.Rand = nil) -> (err: Error) { +int_rand :: proc(dest: ^Int, bits: int, r: ^rnd.Rand = nil) -> (err: Error) { bits := bits; if bits <= 0 { return .Invalid_Argument; } diff --git a/core/math/big/radix.odin b/core/math/big/radix.odin index d64956ba8..06066af18 100644 --- a/core/math/big/radix.odin +++ b/core/math/big/radix.odin @@ -202,7 +202,7 @@ int_itoa_raw :: proc(a: ^Int, radix: i8, buffer: []u8, size := int(-1), zero_ter for offset := 0; offset < count; offset += 4 { bits_to_get := int(min(count - offset, shift)); - if digit, err = extract_bits(a, offset, bits_to_get); err != .None { + if digit, err = int_bitfield_extract(a, offset, bits_to_get); err != .None { return len(buffer) - available, .Invalid_Argument; } available -= 1;