ЦПУ недоиспользуется. Из-за блокировки ввода-вывода?

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

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

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

В конце обработки сообщения я использую метод отправки MessageQueue (как-то асинхронный, но не очень, потому что он должен ждать на диске записи перед возвратом -see System.Messaging - почему MessageQueue не предлагает асинхронную версию Отправить).

Для тестов я размещаю 100 тыс. сообщений в очереди (около 100 МБ общего объема для сообщений 100 КБ), а затем запускаю программу. На двух моих персональных компьютерах (SSD HD на одном и на SATA2 HD с другой с iQ CPU quadcores -8 логической proc) я достигаю ~ 95% использования ЦП на протяжении всего жизненного цикла программы (удаление сообщений 100 тыс., Обработка их и отправка ответов). Сообщения удаляются как можно быстрее, обрабатываются как можно быстрее (здесь задействован процессор), а затем ответ для каждого сообщения, отправленного в другую локальную очередь.

Теперь на виртуальной машине, работающей с двухъядерным процессором без HT (не знаю, что является основным диском, но кажется гораздо менее эффективным, чем мины... во время теста, с Perfmon я могу видеть avg disk sec/write arround 10-15 ms на этой виртуальной машине, тогда как на моих персональных машинах arms 2ms), когда я работаю на той же скамейке, я достигаю ~ 55% CPU (когда я запускаю одну и ту же скамью на машине, не отправляя ответные сообщения в очередь, я достигаю ~ 90% CPU).

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

Я действительно пытаюсь понять, почему я не достигаю, по крайней мере, 95% -ного использования процессора на этой машине. Я слепо говорю, что это связано с более низкой производительностью диска, но все же я не понимаю, почему это приведет к недоиспользованию процессора, учитывая, что я запускаю обработку одновременно с помощью Task.Run. Это также может быть некоторая системная проблема, полностью не связанная с диском, но учитывая, что MessageQueue.Send, похоже, является проблемой и что этот метод в конечном итоге записывает сообщения в файл с отображением памяти + диск, я не вижу, откуда может возникнуть проблема производительности кроме диска.

Конечно, проблема в производительности системы по мере того, как программа максимизирует использование ЦП на моих собственных компьютерах, но мне нужно найти, что узкое место находится именно в системе VM, и почему именно это влияет на concurrency/скорость моего приложения.

Любая идея?

Ответ 1

Для просмотра плохого использования диска и/или процессора используется только один инструмент: Инструментарий производительности Windows. Для примера, как его использовать, см. здесь. Вы должны получить последнюю версию из SDK Windows 8.1 (требуется .NET 4.5.1), которая дает вам большинство возможностей, но тот, что из SDK Windows 8 тоже прекрасен.

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

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

Больше никаких угадок. Вы действительно можете видеть, что делает система.

Чтобы проанализировать дальнейшее включение в CPU Usage Precise, просмотрите следующие столбцы:

  • NewProcess
  • NewThreadId
  • NewThreadStack (фрейм-теги)
  • ReadyingProcess
  • ReadyingThreadId
  • Готов (нас) Сумма
  • Подождите (мы) Сумма
  • Wait (нас)
  • % Использование ЦП

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

Ответ 2

Если счетчики производительности ввода-вывода диска не выглядят ненормально высокими, я бы посмотрел дальше на уровне гипервизора. Предполагая, что вы используете тот же самый код, использование VM добавляет латентность для всего стека (CPU, RAM, Disk). Возможно, вы можете настроить Планирование ЦП на уровне гипервизора и посмотреть, увеличит ли это использование ЦП.

Я бы также подумал об использовании RAMDisk для тестирования производительности. Это позволит устранить задержку Disk/SAN, и вы можете увидеть, устраняет ли это вашу проблему.