6
votes

"Longueur moyenne des séquences dans un fichier FASTA": pouvez-vous améliorer ce code Erlang?

J'essaie d'obtenir la longueur moyenne de FASTA Séquences en utilisant Erlang . Un fichier FASTA ressemble à ceci xxx

J'ai essayé de répondre à cette question en utilisant le code suivant erlang code: xxx

compilation / exécution : xxx

Ce code semble fonctionner correctement pour un petit fichier FASTA, mais il faut des heures pour analyser une plus grande (> 100mo ). Pourquoi ? Je suis un erlang novice, pouvez-vous améliorer ce code?


2 commentaires

Voir aussi le 'Challenge' original: biostar.stackexchange.com/questions/1759


WOW, excellente collection d'échantillons de code non triviaux à partir d'un large éventail de langues. Merci!


5 Réponses :


3
votes

J'ai aussi apprencez Erlang, merci pour la question amusante.

Je comprends travailler avec Erlang Strings lorsque des listes de caractères peuvent être très lentes; Si vous pouvez Travaillez avec des binaires à la place, vous devriez voir des gains de performance. Je ne sais pas comment vous utiliseriez des chaînes de longueur arbitraire avec des fichiers binaires, mais si vous pouvez trier le coup, cela devrait aider.

Aussi, si cela ne vous dérange pas de travailler avec un fichier directement plutôt que de Standard_IO , peut-être que vous pourriez peut-être accélérer les choses en utilisant Fichier: Ouvrir (..., [RAW, READ_AHEAD]) . RAW signifie que le fichier doit figurer sur le système de fichiers du nœud local et read_ahead Spécifie que Erlang doit effectuer un fichier io avec un tampon. (Pensez à utiliser les installations de Stdio de C avec et sans tampon.)

Je m'attendais à ce que le read_ahad Pour faire la plus grande différence, mais tout avec Erlang inclut la phrase "référence avant de deviner" «. Code> 1m31s sur l'ensemble de données UNIPROT_SPROT.FASTA. (Moyenne 359.04679841439776.)

Utilisation de Fichier: Ouvrir (.., [Lire, Read_Ahead]) et Fichier: read_line (s) , je reçois 0m34s .

Utilisation de Fichier: Ouvrir (.., [Lire, read_ahead, brut]) et Fichier: read_line (s) < / Code>, je reçois 0m9s . Oui, neuf secondes.

Voici où je me tiens maintenant; Si vous pouvez comprendre comment utiliser des fichiers binaires au lieu de listes, cela pourrait voir encore plus d'amélioration: xxx


2 commentaires

Merci Arnold, je te teste actuellement de solution. erl (version = r13b01) a soulevé une erreur: "{" init terminant dans do_boot ", {UNDEF, [{File, read_line, [{File_Descriptor, prim_file, {# Port <0.286>, 7}}]}, {golf, scanlines, 3}, {golf, test, 0}, {init, start_it, 1}, {init, st art_em, 1}]}} ". Une idée ?


Ok, j'ai dit que ma version ne prend pas en charge R13B01, je vais tester cela demain sur un autre ordinateur.



2
votes

On dirait que vos problèmes de performances ont été résolus en ouvrant le fichier en mode brut, mais voici quelques pensées supplémentaires si vous avez besoin d'optimiser ce code plus loin.

Apprendre et utiliser FPROF.

Vous utilisez String: Strip / 1 principalement pour supprimer la nouvelle ligne de fuite. Comme les valeurs d'Erlang sont immuables, vous devez effectuer une copie complète de la liste (avec toutes les allocations de mémoire associées) pour supprimer le dernier caractère. Si vous connaissez le fichier est bien formé, il suffit de soustraire un de votre compte, sinon j'essayerais d'écrire une fonction de longueur, le nombre de caractères pertinents et ignore les pertinents.

Je suis méfiant des conseils qui indiquent que les fichiers binaires sont meilleurs que des listes, mais étant donné à quel point vous traitez probablement le cas ici. Les premières étapes consistent à ouvrir le fichier en mode binaire et à utiliser erlang: taille / 1 pour trouver la longueur.

Cela n'affectera pas les performances (de manière significative), mais la multiplication par 1,0 dans total / (1,0 * séquences) n'est nécessaire que dans des langues avec une division brisée. La division Erlang fonctionne correctement.


0 commentaires

1
votes

L'appel String: LEN (String: Strip (L)) Code> traverse la liste au moins deux fois (je ne suis pas au courant de la chaîne: mise en œuvre de la bande). Au lieu de cela, vous pouvez écrire une fonction simple pour compter la longueur de la ligne avec les espaces:

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 commentaires

5
votes

Si vous avez besoin de vraiment rapide io, vous devez faire un peu plus de tenter que d'habitude.

$ 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


4 commentaires

Excellent! Merci pour l'exemple; Pouvez-vous souligner pourquoi nos versions obtiennent des réponses différentes? Merci!


@Sarnold: Je n'ai pas assez de temps pour regarder votre version où est le problème. Je suppose que c'est une trailing "\ n" qui peut être dépouillé par string: bande / 1 mais je ne suis pas sûr. J'ai vérifié par ce code perl -nle '/ ^> /? $ C ++: ($ B + = longueur ((/ (\ s *) /) [0]))} {imprimé $ B / $ C' < / Code> Pour être sûr que la version de la mine n'a pas le même bug que tous les autres sur biostar.stackexchange.com/questions / 1759 Mais tout semble bien et 352.6697028442464 devrait avoir une bonne réponse.


@Sarnold: Une vérification très simple me dit que "\ n" est-ce. Lorsque vous soustrayez l'une de chaque longueur de ligne, vous obtenez une bonne réponse, donc cela signifie fichier: read_line / 1 renvoie la ligne avec la nouvelle ligne Char et String: Strip / 1 ne supprime pas ce.


Merci! Je ne pouvais pas comprendre où nos versions produisaient des calculs différents ...



0
votes

Avez-vous essayé Elixir (Elixir-lang.org) qui fonctionne sur Erlang et a une syntaxe similaire à Ruby. Elixir résout des problèmes de chaîne de la manière suivante:

Les chaînes d'Elixir sont des fichiers binaires UTF8, avec toute la vitesse brute et la mémoire économies qui apporte. Elixir a un module de chaîne avec Unicode fonctionnalité intégrée et est un excellent exemple de code d'écriture qui écrit le code. String.unicode lit divers décharges de base de données Unicode tels que comme unicodedata.txt pour générer de manière dynamique des fonctions Unicode pour la Module de chaîne construit directement à partir de ces données! ( http://devintorr.es/blog/2013/01 / 22 / The-Excitement-of-Elixir / )

Je me demande simplement si Elixir serait plus rapide?


1 commentaires

Vous ne devriez pas deviner mais mesurer.