Ceci est juste un projet personnel que j'ai été creuser dans. Fondamentalement, j'analyse un fichier texte (disons de 20 Mo jusqu'à environ 1 Go) en utilisant StreamReader. La performance est assez solide, mais quand même ... J'ai été impatient de voir ce qui se passerait si je l'analysais en binaire. Ne vous méprenez pas, je n'optimise pas prématurément. Je suis définitivement optimisant micro juste pour "voir". Donc, je lis dans le fichier texte en utilisant des tableaux d'octets. Venez découvrir, les nouvelles lignes peuvent être le (Windows) standard CR/LF ou CR ou LF ... assez salissant. J'avais espéré être capable d'utiliser Array.IndexOf sur CR, puis passer le LF. Au lieu de cela je me trouve en train d'écrire du code très similaire à IndexOf mais en vérifiant pour et renvoyer un tableau si nécessaire. Donc le crux: en utilisant un code très similaire à IndexOf, mon code finit toujours par être incroyablement plus lent. Pour le mettre en perspective à l'aide d'un fichier 800MB:Pourquoi le code System/mscorlib est-il tellement plus rapide? Surtout pour les boucles?
- aide IndexOf et recherche CR: ~ 320mb/s
- En utilisant StreamReader et ReadLine: ~ 180 Mo/s
- boucle répliquant IndexOf: ~ de 150mb/s
est ici le code avec la boucle for (~ 150mb/s):
IEnumerator<byte[]> IEnumerable<byte[]>.GetEnumerator() {
using(FileStream fs = new FileStream(_path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, _bufferSize)) {
byte[] buffer = new byte[_bufferSize];
int bytesRead;
int overflowCount = 0;
while((bytesRead = fs.Read(buffer, overflowCount, buffer.Length - overflowCount)) > 0) {
int bufferLength = bytesRead + overflowCount;
int lastPos = 0;
for(int i = 0; i < bufferLength; i++) {
if(buffer[i] == 13 || buffer[i] == 10) {
int length = i - lastPos;
if(length > 0) {
byte[] line = new byte[length];
Array.Copy(buffer, lastPos, line, 0, length);
yield return line;
}
lastPos = i + 1;
}
}
if(lastPos > 0) {
overflowCount = bufferLength - lastPos;
Array.Copy(buffer, lastPos, buffer, 0, overflowCount);
}
}
}
}
c'est le bloc de code plus rapide (~ 320m b/s):
while((bytesRead = fs.Read(buffer, overflowCount, buffer.Length - overflowCount)) > 0) {
int bufferLength = bytesRead + overflowCount;
int pos = 0;
int lastPos = 0;
while(pos < bufferLength && (pos = Array.IndexOf<byte>(buffer, 13, pos)) != -1) {
int length = pos - lastPos;
if(length > 0) {
byte[] line = new byte[length];
Array.Copy(buffer, lastPos, line, 0, length);
yield return line;
}
if(pos < bufferLength - 1 && buffer[pos + 1] == 10)
pos++;
lastPos = ++pos;
}
if(lastPos > 0) {
overflowCount = bufferLength - lastPos;
Array.Copy(buffer, lastPos, buffer, 0, overflowCount);
}
}
(Non, ce n'est pas prêt pour la production, certains cas vont le faire exploser; J'utilise un tampon de taille 128kb pour ignorer la plupart de ceux-ci.)
Donc ma grande question est ... pourquoi Array.IndexOf fonctionne-t-il beaucoup plus vite? C'est essentiellement la même chose, une boucle pour un tableau. Y a-t-il quelque chose à propos de la façon dont le code mscorlib est exécuté? Même en changeant le code ci-dessus pour vraiment répliquer IndexOf et en cherchant juste CR, puis en sautant LF comme je le ferais si j'utilise IndexOf n'aide pas. Euh ... j'ai traversé plusieurs permutations et il est assez tard pour qu'il y ait peut-être un bug qui me manque?
BTW, j'ai regardé dans ReadLine et a remarqué qu'il utilise un bloc de commutation plutôt qu'un bloc if ... quand je fais quelque chose de similaire, bizarrement, il augmente les performances d'environ 15mb/s. C'est une autre question pour une autre fois (pourquoi est-ce que le changement est plus rapide que si?) Mais je me suis dit que je ferais remarquer que je l'ai regardé.
En outre, je teste une version de publication en dehors de VS afin qu'il n'y ait pas de débogage.
Hmm, intéressant. En utilisant Reflector pour regarder la variante de mscorlib, je ne vois pas de supercherie qu'il pourrait utiliser. – Joey
Il a fini par utiliser la ruse. IEqualityComparer .Default a fini par utiliser ByteEqualityComparer. Je ne l'ai pas remarqué d'abord car il y avait une implémentation par défaut pour IndexOf. Vérifiez la propriété par défaut et il fait un jeu de shell et échange dans le cas spécial pour byte. –