我尝试使用以下代码逐行散列文件(16 MB):
def hash(data, protocol) do
:crypto.hash(protocol, data)
|> Base.encode16()
end
File.stream!(path)
|> Stream.map(&hash(&1, :md5) <> "h")
|> Enum.to_list()
|> hd()
|> IO.puts()
根据time
命令,这需要10到12秒,这似乎是一个巨大的数字,我考虑使用以下Python代码:
import md5
with open('a', 'r') as f:
content = f.readlines()
l = []
for _, val in enumerate(content):
m = md5.new()
m.update(val)
l.append(m.hexdigest() + "h")
print l[0]
在大约2.3秒内运行(仍然根据time
)。
我将在哪里提高我的Elixir代码的性能?我试图将初始流拆分为10个块,并为每个块启动异步任务:
File.stream!(path)
|> Stream.chunk(chunk_size) # with chunk_size being (nb_of_lines_in_file / 10)
|> Enum.map(fn chunk -> Task.async(fn -> Enum.map(chunk, &hash(&1, :md5) <> "h") end) end)
|> Enum.flat_map(&Task.await/1)
|> hd()
|> IO.puts()
但它会产生甚至更差的结果,大约11秒以上,这是为什么?
答案 0 :(得分:2)
要考虑的一件事是,使用时间来记录Elixir代码的性能总是要考虑在内 BEAM虚拟机的启动时间。取决于你的 申请,将其包含在任何内容中可能有意义也可能没有意义 比较其他语言的基准。如果你只是想 最大化Elixir代码的性能,最好使用基准测试 像Benchfella这样的工具甚至只是:来自erlang的timer.tc。
https://hex.pm/packages/benchfella
我的猜测是你的性能问题都与I / O有关。
File.stream!
对大型文件的行处理效率不高。
我写了一篇关于哈希整个文件的类似问题的博文。
http://www.cursingthedarkness.com/2015/06/micro-benchmarking-in-elixir-using.html
这里有关于快速基于线路处理的幻灯片。
http://bbense.github.io/beatwc/
我认为如果你把整个文件淹没在你身上会得到更好的表现。我会毫不犹豫地使用
File.stream!(path) |> Enum.map(fn(line) -> hash(line, :md5) <> "h" end )
获取16mb文件。在管道中使用Stream几乎总是以内存的速度进行交换。由于数据在Elixir中是不可变的,因此大型列表的开销通常比您最初预期的要少。
基于任务的代码不会有太大的帮助,因为我怀疑大多数代码 时间用于分块这两行中的行。
File.stream!(path)
|> Stream.chunk(chunk_size) # with chunk_size being (nb_of_lines_in_file / 10)
那会很慢。您可能觉得有用的另一个代码示例。 https://github.com/dimroc/etl-language-comparison/tree/master/elixir
您可以使用许多技巧在Elixir中快速处理文件。您通常可以将原始File.stream!
版本的速度提高多个数量级。