如何快速解码GBK字符串?


如图所示, 解析一个通过UTF-8编码的UInt8的字符串,有两种方式:
方式一是先转换成指针,再用unsafe_string来解码。
方式二是使用StringEncodings里面的decode模块。
从图片可以看出,第一次运行时,方式一的速度是方式二的50倍,第二次运行时,方式二的速度是方式一的8倍。为啥这两种方式速度差异这么大?

对于UTF-8编码的字符串而言,方式一确实已经够用了,但是如果是GBK编码,如何快速解码呢?

@btime 来测试,你去论坛里搜搜这一方面的帖子

Julia 字符串使用 UTF-8 编码,用 UTF-8 编解码更快。

实际上 UTF-8 字符串建议用自带函数,比如

julia> using BenchmarkTools
julia> msg = "abαβ你好";
julia> encode = @btime codeunits(msg)
  14.278 ns (1 allocation: 16 bytes)
12-element Base.CodeUnits{UInt8, String}:
julia> data = @btime Vector{UInt8}(msg)
  157.557 ns (1 allocation: 96 bytes)
12-element Vector{UInt8}:

调包反而更慢

using StringEncodings, BenchmarkTools
msg = "abαβ你好";
data = @btime encode(msg, "UTF-8")
# 2.061 μs (22 allocations: 1.30 KiB)

同样地,解码建议用自带函数 String,比如

encode = codeunits(msg);
# 加 copy 由于 String 解析过程会清空输入数列,参见 String 文档
msg = @btime String(copy(data)) 
#  147.540 ns (2 allocations: 128 bytes)
# "abαβ你好"
msg = @btime decode(data, "UTF-8")
#  1.913 μs (23 allocations: 2.23 KiB)
# "abαβ你好"

按编码规则手写一个?比如写个字典,GBK一共 2^16=65536 个字符。

Screenshot_20220928_154542
是这样的,我这边要处理的UInt8的字符串,往往是图中这种固定长度的,剩余部分会以0结尾。我用unsafe_string的方式处理,它会自动把末尾的零给抹掉。得到

"明月"

而不是

"明月\0\0\0\0"

如果我要用String来转换,完整的代码是这样的:
Screenshot_20220928_154617
加上查找第一个零这一系列的动作后,这种方式就慢了。

同上,建议用 BenchmarkTools 测试,@time 只会计算一次的时间,误差大。

如果不修改数组,unsafe_stringString 更快。

data = rand(0x01:0xff, 20);
@btime unsafe_string(pointer(copy(data)))
# 59.017 ns (3 allocations: 144 bytes)
@btime String((copy(data)))
# 140.265 ns (2 allocations: 128 bytes)

如果允许清空数组,String 性能可以很高,比如

 # 这里除了第一次运算,之后都是处理空集,所以看起来快了
@btime String($(copy(data)))
 # 3.069 ns (0 allocations: 0 bytes)

纠正:unsafe_string 总比 String 更快,如果你有去 \0 的操作,前者更适合了。@linanisyugioh

这个我试了一下,它主要不是切片慢。是查找第一个零这个查找动作慢。

所以用@view的效果并不明显。