#.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");
Комментарии:
1. docs.microsoft.com/en-us/dotnet/standard/…
2. Спасибо, Ганс. Это обсуждает, что код был исправлен так, чтобы он вел себя одинаково в разных средах. Но означает ли это, что мы все должны просто смириться с огромной потерей производительности? Или это может быть ошибка в dotnet 5?
3. Он документирует, как вернуть старое поведение, конечно, вы хотите попробовать это.
4. Ханс: Кажется, это исправляет разницу в производительности, спасибо! Знаете ли вы, доступен ли NLS при запуске приложения dotnet core в среде Linux? Мне нужно убедиться, что запуск кода на моем локальном компьютере с Windows будет идентичен его запуску в наших контейнерах Linux в производственной среде.
5. NLS доступен только в Windows. Связанная статья объясняет, почему это произошло, они хотели использовать одну и ту же библиотеку как в Unix, так и в Windows.