indexOf для строки, загруженной из файла — dotnet 5.0 вдвое быстрее, чем dotnetcore 3.1

#.net #performance #indexof

#.net #Производительность #индекс

Вопрос:

Я только что обнаружил, что мой сканер файлов работает очень быстро в dotnetcore 3.1, но мучительно медленно в dotnet 5. Он делает все немного по-другому, но dotTrace указывает мне на метод indexOf, который я использую.

Я свел к этому небольшой тест:

 var str = System.IO.File.ReadAllText("A text file of 125MB not containing hello in it");
start = DateTime.Now;
var result = str.IndexOf("hello");
var end = DateTime.Now;
Console.WriteLine("Done in "   end.Subtract(start).TotalSeconds   " seconds");
 

Время выполнения dotnet 3.1: 2 секунды

Время выполнения dotnet 5: 3,6 секунды

Не имеет значения, запускаюсь ли я в релизных или отладочных сборках.

В моем реальном случае разница намного хуже, но я думаю, что это все еще показывает проблему.

Важно отметить, что если я просто создаю строку того же размера в памяти, например new string(‘a’, size125mb), то она выполняется очень быстро в обоих средах выполнения.

dotTrace показывает, что в случае использования indexOf для строки в dotnet 5 трассировка стека становится такой:

Dotnet 5:

 System.String.IndexOf(String, Int32)
System.String.IndexOf(String, Int32, Int32, StringComparison)
System.Globalization.CompareInfo.IndexOf(String, String, Int32, Int32, IndexOf  •  4,327 msSystem.Globalization.CompareInfo.IndexOf(ReadOnlySpan, ReadOnlySpan, CompareOptions)
IcuIndexOfCore
 

Dotnet 3.1:

 System.String.IndexOf(String, Int32)
System.String.IndexOf(String, Int32, Int32, StringComparison)
System.Globalization.CompareInfo.IndexOf(String, String, Int32, Int32, CompareOptions)
System.Globalization.CompareInfo.IndexOfCore(String, String, Int32, Int32, CompareOptions, Int32*)
System.Globalization.CompareInfo.FindString(UInt32, String, Int32, Int32, String, Int32, Int32, Int32*)
 

Это фактически показывает разный код, выполняемый в двух случаях. В результате мой сканер, который просматривает весь загруженный текст, работает за 5 секунд и находит 12 тысяч записей в dotnet 3.1, но в dotnet 5 он сканирует только 2 строки каждую секунду.

Кто-нибудь может сказать, это просто потому, что я должен делать что-то по-другому в dotnet 5, или они допустили небольшую ошибку, из-за чего это было так медленно в dotnet 5?

ОБНОВЛЕНИЕ 1:

Вызов System.IO.File. Вызов System.IO.File.ReadAllText, похоже, также работает намного медленнее в dotnet 5 (это один и тот же файл, загруженный в двух случаях)

введите описание изображения здесь

ОБНОВЛЕНИЕ 2:

Я привел еще один пример, который показывает разницу в производительности в 10 раз:

 var stopwatch = new Stopwatch();
stopwatch.Start();
var str = System.IO.File.ReadAllText("125mb txt file");
Console.WriteLine($"File loaded in {stopwatch.Elapsed.TotalSeconds} seconds");
stopwatch.Restart();

int index = 0;
int foo = 0;
int counter = 0;
while(index < str.Length - 10)
{
    counter  ;
    foo = str.IndexOf("AW()=DAW=)DA=)WDUAOWIDJAOWID", index, 10);
    index  = 10;
    if (foo > 0)
       throw new Exception("Will never happen but dotnet does not know so it cannot remove the body as part of code optimization");
}

Console.WriteLine($"File iterated in {stopwatch.Elapsed.TotalSeconds} seconds, "   counter   " iterations");
 

Вывод с использованием dotnet 5:
введите описание изображения здесь

Вывод с использованием dotnet 3.1
введите описание изображения здесь

Комментарии:

1. docs.microsoft.com/en-us/dotnet/standard/…

2. Спасибо, Ганс. Это обсуждает, что код был исправлен так, чтобы он вел себя одинаково в разных средах. Но означает ли это, что мы все должны просто смириться с огромной потерей производительности? Или это может быть ошибка в dotnet 5?

3. Он документирует, как вернуть старое поведение, конечно, вы хотите попробовать это.

4. Ханс: Кажется, это исправляет разницу в производительности, спасибо! Знаете ли вы, доступен ли NLS при запуске приложения dotnet core в среде Linux? Мне нужно убедиться, что запуск кода на моем локальном компьютере с Windows будет идентичен его запуску в наших контейнерах Linux в производственной среде.

5. NLS доступен только в Windows. Связанная статья объясняет, почему это произошло, они хотели использовать одну и ту же библиотеку как в Unix, так и в Windows.