From 731424953a325323fce325afa4c36433d96e2b51 Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Sun, 26 Aug 2018 12:16:46 +0200 Subject: [PATCH] improve performance for string(...) (#28876) * improve performance for various string methods --- base/strings/io.jl | 38 +++++++++++++++++++++++++------------- base/strings/substring.jl | 31 ++++++++++++++++++++++--------- test/strings/basic.jl | 1 + 3 files changed, 48 insertions(+), 22 deletions(-) diff --git a/base/strings/io.jl b/base/strings/io.jl index 64b9a613c084e..e2a759c473189 100644 --- a/base/strings/io.jl +++ b/base/strings/io.jl @@ -103,31 +103,43 @@ function sprint(f::Function, args...; context=nothing, sizehint::Integer=0) String(resize!(s.data, s.size)) end -tostr_sizehint(x) = 0 +tostr_sizehint(x) = 8 tostr_sizehint(x::AbstractString) = lastindex(x) tostr_sizehint(x::Float64) = 20 tostr_sizehint(x::Float32) = 12 -function print_to_string(xs...; env=nothing) +function print_to_string(xs...) if isempty(xs) return "" end + siz = 0 + for x in xs + siz += tostr_sizehint(x) + end # specialized for performance reasons - s = IOBuffer(sizehint=tostr_sizehint(xs[1])) - if env !== nothing - env_io = IOContext(s, env) - for x in xs - print(env_io, x) - end - else - for x in xs - print(s, x) - end + s = IOBuffer(sizehint=siz) + for x in xs + print(s, x) end String(resize!(s.data, s.size)) end -string_with_env(env, xs...) = print_to_string(xs...; env=env) +function string_with_env(env, xs...) + if isempty(xs) + return "" + end + siz = 0 + for x in xs + siz += tostr_sizehint(x) + end + # specialized for performance reasons + s = IOBuffer(sizehint=siz) + env_io = IOContext(s, env) + for x in xs + print(env_io, x) + end + String(resize!(s.data, s.size)) +end """ string(xs...) diff --git a/base/strings/substring.jl b/base/strings/substring.jl index d6fba1b41c9bf..9b9e0d1e7424b 100644 --- a/base/strings/substring.jl +++ b/base/strings/substring.jl @@ -142,19 +142,32 @@ function reverse(s::Union{String,SubString{String}})::String end end -function string(a::Union{String, SubString{String}}...) - if length(a) == 1 - return String(a[1]) - end +string(a::String) = String(a) +string(a::SubString{String}) = String(a) + +function string(a::Union{Char, String, SubString{String}}...) n = 0 - for str in a - n += sizeof(str) + for v in a + if v isa Char + n += codelen(v) + else + n += sizeof(v) + end end out = _string_n(n) offs = 1 - for str in a - unsafe_copyto!(pointer(out,offs), pointer(str), sizeof(str)) - offs += sizeof(str) + for v in a + if v isa Char + x = bswap(reinterpret(UInt32, v)) + for j in 1:codelen(v) + unsafe_store!(pointer(out, offs), x % UInt8) + offs += 1 + x >>= 8 + end + else + unsafe_copyto!(pointer(out,offs), pointer(v), sizeof(v)) + offs += sizeof(v) + end end return out end diff --git a/test/strings/basic.jl b/test/strings/basic.jl index 034d45bee9596..521dfa6d52b99 100644 --- a/test/strings/basic.jl +++ b/test/strings/basic.jl @@ -34,6 +34,7 @@ using Random @test string("∀∃", "1∀∃") === "∀∃1∀∃" @test string(SubString("∀∃"), SubString("1∀∃", 2)) === "∀∃∀∃" @test string(s"123") === s"123" + @test string("123", 'α', SubString("1∀∃", 2), 'a', "foo") === "123α∀∃afoo" codegen_egal_of_strings(x, y) = (x===y, x!==y) @test codegen_egal_of_strings(string("ab", 'c'), "abc") === (true, false) let strs = ["", "a", "a b c", "до свидания"]