«Средняя длина последовательностей в файле fasta»: можете ли вы улучшить этот код Erlang? - PullRequest
6 голосов
/ 21 июля 2010

Я пытаюсь получить среднюю длину быстрых последовательностей , используя Erlang . Фаста файл выглядит так

>title1
ATGACTAGCTAGCAGCGATCGACCGTCGTACGC
ATCGATCGCATCGATGCTACGATCGATCATATA
ATGACTAGCTAGCAGCGATCGACCGTCGTACGC
ATCGATCGCATCGATGCTACGATCTCGTACGC
>title2
ATCGATCGCATCGATGCTACGATCTCGTACGC
ATGACTAGCTAGCAGCGATCGACCGTCGTACGC
ATCGATCGCATCGATGCTACGATCGATCATATA
ATGACTAGCTAGCAGCGATCGACCGTCGTACGC
>title3
ATCGATCGCATCGAT(...)

Я пытался ответить на этот вопрос, используя следующий Erlang код:

-module(golf).
-export([test/0]).

line([],{Sequences,Total}) ->  {Sequences,Total};
line(">" ++ Rest,{Sequences,Total}) -> {Sequences+1,Total};
line(L,{Sequences,Total}) -> {Sequences,Total+string:len(string:strip(L))}.

scanLines(S,Sequences,Total)->
        case io:get_line(S,'') of
            eof -> {Sequences,Total};
            {error,_} ->{Sequences,Total};
            Line -> {S2,T2}=line(Line,{Sequences,Total}), scanLines(S,S2,T2)
        end  .

test()->
    {Sequences,Total}=scanLines(standard_io,0,0),
    io:format("~p\n",[Total/(1.0*Sequences)]),
    halt().

Компиляция / Execution

erlc golf.erl
erl -noshell -s golf test < sequence.fasta
563.16

этот код работает нормально для небольшого файла fasta, но для его анализа требуется несколько часов (> 100Mo). Зачем ? Я новичок в Erlang, не могли бы вы улучшить этот код?

Ответы [ 5 ]

5 голосов
/ 22 июля 2010

Если вам нужен действительно быстрый ввод-вывод, вам придется сделать немного больше хитрости, чем обычно.

-module(g).
-export([s/0]).
s()->
  P = open_port({fd, 0, 1}, [in, binary, {line, 256}]),
  r(P, 0, 0),
  halt().
r(P, C, L) ->
  receive
    {P, {data, {eol, <<$>:8, _/binary>>}}} ->
      r(P, C+1, L);
    {P, {data, {eol, Line}}} ->
      r(P, C, L + size(Line));
    {'EXIT', P, normal} ->
      io:format("~p~n",[L/C])
  end.

Это самый быстрый ввод-вывод, насколько я знаю, но обратите внимание -noshell -noinput.Компилируем так же, как erlc +native +"{hipe, [o3]}" g.erl, но с -smp disable

erl -smp disable -noinput -mode minimal -boot start_clean -s erl_compile compile_cmdline @cwd /home/hynek/Download @option native @option '{hipe, [o3]}' @files g.erl

и выполняем:

time erl -smp disable -noshell -mode minimal -boot start_clean -noinput -s g s < uniprot_sprot.fasta
352.6697028442464

real    0m3.241s
user    0m3.060s
sys     0m0.124s

С -smp enable, но с использованием собственного кода:

$ erlc +native +"{hipe, [o3]}" g.erl
$ time erl -noshell -mode minimal -boot start_clean -noinput -s g s<uniprot_sprot.fasta
352.6697028442464

real    0m5.103s
user    0m4.944s
sys     0m0.112s

Байт-код, но с -smp disable (почти наравне с нативным, потому что большая часть работы выполняется в порту!):

$ erlc g.erl
$ time erl -smp disable -noshell -mode minimal -boot start_clean -noinput -s g s<uniprot_sprot.fasta
352.6697028442464

real    0m3.565s
user    0m3.436s
sys     0m0.104s

Просто для полноты байт-кода с smp:

$ time erl -noshell -mode minimal -boot start_clean -noinput -s g s<uniprot_sprot.fasta 
352.6697028442464

real    0m5.433s
user    0m5.236s
sys     0m0.128s

Для сравнения sarnold версия дает мне неправильный ответ и берет больше на том же HW:

$ erl -smp disable -noinput -mode minimal -boot start_clean -s erl_compile compile_cmdline @cwd /home/hynek/Download @option native @option '{hipe, [o3]}' @files golf.erl
./golf.erl:5: Warning: variable 'Rest' is unused
$ time erl -smp disable -noshell -mode minimal -s golf test
359.04679841439776

real    0m17.569s
user    0m16.749s
sys     0m0.664s

EDIT : я посмотрел на характеристикиuniprot_sprot.fasta и я немного удивлен.Это 3824397 строк и 232 МБ.Это означает, что версия -smp disabled может обрабатывать 1,18 миллиона строк текста в секунду (71 МБ / с при линейно-ориентированном вводе-выводе).

