如何在Erlang中有效地读取STDIN中的数千行?

时间:2017-09-29 16:29:17

标签: erlang stdin

从STDIN读取数千行时,我偶然发现了一个问题。在我发现this problem的某些测试需要从STDIN读取数千行之前,这本来就是一个想象的边缘情况。起初我认为我的算法不是最优的,只是偶然地发现只有没有任何计算的读取行可以使测试时间缩短一半。

以下是超时部分代码:

process_queries(0, _) -> ok;
process_queries(N, A) -> 
    case io:fread("", "~s~d~d") of
        {ok, _} -> process_queries(N - 1, A)
        %{ok, ["Q", L, R]} -> process_queries(N - 1, apply_q(L, R, A));
        %{ok, ["U", Idx, Val]} -> process_queries(N - 1, apply_u(Idx, Val, A))
    end
.

我故意留下评论,表明所有的计算都被禁用了。因此,此代码在给定N=7984时超时。

在Erlang中有没有更好的方法从STDIN读取和处理数千行?

  • io:get_line一次只能获得一行。
  • io:get_chars要求您知道要获得多少角色。

2 个答案:

答案 0 :(得分:2)

我建议将stdio切换为二进制,然后使用io:get_line。通过拆分空格并将两个值转换为整数,您的数据格式非常简单。在简单的基准测试中,以下代码的运行速度比我的代码快〜10倍。我使用escript进行基准测试,这意味着由于escript会动态解析和编译代码,因此差异很可能超过10倍。

process_queries_2(0, _) -> ok;
process_queries_2(N, A) -> 
    Line = io:get_line(""),
    [X, Y0, Z0, _] = binary:split(Line, [<<$\s>>, <<$\n>>], [global]),
    Y = binary_to_integer(Y0),
    Z = binary_to_integer(Z0),
    % use X, Y, Z
    process_queries_2(N - 1, A).

以下是我用来进行基准测试的代码:

main(["1"]) ->
  ok = io:setopts(standard_io, [binary]),
  process_queries(10000, {});
main(["2"]) ->
  ok = io:setopts(standard_io, [binary]),
  process_queries_2(10000, {}).%
$ time yes 'Q 100 200' | escript a.erl 1
yes 'Q 100 200'  4.64s user 0.11s system 93% cpu 5.063 total
escript a.erl 1  4.67s user 1.44s system 120% cpu 5.062 total
$ time yes 'Q 100 200' | escript a.erl 2
yes 'Q 100 200'  0.36s user 0.01s system 77% cpu 0.466 total
escript a.erl 2  0.40s user 0.10s system 106% cpu 0.464 total

加速的原因是Erlang Strings是链表,与二进制相比,这对于CPU时间和内存使用都是非常低效的。二进制是一个连续的内存块。

答案 1 :(得分:2)

我的解决方案有一段摘录。如何做到这一点非常有效。

read_command(CP) ->
    {ok, Line} = file:read_line(standard_io),
    [C, A, B] = binary:split(Line, CP, [global, trim_all]),
    {case C of <<"Q">> -> 'Q'; <<"U">> -> 'U' end,
     binary_to_integer(A),
     binary_to_integer(B)}.

read_commands(N, CP) ->
    [ read_command(CP) || _ <- lists:seq(1, N) ].

execute(Array, L) ->
    lists:foldl(fun({'Q', F, T}, A) ->
                        {Val, A2} = query(A, F, T),
                        file:write(standard_io, [integer_to_binary(Val), $\n]),
                        A2;
                   ({'U', I, V}, A) ->
                        update(A, I, V)
                end, Array, L).

read_int_line(CP) ->
    {ok, Line} = file:read_line(standard_io),
    [binary_to_integer(X) || X <- binary:split(Line, CP, [global, trim_all])].

main() ->
    ok = io:setopts([binary]),
    CP = binary:compile_pattern([<<" ">>, <<$\n>>]),
    [N] = read_int_line(CP),
    L = read_int_line(CP),
    N = length(L),
    [K] = read_int_line(CP),
    execute(init(L), read_commands(K, CP)).

当然,您必须自己编写init/1update/3query/3