Moyen efficace d'écrire beaucoup de lignes dans un fichier texte
J'ai commencé à faire quelque chose comme suit:
using (TextWriter textWriter = new StreamWriter(filePath, append))
{
foreach (MyClassA myClassA in myClassAs)
{
textWriter.WriteLine(myIO.GetCharArray(myClassA));
if (myClassA.MyClassBs != null)
myClassA.MyClassBs.ToList()
.ForEach(myClassB =>
textWriter.WriteLine(myIO.GetCharArray((myClassB)));
if (myClassA.MyClassCs != null)
myClassA.MyClassCs.ToList()
.ForEach(myClassC =>
textWriter.WriteLine(myIO.GetCharArray(myClassC)));
}
}
Cela semblait assez lent (~35 secondes pour 35 000 lignes).
Puis j'ai essayé de suivre l'exemple ici à créer une zone tampon, avec le code suivant, mais il n'a pas gain de moi quoi que ce soit. J'ai été voir encore temps d'environ 35 secondes. Est-il une erreur dans la façon dont j'ai mis en place le tampon?
using (TextWriter textWriter = new StreamWriter(filePath, append))
{
char[] newLineChars = Environment.NewLine.ToCharArray();
//Chunk through 10 lines at a time.
int bufferSize = 500 * (RECORD_SIZE + newLineChars.Count());
char[] buffer = new char[bufferSize];
int recordLineSize = RECORD_SIZE + newLineChars.Count();
int bufferIndex = 0;
foreach (MyClassA myClassA in myClassAs)
{
IEnumerable<IMyClass> myClasses =
new List<IMyClass> { myClassA }
.Union(myClassA.MyClassBs)
.Union(myClassA.MyClassCs);
foreach (IMyClass myClass in myClasses)
{
Array.Copy(myIO.GetCharArray(myClass).Concat(newLineChars).ToArray(),
0, buffer, bufferIndex, recordLineSize);
bufferIndex += recordLineSize;
if (bufferIndex >= bufferSize)
{
textWriter.Write(buffer);
bufferIndex = 0;
}
}
}
if (bufferIndex > 0)
textWriter.Write(buffer);
}
Est-il un meilleur moyen pour y parvenir?
- Avez-vous essayé d'utiliser StringBuilder et de construire la chaîne alors il suffit d'écrire une fois pour le fichier.
- aucune raison particulière d'utiliser des tampons?
- Comment êtes-vous certain que c'est vraiment le IO qui est le problème ici? Qu'advient-il si vous venez d'écrire à un
StreamWriter
enroulé autour d'uneMemoryStream
, par exemple? - Justifier à gauche le code pour plus de lisibilité. Qu'est-ce que MyClassBs?
- MyClassB (ainsi que MyClassA et MyClassC) sont typiques POCOs. MyClassA contient une liste de MyClassBs et une liste de MyClassCs. Ne fait que répondre à votre question?
- il est basé sur l'hypothèse qu'il y a des coûts rattachés à l'écriture d'une ligne qui est atténué par la rédaction de plus d'une ligne à la fois. Vous ne savez pas si c'est une précis hypothèse, mais j'ai eu l'idée de l'article, je me suis lié.
- Je n'ai pas essayé. Je peux donner un coup de feu, bien que.
- Comment savez-vous 35 secondes est "assez lent"? Test de l'OI de la première à obtenir certaines limites? avant de bricoler avec le code c#...
- Skeet - j'ai essayé d'utiliser un MemoryStream comme vous l'avez suggéré, et il a fallu environ la même quantité de temps, donc je pense que le problème doit être avec le GetCharArray() la méthode. Je vais regarder un peu plus. Merci.
- Ne demandez pas ce que MyClassA est. Demandé à ce que MyClassBs est. Liste? OverservableCollection? Énumérable? Peut-être en mesure d'éliminer la ToList() de l'appel.
- MyClassBs est un IEnumerable<MyClassB>.
- La baisse de la construction de toutes les lignes dans la mémoire, c'est que vous avez toutes les lignes dans la mémoire. E. g. j'ai écrit une exportation de données qui écrit les quelques milliards de lignes. Ce serait une perte pour le cache de l'impressionnante chaîne de caractères en mémoire.
- Je suis d'accord si vous êtes à la production d'un milliard de lignes qu'il serait inefficace, mais si vous faites un peu alors ce serait bien. Sans connaître les détails, il est difficile de donner la bonne solution.
Vous devez vous connecter pour publier un commentaire.
J'ai jeté un extrait de simple qui, je pense, est un peu plus propre; mais, encore une fois, je ne suis pas tout à fait sûr de ce que vous essayez d'accomplir. Aussi, je n'ai pas de vos classes disponibles, donc je ne peux pas vraiment faire toute sorte de tests.
Cet échantillon n'est fondamentalement la même chose, sauf qu'il utilise des méthodes génériques, et il effectue toutes les écrire en un seul endroit.
Voici le StringBuilder version:
Je soupçonne fortement que la majorité de votre temps n'est pas passé dans le I/O. Il n'y a aucun moyen qu'il doit prendre en 35 secondes pour écrire de 35 000 lignes, à moins que ces lignes sont vraiment long.
Très probablement, la majorité du temps est passé dans la
GetCharArray
méthode, tout ce qui n'.Quelques suggestions:
Si vous pensez vraiment que je/O est le problème, d'augmenter le flux de la taille de la mémoire tampon. Appelez le StreamWriter constructeur qui vous permet de spécifier une taille de mémoire tampon. Par exemple,
Qui va faire mieux que le défaut 4K taille de la mémoire tampon. La plus grande de 64 ko de la taille de la mémoire tampon n'est généralement pas utile, et peut réduire les performances.
Ne pas pré-tampon de lignes ou de compléter un
StringBuilder
. Que pourrait vous donner de petites augmentations de rendement, mais à un coût énorme dans la complexité. Le petit boost de performance n'est pas la peine de l'entretien cauchemar.Profiter de
foreach
. Vous avez ce code:Qui a pour créer une liste concrète de ce que
MyClassBs
collection est, et puis énumérer elle. Pourquoi ne pas simplement d'énumérer la chose directement:Qui vous permettront d'économiser la mémoire requise par l'
ToList
, et le temps qu'il faut pour énumérer la collection lors de la création de la liste.Tout ce que dit, il est presque certain que votre
GetCharArray
méthode est la chose qui prend tout le temps. Si vous voulez vraiment accélérer votre programme, regardez-là. En essayant d'optimiser l'écriture de laStreamWriter
est une perte de temps. Vous n'allez pas pour obtenir les performances de manière significative il.Utilisation tampon de flux pour l'écriture
par exemple, pour le tampon d'écriture à la Console d'utiliser
de même pour le tampon d'écriture à l'utilisation du fichier de