2010-10-27 4 views
0

Bonjour,Éviter BinaryReader.ReadString() en C#?

Au démarrage de l'application que j'écris, j'ai besoin de lire environ 1.600.000 entrées d'un fichier à Dictionary<Tuple<String, String>, Int32>. Cela prend environ 4-5 secondes pour construire la structure entière en utilisant un BinaryReader (en utilisant un FileReader prend à peu près le même temps). J'ai profilé le code et trouvé que la fonction faisant le plus de travail dans ce processus est BinaryReader.ReadString(). Bien que ce processus ne doive être exécuté qu'une seule fois et au démarrage, je voudrais le faire aussi vite que possible. Est-il possible que je puisse éviter BinaryReader.ReadString() et rendre ce processus plus rapide?

Merci beaucoup.

+0

Évitez le 'BinaryRe ader 'tous ensemble sauf si vous en avez vraiment besoin pour lire des bits/données non alignés. – leppie

Répondre

0

Si vous pensez que la lecture de la ligne de fichiers en ligne est le goulot d'étranglement, et en fonction de sa taille, vous pouvez essayer de le lire à la fois:

// read the entire file at once 
string entireFile = System.IO.File.ReadAllText(path); 

Il cela ne suffit pas, vous pouvez essayer d'ajouter un thread séparé avec un sémaphore, qui commencerait à lire en arrière-plan immédiatement lorsque le programme est démarré, mais bloquer le thread demandeur au moment où vous essayez d'accéder aux données. Cela s'appelle un avenir, et vous avez une implémentation dans la bibliothèque miscutil de Jon Skeet.

Vous l'appelez comme ceci au démarrage de l'application:

// following line invokes "DoTheActualWork" method on a background thread. 
// DoTheActualWork returns an instance of MyData when it's done 
Future<MyData> calculation = new Future<MyData>(() => DoTheActualWork(path)); 

Et puis, quelque temps plus tard, vous pouvez accéder à la valeur dans le thread principal:

// following line blocks the calling thread until 
// the background thread completes 
MyData result = calculation.Value; 

Si vous regardez la La propriété Value de Future, vous pouvez voir qu'elle bloque à AsyncWaitHandle si le thread est toujours en cours d'exécution:

public TResult Value 
{ 
    get 
    { 
     if (!IsCompleted) 
     { 
      _asyncResult.AsyncWaitHandle.WaitOne(); 
      _lock.WaitOne(); 
     } 
     return _value; 
    } 
} 
5

Etes-vous sûr que vous devez absolument avant de continuer?

J'examinerais la possibilité de passer de la tâche à un fil séparé qui définit un drapeau lorsque vous avez terminé. Ensuite, votre code de démarrage lance simplement ce thread et continue sur sa voie joyeuse, en s'arrêtant seulement lorsque les deux:

  • le drapeau n'est pas encore défini; et
  • plus aucun travail ne peut être fait sans les données.

Souvent, l'illusion de la vitesse est assez bon, comme tous ceux qui ont codé un écran de démarrage vous dira. Une autre possibilité, si vous contrôlez les données, est de les stocker sous une forme plus binaire, de sorte que vous puissiez simplement les fusionner d'un seul coup (c'est-à-dire, pas d'interprétation des données, juste lire tout). Cela, bien sûr, rend plus difficile la modification des données provenant de l'extérieur de votre application, mais vous n'avez pas indiqué cela comme une exigence.

Si est une exigence ou vous ne contrôlez pas les données, je serais toujours regarder dans ma première suggestion ci-dessus.

+0

+1, je déteste avoir des applications de démarrage à froid. Ne demander des données que quand vous devez absolument, ou le faire dans une tâche de backgroup ... –

0

Si les chaînes sont répétées à l'intérieur des tuples, vous pouvez réorganiser votre fichier pour avoir toutes les différentes chaînes impliquées au début, et avoir des références à ces chaînes (entiers) dans le corps du fichier. Votre dictionnaire principal n'a pas à changer, mais vous auriez besoin d'un dictionnaire temporaire au démarrage avec toutes les différentes chaînes (valeurs) et leurs références (clés).

Questions connexes