C #, для циклов и теста скорости ... Точно такой же цикл быстрее второй раз? - PullRequest
4 голосов
/ 19 ноября 2008
public Int64 ReturnDifferenceA()
{
  User[] arrayList;
  Int64 firstTicks;
  IList<User> userList;
  Int64 secondTicks;
  System.Diagnostics.Stopwatch watch;

  userList = Enumerable
              .Range(0, 1000)
              .Select(currentItem => new User()).ToList();

  arrayList = userList.ToArray();

  watch = new Stopwatch();
  watch.Start();

  for (Int32 loopCounter = 0; loopCounter < arrayList.Count(); loopCounter++)
  {
     DoThings(arrayList[loopCounter]);
  }

  watch.Stop();
  firstTicks = watch.ElapsedTicks;

  watch.Reset();
  watch.Start();
  for (Int32 loopCounter = 0; loopCounter < arrayList.Count(); loopCounter++)
  {
     DoThings(arrayList[loopCounter]);
  }
  watch.Stop();
  secondTicks = watch.ElapsedTicks;

  return firstTicks - secondTicks;
}

Как видите, это действительно просто. Создайте список пользователей, принудительно создайте массив, запустите наблюдение, переберите список и вызовите метод, остановите наблюдение. Повторение. Завершите, вернув разницу от первого запуска и второго.

Теперь я звоню с этими:

differenceList = Enumerable
                 .Range(0, 50)
                 .Select(currentItem => ReturnDifferenceA()).ToList();
average = differenceList.Average();

differenceListA = Enumerable
                  .Range(0, 50)
                  .Select(currentItem => ReturnDifferenceA()).ToList();
averageA = differenceListA.Average();

differenceListB = Enumerable
                  .Range(0, 50)
                  .Select(currentItem => ReturnDifferenceA()).ToList();
averageB = differenceListB.Average();

Теперь самое интересное то, что все средние значения положительны на относительно большое количество, в пределах от 150 000 до 300 000 тиков.

Чего я не понимаю, так это того, что я иду по тому же списку, тем же способом, тем же методом, и все же есть такая разница. Есть ли какое-то кэширование?

Другая интересная вещь заключается в том, что если я перебираю список ДО первой секции секундомера, средние значения будут около 5 000 или около того.

Ответы [ 7 ]

5 голосов
/ 20 ноября 2008

Вы работаете на языке высокого уровня со средой выполнения, в которой выполняется много операций кэширования и оптимизации производительности, это часто встречается. Иногда это называется прогревом виртуальной машины или прогревом сервера (когда это производственное приложение).

Если что-то будет предприниматься неоднократно, вы будете часто замечать, что в первый раз измеренное время выполнения будет больше, а остальное должно выровняться до меньшего значения.

Я делаю это в коде MATLAB и вижу, что при первом запуске цикла бенчмарка это занимает пять секунд, а последующие - пятую секунды. Это огромная разница, потому что это интерпретируемый язык, который требует определенной формы компиляции, но на самом деле это не влияет на вашу производительность, потому что подавляющее большинство будет «второй раз» в любом производственном приложении.

4 голосов
/ 20 ноября 2008

кстати, использование IEnumerable.Count () в массиве в сотни раз медленнее, чем в массиве Array.Length ... Хотя это совсем не отвечает на вопрос.

3 голосов
/ 20 ноября 2008

Вполне возможно, что DoThings () не JIT-компилируется в собственный код, пока он не будет вызван в первый раз.

1 голос
/ 07 августа 2009

Потому что .NET, как и платформа Java, является средой JIT. Весь высокоуровневый .NET-код компилируется в байт-код промежуточного языка Microsoft.

Чтобы запустить вашу программу, этот байт-код необходимо скомпилировать / перевести на собственный машинный код. Однако скомпилированные запрограммированные файлы .NET хранятся не в собственном машинном коде, а в байт-коде промежуточной виртуальной машины.

Первый запуск - это JIT-компилирование, поэтому потребовалось дополнительное время. Последующие запуски больше не нуждаются в JIT-компиляции, но собственный код извлекается из JIT-кеша, поэтому он должен быть быстрее.

Поддерживали ли вы свое приложение, не прерывая его при последующих запусках? Тогда вторая причина также связана с ВМ. (ВМ: 1 = виртуальная машина; ВМ: 2 = виртуальная память). Все современные обобщенные операционные системы запускают свои процессы в виртуальной памяти, которая представляет собой карту реальной памяти, что позволяет операционной системе управлять и оптимизировать использование системных ресурсов. Менее используемые процессы часто удаляются в дисковый кэш, чтобы другие процессы могли оптимально использовать ресурсы.

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

Кроме того, ОС распределяет ресурсы по мере необходимости. Таким образом, в первом раунде вашему процессу пришлось пройти через все усилия, направленные на то, чтобы раздвинуть конверт с ОС, чтобы расширить границы своих ресурсов.

Виртуальная машина позволяет .NET и Java преобразовывать большинство функций программирования в уровень, независимый от машины, разделяя и, следовательно, оставляя меньшую путаницу, с которой приходится сталкиваться машинно-зависимым программистам. Даже несмотря на то, что Microsoft Windows работает на довольно унифицированном оборудовании-потомке x86, в разных версиях ОС и моделях процессоров есть достаточные различия, чтобы гарантировать выделенную виртуальную машину, чтобы предоставить программистам и пользователям .NET согласованное представление.

0 голосов
/ 07 августа 2009

Оставьте на минутку вопрос о разогреве виртуальной машины или машины, кэшировании, JIT-оптимизации: что еще делает ваш компьютер? Какие-нибудь системные сервисы 3e42 и вещи из панели задач захватывают какой-то процессор? Может быть, ваш клиент Steam решил проверить наличие обновлений, или IE нужно было сделать что-то ужасно важное, или ваша антивирусная программа помешала?

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

Но тогда что я знаю? - возможно, ваш метод измерения также управляется средой выполнения .net (или какой-либо другой) и учитывает только «виртуальные циклы» времени выполнения.

0 голосов
/ 20 ноября 2008

Я подозреваю, что функция, которую вы вызываете, не работает точно по времени до первого запуска. Что вы можете попробовать, так это запустить его один раз, затем остановить и запустить снова. Без изменений кода компиляция Just-In-Time из предыдущего запуска все равно должна быть в порядке, а любые оставшиеся оптимизации, которые вы видите, - это фактические эффекты кэширования в работе.

0 голосов
/ 20 ноября 2008

Вы говорите, что не можете сделать это 3 раза, 2 и 3 раза относительно близки. Мне кажется, что это только первый раз, когда все идет медленно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...