Parallel processing 如何使用Erlang以分布式方式读取二进制文件?
我有几个中等大小(1到5GB)的二进制文件,我想使用Erlang读取和处理这些文件 每个文件都有不同大小的记录,即一条记录的大小为200 kb,而另一条记录的大小可能为800 kb。记录大小可以通过读取记录的前几个字节来获得。由于它是一个二进制文件,所以两个记录之间没有分隔符 为了处理这些文件,我们可以编写多线程程序,但为了好玩,我想到了使用Erlang并行处理文件 我是Erlang的新手,所以我不知道如何将文件分割成块并将这些块传递给Erlang进程Parallel processing 如何使用Erlang以分布式方式读取二进制文件?,parallel-processing,erlang,binary-data,Parallel Processing,Erlang,Binary Data,我有几个中等大小(1到5GB)的二进制文件,我想使用Erlang读取和处理这些文件 每个文件都有不同大小的记录,即一条记录的大小为200 kb,而另一条记录的大小可能为800 kb。记录大小可以通过读取记录的前几个字节来获得。由于它是一个二进制文件,所以两个记录之间没有分隔符 为了处理这些文件,我们可以编写多线程程序,但为了好玩,我想到了使用Erlang并行处理文件 我是Erlang的新手,所以我不知道如何将文件分割成块并将这些块传递给Erlang进程 任何人都可以提供一些想法吗?为什么不为每个
任何人都可以提供一些想法吗?为什么不为每个文件启动一个流程?我看不出将小文件分割成块的价值。你的文件已经很小了。这也可以在许多机器上并行化。我在其他编程语言中也做过很多次,在Erlang中这是一个有趣的学习练习 基本策略是告诉每个进程它的起始字节偏移量、记录大小和要读取的记录数。(如果一个文件中有不同大小的记录,则可能会传递一个记录大小列表,如
[200800800800800800800200100]
)。工人独立处理他们的区块,并将结果返回给父级
我认为这很容易,你可以自己解决。您需要将erlang:spawn
、文件:open
、文件:read
和文件:position
作为主要部分。但是如果你想要破坏者,这里是我的一个模块的实现,它从一个二进制文件中读取数字,并使用多个进程找到平均值。(我不是Erlang专家,所以可能有更好的方法。)
一般来说,我会让一个“阅读器”进程读取记录,然后将它们发送给一个或多个工作进程。通过将繁重的工作(读后处理)转移到不同的进程/核心,这限制了阅读过程必须做的额外工作量(从而确保它的阅读速度尽可能快)。很抱歉,回复太晚了。我在过圣诞节。非常感谢分享您的知识。我正在我的机器上尝试你的程序,很快会让你知道我的结果。
-module(average).
-export([write_file/3, read_file/4]).
-export([read_file_worker/5]).
write_file(Filename, BlockSize, ListOfNumbers) ->
BS = BlockSize*8,
BinData = [<<X:BS>> || X <- ListOfNumbers],
{ok, IoDevice} = file:open(Filename, [write, raw, binary]),
file:write(IoDevice, BinData),
file:close(IoDevice).
read_file(Filename, BlocksPerProcess, BlockSize, TotalBlockCount) ->
{ok, SpawnCount} = read_file_spawner(Filename, BlocksPerProcess, BlockSize, TotalBlockCount, 0, 0),
{ok, Sum} = read_file_listener(SpawnCount, 0),
io:format("Total sum: ~p~nNumbers seen: ~p~nAverage: ~p~n", [Sum, TotalBlockCount, Sum/TotalBlockCount]).
read_file_spawner(Filename, BlocksPerProcess, BlockSize, TotalBlockCount, BlockOffset, SpawnCount) when BlockOffset < TotalBlockCount ->
Offset = BlockOffset * BlockSize,
MaxBlocks = min(BlocksPerProcess, TotalBlockCount - BlockOffset),
spawn(?MODULE, read_file_worker, [self(), Filename, Offset, BlockSize, MaxBlocks]),
read_file_spawner(Filename, BlocksPerProcess, BlockSize, TotalBlockCount, BlockOffset + BlocksPerProcess, SpawnCount + 1);
read_file_spawner(_Filename, _BlocksPerProcess, _BlockSize, _TotalBlockCount, _BlockOffset, SpawnCount) ->
{ok, SpawnCount}.
read_file_listener(0, Accum) ->
{ok, Accum};
read_file_listener(SpawnCount, Accum) ->
receive
{ok, Number} ->
io:format("Got ~p~n", [Number]),
read_file_listener(SpawnCount - 1, Accum + Number)
end.
read_file_worker(MasterPid, Filename, Offset, BlockSize, MaxBlocks) ->
{ok, IoDevice} = file:open(Filename, [read, raw, binary]),
{ok, Offset} = file:position(IoDevice, {bof, Offset}),
{ok, Sum} = read_file_worker_loop(IoDevice, BlockSize, 0, MaxBlocks, 0),
MasterPid ! {ok, Sum}.
read_file_worker_loop(IoDevice, BlockSize, BlocksRead, MaxBlocks, Accum) when BlocksRead < MaxBlocks ->
{ok, BinData} = file:read(IoDevice, BlockSize),
Number = binary:decode_unsigned(BinData),
read_file_worker_loop(IoDevice, BlockSize, BlocksRead + 1, MaxBlocks, Accum + Number);
read_file_worker_loop(_IoDevice, _BlockSize, _BlocksRead, _MaxBlocks, Accum) ->
{ok, Accum}.
12> Numbers = [1,1,2,4,6,10,10,1,0,500].
[1,1,2,4,6,10,10,1,0,500]
13> average:write_file("/tmp/test.binary", 32, Numbers).
ok
14> average:read_file("/tmp/file.binary", 2, 32, length(Numbers)).
Got 500
Got 11
Got 6
Got 16
Got 2
Total sum: 535
Numbers seen: 10
Average: 53.5
ok