3 голосов
/ 21 июля 2010

Я тоже изучаю Erlang, спасибо за интересный вопрос.

Я понимаю, что работа со строками Erlang так как списки символов могут быть очень медленными;если вы можете работать с двоичными файлами , вы должны увидеть некоторое увеличение производительности.Я не знаю, как бы вы использовали строки произвольной длины с двоичными файлами, но если вы можете разобраться с этим, это должно помочь.

Кроме того, если вы не против работать с файлом напрямую, а не standard_io, возможно, вы могли бы ускорить процесс, используя file:open(..., [raw, read_ahead]).raw означает, что файл должен находиться в файловой системе локального узла, а read_ahead указывает, что Erlang должен выполнить IO файла с буфером.(Подумайте об использовании средств stdio в C с буферизацией и без нее.)

Я бы ожидал, что read_ahead будет иметь наибольшее значение, но все, что есть в Erlang, включает фразу "эталон перед угадыванием".1015 * РЕДАКТИРОВАТЬ

Использование file:open("uniprot_sprot.fasta", [read, read_ahead]) дает 1m31s для полного набора данных uniprot_sprot.fasta.(Среднее 359.04679841439776.)

Используя file:open(.., [read, read_ahead]) и file:read_line(S), я получаю 0m34s.

Используя file:open(.., [read, read_ahead, raw]) и file:read_line(S), я получаю 0m9s.Да, девять секунд.

Вот где я стою сейчас;если вы сможете понять, как использовать двоичные файлы вместо списков, это может привести к еще большему улучшению:

-module(golf).
-export([test/0]).

line([],{Sequences,Total}) ->  {Sequences,Total};
line(">" ++ Rest,{Sequences,Total}) -> {Sequences+1,Total};
line(L,{Sequences,Total}) -> {Sequences,Total+string:len(string:strip(L))}.

scanLines(S,Sequences,Total)->
        case file:read_line(S) of
            eof -> {Sequences,Total};
            {error,_} ->{Sequences,Total};
            {ok, Line} -> {S2,T2}=line(Line,{Sequences,Total}), scanLines(S,S2,T2)
        end  .

test()->
    F = file:open("/home/sarnold/tmp/uniprot_sprot.fasta", [read, read_ahead, raw]),
    case F of
    { ok, File } -> 
        {Sequences,Total}=scanLines(File,0,0),
        io:format("~p\n",[Total/(1.0*Sequences)]);
    { error, Reason } ->
            io:format("~s", Reason)
    end,
    halt().
2 голосов
/ 21 июля 2010

Похоже, что ваши большие проблемы с производительностью были решены путем открытия файла в необработанном режиме, но здесь есть еще несколько соображений, если вам нужно дополнительно оптимизировать этот код.

Изучите и используйте fprof.

Вы используете string:strip/1 в основном для удаления завершающего перевода строки.Поскольку значения erlang являются неизменяемыми, вам необходимо сделать полную копию списка (со всем связанным распределением памяти), чтобы удалить последний символ.Если вы знаете, что файл правильно сформирован, просто вычтите один из вашего числа, иначе я бы попытался написать функцию длины, которая подсчитывает количество соответствующих символов и игнорирует не относящиеся к делу.

говорит, что двоичные файлы лучше, чем списки, но учитывая, как мало вы обрабатываете, это, вероятно, имеет место здесь.Первые шаги - открыть файл в двоичном режиме и использовать erlang:size/1 для определения длины.

Это не повлияет на производительность (значительно), но умножение на 1,0 в Total/(1.0*Sequences) необходимо только вязыки с нарушенным делением.Эрлангское деление работает корректно.

1 голос
/ 21 июля 2010

Вызов string:len(string:strip(L)) обходит список по крайней мере дважды (я не знаю о строке: реализация полосы). Вместо этого вы можете написать простую функцию для подсчета длины строки без пробелов:

stripped_len(L) ->
  stripped_len(L, 0).

stripped_len([$ |L], Len) ->
  stripped_len(L, Len);

stripped_len([_C|L], Len) ->
  stripped_len(L, Len + 1);

stripped_len([], Len) ->
  Len.

Тот же метод может быть применен и к двоичным файлам.

0 голосов
/ 11 апреля 2014

Вы пробовали Elixir (elixir-lang.org), который работает поверх Erlang и имеет синтаксис, похожий на Ruby. Эликсир решает проблемы со строками следующим образом:

Строки Elixir - это бинарные файлы UTF8 со всей необработанной скоростью и памятью. экономия, которая приносит. Elixir имеет строковый модуль с Unicode встроенная функциональность и является отличным примером написания кода, который пишет код String.Unicode читает различные дампы базы данных Unicode, такие как как UnicodeData.txt для динамического создания функций Unicode для Строковый модуль построен прямо из этих данных! (http://devintorr.es/blog/2013/01/22/the-excitement-of-elixir/)

Просто интересно, будет ли Эликсир быстрее?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...