Это просто личный проект, в который я копался. По сути, я анализирую текстовый файл (скажем, от 20 МБ до 1 ГБ) с помощью StreamReader. Производительность довольно хорошая, но все же ... Мне не терпелось увидеть, что произойдет, если я проанализирую его в двоичном формате. Не поймите меня неправильно, я не оптимизирую преждевременно. Я определенно микрооптимизируюсь нарочно, просто чтобы «увидеть».
Итак, я читаю в текстовом файле, используя байтовые массивы. Приходите, чтобы узнать, что новые строки могут быть (Windows) стандартными CR / LF или CR или LF ... довольно грязно. Я надеялся, что смогу использовать Array.IndexOf на CR, а затем пропустить LF. Вместо этого я пишу код, очень похожий на IndexOf, но проверяю любой из них и возвращаю массив по мере необходимости.
Итак, суть: используя код, очень похожий на IndexOf, мой код все равно оказывается безумно медленным. Чтобы поместить это в перспективу, используя файл 800 МБ:
- Использование IndexOf и поиск CR: ~ 320 МБ / с
- Использование StreamReader и ReadLine: ~ 180 МБ / с
- для репликации цикла IndexOf: ~ 150 МБ / с
вот код с циклом for (~ 150 Мбит / с):
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);
}
}
}
}
это более быстрый кодовый блок (~ 320 МБ / с):
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);
}
}
(Нет, он не готов к работе, в некоторых случаях он будет взорван; я использую буфер размером 128 КБ, чтобы игнорировать большинство из них.)
Итак, мой большой вопрос ... почему Array.IndexOf работает намного быстрее? Это по сути то же самое, для цикла обход массива. Есть ли что-то в способе выполнения кода mscorlib? Даже изменение приведенного выше кода для реальной репликации IndexOf и поиск только CR, а затем пропуск LF, как если бы я использовал IndexOf, не помогает. Э-э-э ... Я проходил различные перестановки, и уже достаточно поздно, чтобы, возможно, я обнаружил какую-то явную ошибку?
Кстати, я заглянул в ReadLine и заметил, что он использует блок переключателей, а не блок if ... когда я делаю что-то подобное, как ни странно, это увеличивает производительность примерно на 15 Мбит / с. Это еще один вопрос для другого времени (почему переключение происходит быстрее, чем если бы?), Но я решил, что укажу, что я действительно посмотрел на него.
Кроме того, я тестирую сборку релиза вне VS, поэтому отладки не происходит.