大文件和哈希 - 性能问题

Large file and hashing - performance concern

我正在尝试使用以下代码逐行散列文件 (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]

运行s(仍然根据time)约2.3秒。

我应该从哪里开始提高我的 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 秒以上,这是为什么?

需要考虑的一点是,用时间来记录Elixir代码的性能总是要考虑的 BEAM 虚拟机的启动时间。取决于你的 应用程序,将其包含在任何应用程序中可能有意义也可能没有意义 与其他语言的比较基准。如果你只是想 最大限度地提高 Elixir 代码的性能,最好使用基准测试 像 Benchfella 这样的工具,甚至只是来自 erlang 的 :timer.tc。

https://hex.pm/packages/benchfella

我猜你的性能问题都与 I/O 相关。 File.stream! 对于大文件的行处理效率不是特别高。

我写了一篇博客 post 讨论对整个文件进行哈希处理的类似问题。

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! 版本的速度提高多个数量级。