Skip to content

Instantly share code, notes, and snippets.

@makenowjust
Last active October 17, 2020 00:45
Show Gist options
  • Save makenowjust/2128ff45f232bea179d711b0788052ff to your computer and use it in GitHub Desktop.
Save makenowjust/2128ff45f232bea179d711b0788052ff to your computer and use it in GitHub Desktop.
require "benchmark"
struct Int
DIGITS =
"0001020304050607080910111213141516171819" \
"2021222324252627282930313233343536373839" \
"4041424344454647484950515253545556575859" \
"6061626364656667686970717273747576777879" \
"8081828384858687888990919293949596979899"
def fast_to_s(base : Int = 10, *, upcase : Bool = false) : String
raise ArgumentError.new("Invalid base #{base}") unless 2 <= base <= 36 || base == 62
raise ArgumentError.new("upcase must be false for base 62") if upcase && base == 62
case self
when 0
"0"
when 1
"1"
else
fast_internal_to_s(base, upcase) do |ptr, count|
String.new(ptr, count, count)
end
end
end
def fast_to_s(io : IO, base : Int = 10, *, upcase : Bool = false) : Nil
raise ArgumentError.new("Invalid base #{base}") unless 2 <= base <= 36 || base == 62
raise ArgumentError.new("upcase must be false for base 62") if upcase && base == 62
case self
when 0
io << '0'
when 1
io << '1'
else
fast_internal_to_s(base, upcase) do |ptr, count|
io.write_utf8 Slice.new(ptr, count)
end
end
end
private def fast_internal_to_s(base, upcase = false)
# Given sizeof(self) <= 128 bits, we need at most 128 bytes for a base 2
# representation, plus one byte for the trailing 0.
chars = uninitialized UInt8[129]
ptr_end = chars.to_unsafe + 128
ptr = ptr_end
num = self
neg = num < 0
if base == 10
digits = DIGITS.to_unsafe
while num >= 100 || num <= -100
ptr -= 2
ptr.copy_from(digits + num.remainder(100).abs &* 2, 2)
num = num.tdiv(100)
end
if num >= 10 || num <= -10
ptr -= 2
ptr.copy_from(digits + num.abs &* 2, 2)
elsif num != 0
ptr -= 1
ptr.value = 0x30u8 &+ num.abs
end
else
digits = (base == 62 ? DIGITS_BASE62 : (upcase ? DIGITS_UPCASE : DIGITS_DOWNCASE)).to_unsafe
while num != 0
ptr -= 1
ptr.value = digits[num.remainder(base).abs]
num = num.tdiv(base)
end
end
if neg
ptr -= 1
ptr.value = '-'.ord.to_u8
end
count = (ptr_end - ptr).to_i32
yield ptr, count
end
end
Benchmark.ips do |x|
x.report("to_s") do
1000.times { |i| (i * 123).to_s }
end
x.report("fast_to_s") do
1000.times { |i| (i * 123).fast_to_s }
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment