大文件和哈希 - 性能问题
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!
版本的速度提高多个数量级。
我正在尝试使用以下代码逐行散列文件 (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!
版本的速度提高多个数量级。