如何使用Erlang以分布式方式读取二进制文件?

时间:2013-12-20 10:43:08

标签: parallel-processing erlang binary-data

我有几个中等大小(1到5 GB)的二进制文件,我想阅读并使用Erlang进行处理。

每个文件都有不同大小的记录,即一个记录为200 kb,但其他记录的大小可能为800 kb。通过读取记录的前几个字节可以获得记录大小。由于它是二进制文件,因此两条记录之间没有分隔符。

要处理这些文件,我们可以编写多线程程序,但为了获得乐趣,我想到使用Erlang并行处理文件。

我是Erlang的新手,所以我不知道如何将文件分成块并将这些块传递给Erlang进程。

任何人都可以提出一些想法吗?

2 个答案:

答案 0 :(得分:2)

我已经在其他编程语言中多次这样做了,这在Erlang中是一个有趣的学习练习。

基本策略是告诉每个进程它的起始字节偏移量,记录大小和要读取的记录数。 (如果在一个文件中有不同大小的记录,则可以传递记录大小列表,如[200, 800, 800, 800, 800, 200, 100])。工作人员独立处理他们的块并将结果返回给父级。

我认为你很容易弄清楚自己。您需要查看erlang:spawnfile:openfile:readfile: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}.

在这里它正在运行,使用5个进程从文件中读取10个数字:

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

答案 1 :(得分:0)

为什么不为每个文件启动进程?我看不出将小文件拆分成块的价值。你的文件已经很小了。这也可以在许多机器上并行化。