Время выполнения блоков кода
Обнаружил некоторую странность связанную с производительностью кода. Ниже приведён код небольшой учебной программы. Он выполняет вывод текста в консоль, а так же фиксирует и выводит время выполнения вывода. Если запустить его, то будет видно, что на первый вывод в консоль, тратится очень много времени, относительно всех остальных выводов. Примерно 80000 тиков, хотя в остальных случаях вывод займёт около 200. И это вполне объяснимо, т.к. в этот момент видимо происходит инициализация класса Console. Но дальше я обнаружил некоторые странности.
Console.WriteLine(i) выводится дольше чем Console.WriteLine("a" + i), и это выглядит крайне нелогичным и не очевидным. Ведь во втором случае добавляется же ещё и конкатенация строк. Почему же вывести int в консоль оказывается дороже, чем вывести строку в которую сконкатенирован int?
Понятно, что время выполнения может сильно гулять, но я заметил странную закономерность. Первый вывод во втором цикле всегда занимает в 2-3 раза больше тиков, чем последующие. Сперва я предположил, что возможно в этот момент происходит инициализация конструкции for, но тогда бы наверное тот же эффект наблюдался бы и в третьем цикле? А его нет. Почему так?
static void Main(string[] args)
{
Stopwatch stopwatch = new();
stopwatch.Start();
Console.WriteLine("Test");
stopwatch.Stop();
Console.WriteLine(stopwatch.ElapsedTicks);
//Первый цикл
for(int i = 0; i < 2; i++)
{
stopwatch.Restart();
Console.WriteLine(i);
stopwatch.Stop();
Console.WriteLine(stopwatch.ElapsedTicks);
}
//Второй цикл
for(int i = 0; i < 5; i++)
{
stopwatch.Restart();
Console.WriteLine("Hello, World! " + $"{i}");
stopwatch.Stop();
Console.WriteLine(stopwatch.ElapsedTicks);
}
//Третий цикл
for (int i = 0; i < 5; i++)
{
stopwatch.Restart();
Console.WriteLine("Hello, People! " + $"{i}");
stopwatch.Stop();
Console.WriteLine(stopwatch.ElapsedTicks);
}
}
Ответы (2 шт):
Вот тут будет понятный результат:
// Framework 4.8
using System;
using System.Diagnostics;
static class Program {
static void Main(string[] args) {
long et; string s; int i, j, loops = 1000, sops = 1000;
Action<long> cwl = (eltcks) => Console.WriteLine("Average time {0}", eltcks / loops);
var stopwatch = new Stopwatch();
for (et = 0, i = 0; i < loops; i++) { //Первый цикл
stopwatch.Restart();
for (j = 0; j < sops; j++) s = i.ToString();
et += stopwatch.ElapsedTicks;
}
cwl(et);
for (et = 0, i = 0; i < loops; i++) { //Второй цикл
stopwatch.Restart();
for (j = 0; j < sops; j++) s = String.Format("Hello, World! {0}", i);
et += stopwatch.ElapsedTicks;
}
cwl(et);
for (et = 0, i = 0; i < loops; i++) { //Третий цикл
stopwatch.Restart();
for (j = 0; j < sops; j++) s = String.Format("Hello, People! {0}", i);
et += stopwatch.ElapsedTicks;
}
cwl(et);
for (et = 0, i = 0; i < loops; i++) { //Первый цикл
stopwatch.Restart();
for (j = 0; j < sops; j++) s = i.ToString();
et += stopwatch.ElapsedTicks;
}
cwl(et);
}
}
-->
Average time 315
Average time 997
Average time 1050
Average time 301
Эта проблема давно замечена, решается выводом в консоль строк, равных количеству буфера строк консоли.
Таким образом буфер консоли инициализируется полностью, и консоль становится стабильной.
Узнать количество строк буфера текущей консоли можно через класс System.Console.
Можете написать собственный класс консоли, от него наследоваться. Этот класс при инициализации будет сам чекать настройки консоли, и выводить нужное количество строк для первой полной инициализации буфера.
ПС: Заметьте - когда буфер полностью инициализирован, и вы шлёте в консоль что-то совсем простое для вывода - память консоли стоит колом, вообще не меняется.
Какой то код писать - бессмысленно, он слишком прост.
for (i=0; i<System.Console.ВзятьПолнуюВысотуБуфера; i++)
System.Console.WriteLine(ВывестиСтрокуДлинойВБуфер);