Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

show(io::IO, int) optimization #41415

Open
wants to merge 18 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions base/Base.jl
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,8 @@ function __init__()
if haskey(ENV, "JULIA_MAX_NUM_PRECOMPILE_FILES")
MAX_NUM_PRECOMPILE_FILES[] = parse(Int, ENV["JULIA_MAX_NUM_PRECOMPILE_FILES"])
end

init_print_int_scratch()
nothing
end

Expand Down
171 changes: 150 additions & 21 deletions base/intfuncs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -639,10 +639,25 @@ ndigits(x::Integer; base::Integer=10, pad::Integer=1) = max(pad, ndigits0z(x, ba

## integer to string functions ##

function bin(x::Unsigned, pad::Int, neg::Bool)
m = 8 * sizeof(x) - leading_zeros(x)
n = neg + max(pad, m)
a = StringVector(n)
const PRINT_INT_SCRATCH_LENGTH = 256
const PRINT_INT_SCRATCH = Vector{UInt8}[StringVector(PRINT_INT_SCRATCH_LENGTH)]
green-nsk marked this conversation as resolved.
Show resolved Hide resolved

@inline function print_int_scratch(n)
# FIXME: scratch = @inbounds print_int_scratch_[Threads.threadid()]
scratch = @inbounds PRINT_INT_SCRATCH[1]
resize!(scratch, n)
scratch
end

function init_print_int_scratch()
# FIXME: resize_nthreads!(PRINT_INT_SCRATCH, print_int_scratch_[1])
# print_int_scratch() also needs to use Threads.threadid(), but it's
# not clear how to replace that as @eval into base is prohibited

nothing
end

@inline function bin_impl_(a, x::Unsigned, n::Int, neg::Bool)
# for i in 0x0:UInt(n-1) # automatic vectorization produces redundant codes
# @inbounds a[n - i] = 0x30 + (((x >> i) % UInt8)::UInt8 & 0x1)
# end
Expand All @@ -663,29 +678,61 @@ function bin(x::Unsigned, pad::Int, neg::Bool)
i -= 1
end
if neg; @inbounds a[1]=0x2d; end

nothing
end

function bin(x::Unsigned, pad::Int, neg::Bool)
m = 8 * sizeof(x) - Base.leading_zeros(x)
n = neg + max(pad, m)
a = Base.StringVector(n)

bin_impl_(a, x, n, neg)
String(a)
end

function oct(x::Unsigned, pad::Int, neg::Bool)
m = div(8 * sizeof(x) - leading_zeros(x) + 2, 3)
function bin_io(io::IO, x::Unsigned, pad::Int, neg::Bool)
m = 8 * sizeof(x) - Base.leading_zeros(x)
n = neg + max(pad, m)
a = StringVector(n)
scratch = print_int_scratch(n)

bin_impl_(scratch, x, n, neg)
write(io, scratch)
end

@inline function oct_impl_(a, x::Unsigned, n::Int, neg::Bool)
i = n
while i > neg
@inbounds a[i] = 0x30 + ((x % UInt8)::UInt8 & 0x7)
x >>= 0x3
i -= 1
end
if neg; @inbounds a[1]=0x2d; end

nothing
end

function oct(x::Unsigned, pad::Int, neg::Bool)
m = div(8 * sizeof(x) - Base.leading_zeros(x) + 2, 3)
n = neg + max(pad, m)
a = Base.StringVector(n)

oct_impl_(a, x, n, neg)
String(a)
end

function oct_io(io::IO, x::Unsigned, pad::Int, neg::Bool)
m = div(8 * sizeof(x) - leading_zeros(x) + 2, 3)
n = neg + max(pad, m)
scratch = print_int_scratch(n)
oct_impl_(scratch, x, n, neg)
write(io, scratch)
end

# 2-digit decimal characters ("00":"99")
const _dec_d100 = UInt16[(0x30 + i % 10) << 0x8 + (0x30 + i ÷ 10) for i = 0:99]

function dec(x::Unsigned, pad::Int, neg::Bool)
n = neg + ndigits(x, pad=pad)
a = StringVector(n)
@inline function dec_impl_(a, x::Unsigned, n::Int, neg::Bool)
i = n
@inbounds while i >= 2
d, r = divrem(x, 0x64)
Expand All @@ -699,13 +746,25 @@ function dec(x::Unsigned, pad::Int, neg::Bool)
@inbounds a[i] = 0x30 + (rem(x, 0xa) % UInt8)::UInt8
end
if neg; @inbounds a[1]=0x2d; end
String(a)

nothing
end

function hex(x::Unsigned, pad::Int, neg::Bool)
m = 2 * sizeof(x) - (leading_zeros(x) >> 2)
n = neg + max(pad, m)
a = StringVector(n)
function dec(x::Unsigned, pad::Int, neg::Bool)
n = neg + ndigits(x, pad=pad)
scratch = Base.StringVector(n)
dec_impl_(scratch, x, n, neg)
String(scratch)
end

function dec_io(io::IO, x::Unsigned, pad::Int, neg::Bool)
n = neg + ndigits(x, pad=pad)
scratch = print_int_scratch(n)
dec_impl_(scratch, x, n, neg)
write(io, scratch)
end

@inline function hex_impl_(a, x::Unsigned, n::Int, neg::Bool)
i = n
while i >= 2
b = (x % UInt8)::UInt8
Expand All @@ -720,19 +779,32 @@ function hex(x::Unsigned, pad::Int, neg::Bool)
@inbounds a[i] = d + ifelse(d > 0x9, 0x57, 0x30)
end
if neg; @inbounds a[1]=0x2d; end

nothing
end

function hex(x::Unsigned, pad::Int, neg::Bool)
m = 2 * sizeof(x) - (Base.leading_zeros(x) >> 2)
n = neg + max(pad, m)
a = Base.StringVector(n)

hex_impl_(a, x, n, neg)
String(a)
end

function hex_io(io::IO, x::Unsigned, pad::Int, neg::Bool)
m = 2 * sizeof(x) - (leading_zeros(x) >> 2)
n = neg + max(pad, m)
scratch = print_int_scratch(n)
hex_impl_(scratch, x, n, neg)
write(io, scratch)
end

const base36digits = UInt8['0':'9';'a':'z']
const base62digits = UInt8['0':'9';'A':'Z';'a':'z']

function _base(base::Integer, x::Integer, pad::Int, neg::Bool)
(x >= 0) | (base < 0) || throw(DomainError(x, "For negative `x`, `base` must be negative."))
2 <= abs(base) <= 62 || throw(DomainError(base, "base must satisfy 2 ≤ abs(base) ≤ 62"))
b = (base % Int)::Int
@inline function _base_impl_(a, b::Integer, x::Integer, n::Int, neg::Bool)
digits = abs(b) <= 36 ? base36digits : base62digits
n = neg + ndigits(x, base=b, pad=pad)
a = StringVector(n)
i = n
@inbounds while i > neg
if b > 0
Expand All @@ -745,9 +817,33 @@ function _base(base::Integer, x::Integer, pad::Int, neg::Bool)
i -= 1
end
if neg; @inbounds a[1]=0x2d; end

nothing
end

function _base(base::Integer, x::Integer, pad::Int, neg::Bool)
(x >= 0) | (base < 0) || throw(DomainError(x, "For negative `x`, `base` must be negative."))
2 <= abs(base) <= 62 || throw(DomainError(base, "base must satisfy 2 ≤ abs(base) ≤ 62"))
b = (base % Int)::Int
n = neg + ndigits(x, base=b, pad=pad)
a = StringVector(n)

_base_impl_(a, b, x, n, neg)
String(a)
end

function _base_io(io::IO, base::Integer, x::Integer, pad::Int, neg::Bool)
(x >= 0) | (base < 0) || throw(DomainError(x, "For negative `x`, `base` must be negative."))
2 <= abs(base) <= 62 || throw(DomainError(base, "base must satisfy 2 ≤ abs(base) ≤ 62"))
b = (base % Int)::Int
n = neg + ndigits(x, base=b, pad=pad)

scratch = print_int_scratch(n)
_base_impl_(scratch, b, x, n, neg)

write(io, scratch)
end

split_sign(n::Integer) = unsigned(abs(n)), n < 0
split_sign(n::Unsigned) = n, false

Expand Down Expand Up @@ -787,6 +883,39 @@ function string(n::Integer; base::Integer = 10, pad::Integer = 1)
end
end

function print_int(io::IO, n::Integer; base::Integer = 10, pad::Integer = 1)
pad = (min(max(pad, typemin(Int)), typemax(Int)) % Int)::Int
if base == 2
(n_positive, neg) = split_sign(n)
bin_io(io, n_positive, pad, neg)
elseif base == 8
(n_positive, neg) = split_sign(n)
oct_io(io, n_positive, pad, neg)
elseif base == 10
(n_positive, neg) = split_sign(n)
dec_io(io, n_positive, pad, neg)
elseif base == 16
(n_positive, neg) = split_sign(n)
hex_io(io, n_positive, pad, neg)
else
_base_io(io, base, base > 0 ? unsigned(abs(n)) : convert(Signed, n), pad, (base>0) & (n<0))
end

return nothing
end

function print_int(io::IO, prefix::Tuple, n::Integer; base::Integer = 10, pad::Integer = 1)
lock(io)
try
print(io, prefix...)
print_int(io, n; base = base, pad = pad)
finally
unlock(io)
end

return nothing
end

string(b::Bool) = b ? "true" : "false"

"""
Expand Down
9 changes: 5 additions & 4 deletions base/show.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1051,11 +1051,12 @@ end

show(io::IO, ::Nothing) = print(io, "nothing")
show(io::IO, b::Bool) = print(io, get(io, :typeinfo, Any) === Bool ? (b ? "1" : "0") : (b ? "true" : "false"))
show(io::IO, n::Signed) = (write(io, string(n)); nothing)
show(io::IO, n::Unsigned) = print(io, "0x", string(n, pad = sizeof(n)<<1, base = 16))
print(io::IO, n::Unsigned) = print(io, string(n))
show(io::IO, n::Signed) = print_int(io, n)
show(io::IO, n::Unsigned) = print_int(io, ("0x", ), n; pad = sizeof(n)<<1, base = 16)
print(io::IO, n::Signed) = show(io, n)
print(io::IO, n::Unsigned) = print_int(io, n)

show(io::IO, p::Ptr) = print(io, typeof(p), " @0x$(string(UInt(p), base = 16, pad = Sys.WORD_SIZE>>2))")
show(io::IO, p::Ptr) = print_int(io, (typeof(p), " @0x"), UInt(p); base = 16, pad = Sys.WORD_SIZE>>2)

has_tight_type(p::Pair) =
typeof(p.first) == typeof(p).parameters[1] &&
Expand Down
31 changes: 31 additions & 0 deletions test/intfuncs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,37 @@ end
@test bitstring(1035) == (Int == Int32 ? "00000000000000000000010000001011" :
"0000000000000000000000000000000000000000000000000000010000001011")
@test bitstring(Int128(3)) == "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011"

test_print_int = (args...; kwargs...) -> begin
iobuf = IOBuffer()
Base.print_int(iobuf, args...; kwargs...)
String(take!(iobuf))
end

@test test_print_int(UInt32('3'), base = 2) == "110011"
@test test_print_int(UInt32('3'), pad = 7, base = 2) == "0110011"
@test test_print_int(3, base = 2) == "11"
@test test_print_int(3, pad = 2, base = 2) == "11"
@test test_print_int(3, pad = Int32(2), base = Int32(2)) == "11"
@test test_print_int(3, pad = typemin(Int128) + 3, base = 0x2) == "11"
@test test_print_int(3, pad = 3, base = 2) == "011"
@test test_print_int(-3, base = 2) == "-11"
@test test_print_int(-3, pad = 3, base = 2) == "-011"

@test test_print_int(9, base = 8) == "11"
@test test_print_int(-9, base = 8) == "-11"
@test test_print_int(-9, base = 8, pad = 5) == "-00011"
@test test_print_int(-9, base = 8, pad = Int32(5)) == "-00011"

@test test_print_int(121, base = 10) == "121"
@test test_print_int(121, base = 10, pad = 5) == "00121"
@test test_print_int(121, base = 10, pad = 5) == "00121"

@test test_print_int(12, base = 16) == "c"
@test test_print_int(-12, pad = 3, base = 16) == "-00c"
@test test_print_int(-12, pad = Int32(3), base = Int32(16)) == "-00c"

@test test_print_int(5, pad = 7, base = 2) == "0000101"
end

@testset "digits/base" begin
Expand Down