我正在维护一个Julia库,其中包含一个函数,用于在长字符串中每80个字符后插入一行新行。
当字符串长度超过100万个字符时,此函数将变得非常慢(秒或更长)。时间的增长似乎不仅仅是线性的,可能是二次的。我不明白为什么。有人能解释一下吗?
这是一些可复制的代码:
function chop(s; nc=80)
nr = ceil(Int64, length(s)/nc)
l(i) = 1+(nc*(i-1))
r(i) = min(nc*i, length(s))
rows = [String(s[l(i):r(i)]) for i in 1:nr]
return join(rows,'\n')
end
s = "A"^500000
chop(s)
似乎这一行是大部分时间花费的地方:rows=[String(s[l(i): r(i)])for i in 1: nr]
这是否意味着初始化一个新的String
需要很长时间?这并不能真正解释超线性运行时间。
我知道构建字符串的标准快速方法是使用IOBuffer或更高级别的StringBuilders包:https://github.com/davidanthoff/StringBuilders.jl
有人能帮我理解为什么上面的代码这么慢吗?
奇怪的是,下面的速度要快得多,只需添加s=收集(s)
:
function chop(s; nc=80)
s = collect(s) #this line is new
nr = ceil(Int64, length(s)/nc)
l(i) = 1+(nc*(i-1))
r(i) = min(nc*i, length(s))
rows = [String(s[l(i):r(i)]) for i in 1:nr]
return join(rows,'\n')
end
我更倾向于使用通用的单行解决方案,即使它比Przemysaw提出的要慢一点(我已经为简单而不是速度而优化了它):
chop_and_join(s::Union{String,SubString{String}}; nc::Integer=80) =
join((SubString(s, r) for r in findall(Regex(".{1,$nc}"), s)), '\n')
好处是它可以正确处理所有Unicode字符,并且还可以处理子字符串{String}。
给定的解决方案如何工作:
findall(Regex(“..{1,$nc}”)
返回一个范围向量,该向量与多达nc个字符的范围匹配第一次尝试:
chop
,因为它会掩盖Base Julia同名的函数;长度
被多次调用,它是一个昂贵的函数;它应该只被调用一次并存储为变量;长度
是不正确的,因为Julia使用字节索引而不是字符索引(请参阅此处的解释)String(s[l(i): r(i)])
效率低下,因为它分配了String
两次(实际上不需要外部String
)第二次尝试:
s=Collection(s)
解决了多次调用长度
和错误使用字节索引的问题,但效率低下,因为它不必要地分配Vector{Char}
并且它还使您的代码类型不稳定(因为您分配给与最初存储的类型不同的变量s
值);String(s[l(i): r(i)])
首先分配一个小的Vector{Char}
,然后分配String
如果您想要比正则表达式更快的东西并进行更正,您可以使用以下代码:
function chop4(s::Union{String, SubString{String}}; nc::Integer=80)
@assert nc > 0
isempty(s) && return s
sz = sizeof(s)
cu = codeunits(s)
buf_sz = sz + div(sz, nc)
buf = Vector{UInt8}(undef, buf_sz)
start = 1
buf_loc = 1
while true
stop = min(nextind(s, start, nc), sz + 1)
copyto!(buf, buf_loc, cu, start, stop - start)
buf_loc += stop - start
if stop == sz + 1
resize!(buf, buf_loc - 1)
break
else
start = stop
buf[buf_loc] = UInt8('\n')
buf_loc += 1
end
end
return String(buf)
end
可以对字节进行操作
function chop2(s; nc=80)
b = transcode(UInt8, s)
nr = ceil(Int64, length(b)/nc)
l(i) = 1+(nc*(i-1))
r(i) = min(nc*i, length(b))
dat = UInt8[]
for i in 1:nr
append!(dat, @view(b[l(i):r(i)]))
i < nr && push!(dat, UInt8('\n'))
end
String(dat)
end
和基准测试(大约快5000倍):
@btime chop($s);
1.531 s (6267 allocations: 1.28 MiB)
julia> @btime chop2($s);
334.100 μs (13 allocations: 1.57 MiB)
注意事项:
dat
,此代码仍然可以稍微快一点,但我尝试与原始代码类似。String
在Julia中是不可变的,如果需要以这种方式处理字符串,最好先创建一个向量{Char}
,以避免重复分配新的大字符串。
问题内容: 我有一个名为Memcached.Js的项目,它是Memcached服务器到Node.js的端口。 我一直在使用字符串和缓冲区进行比较,比较内存占用量和性能。对于内存,毫无疑问,缓冲区是正确的选择。 但令我惊讶的是,表演并非如此。执行字符串操作比使用缓冲区更快。这是我尝试的: 完整的代码在这里:https : //github.com/dalssoft/memcached.js/blob
问题内容: SimpleDateFormat: 抛出的异常: 有任何想法吗? 编辑: 感谢您的快速解答。你们都是正确的,我只是错过了SimpleDateFormat文档中的一个关键句子-我可能应该把它称为一天。 问题答案: 从SimpleDateFormat javadocs : 月:如果图案字母的数目为3或更多,则将月份解释为文本;否则,将其解释为数字。 尝试使用“ MMM dd yyyy”之类
问题内容: Oracle Java Community网站上的一篇文章提供了以下方法作为示例(对于JPA Converter,但这并不相关): 将String y强制转换为String val有什么用?有正当的理由吗? 原始文章:JPA的新增功能 问题答案: 这样的转换是完全没有必要的。我可以想象那是以前 但是后来参数类型更改为,而作者只是忘了删除强制类型转换。
问题内容: 我认为由于无法识别的转义序列而无法编译。 是什么究竟代表什么? 问题答案: 这是一个八进制转义序列,如JLS的3.10.6节中所列。因此,例如: 等效于: (因为八进制16 =十六进制E。) 因此,我们使用U + 0001,即“开始标题”字符。 根据我的经验,八进制转义序列很少在Java中使用,因此我个人会尽量避免使用它们。当我想使用数字转义序列指定字符时,我总是使用。
我试图执行OffsetDateTime.parse(mytext,dateTimeFormatter.ofpattern(mypattern)),它抛出了DateTimeParseException,无法从TemporalAccessor获得OffsetDateTime
为了好玩,我决定用红宝石编码伊拉托西筛子。只是为了好玩,因为我知道有一个库函数。而且,我认为它会很快。但我发现它并不是,至少在我的ruby 1.9.3中,我的上网本速度快了好几倍,甚至在c中也没有。为什么会这样呢。 库实现: 我在红宝石: 图书馆非常慢。