Как измерить время многопоточного процесса в многозадачной среде?

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

Обычно измеряются два вида времени:

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

Я считаю, что мне нужно время выполнения отдельных потоков, которое может отличаться от максимального времени ЦП, используемого любым потоком, из-за структуры зависимостей задач между потоками. Например, в процессе с 2 потоками поток 1 сильно загружен в первые две трети времени выполнения (для времени ЦП t), в то время как поток 2 загружен в последние две трети времени выполнения процесса (опять же, для времени процессора т). В этом случае:

  • время настенных часов вернет 3t/2 + время переключения контекста + время, используемое другими процессами между ними,
  • максимальное время процессора всех потоков вернет значение, близкое к t, и
  • общее время процессора близко к 2т.
  • То, что я надеюсь получить в качестве результата измерения, - это рабочий диапазон, т. Е. 3t/2.

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

Более того, задержка также зависит от того, как ОС планирует потоки; все усложняется, если некоторые потоки процесса ожидают ЦП, а другие работают. Но давайте забудем об этом.

Есть ли эффективный способ вычислить / приблизить это время выполнения? Для предоставления примеров кода, пожалуйста, используйте любой язык программирования, но предпочтительно C или C++ на Linux.

PS: я понимаю, что это определение makepan отличается от того, что используется при планировании задач. Определение, используемое в задачах планирования, похоже на время настенных часов.

1 ответ

Переформулировка вопроса

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

Как оценить, сколько времени займет выполнение программы на одноядерном компьютере?

Эмпирически

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

... Ой, подождите, у вашего компьютера уже есть одно ядро ​​(у него также есть некоторые другие, но нам не нужно их использовать).

Как это сделать, будет зависеть от операционной системы, но один из первых результатов, которые я нашел от Google, объясняет несколько подходов для Windows XP и Vista.

http://masolution.blogspot.com/2008/01/how-to-use-only-one-core-of-multi-core.html

После этого вы можете:

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

Независимый параллелизм

Аналитическая оценка этого требует знания вашей программы, метода параллелизма и т. Д.

В качестве простого примера, предположим, я пишу многопоточную программу, которая вычисляет десятимиллиардную десятичную цифру числа пи и десятимиллиардную десятичную цифру числа е.

Мой код выглядит так:

public static int main()
{
    Task t1 = new Task( calculatePiDigit );
    Task t2 = new Task( calculateEDigit );
    t1.Start();
    t2.Start();
    Task.waitall( t1, t2 );
}

И график "происходит до" выглядит так:

Очевидно, что они независимы.

В этом случае

  • Время рассчитывает PiDigit() само по себе.
  • Время рассчитывает EDigit() само по себе.
  • Добавьте время вместе.

2-этапный трубопровод

Когда задачи не являются независимыми, вы не сможете просто добавить отдельные моменты времени вместе.

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

Мой код выглядит примерно так:

ConcurrentQueue<Image> originalImages = new ConcurrentQueue<Image>();
ConcurrentQueue<Image> grayscaledImages = new ConcurrentQueue<Image>();
ConcurrentQueue<Image> completedImages = new ConcurrentQueue<Image>();

public static int main()
{
     PipeLineStage p1 = new PipeLineStage(originalImages, grayScale, grayscaledImages);
     PipeLineStage p2 = new PipeLineStage(grayscaledImages, lineDetect, completedImages);

     p1.Start();
     p2.Start();

     originalImages.add( image1 );
     originalImages.add( image2 );
     //... 
     originalImages.add( image10 );

     originalImages.add( CancellationToken );

     Task.WaitAll( p1, p2 );
}

График, ориентированный на данные, происходит до:

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

В любом случае, мы знаем, что GrayScale() будет вызываться 10 раз, а LineDetection() будет вызываться 10 раз, поэтому мы можем просто рассчитать время независимо друг от друга, а затем умножить их на 10.

Но как насчет затрат на отправку / прослушивание / опрос ConcurrentQueues?

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

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

Затраты на переключение контекста?

Посмотрите на этот вопрос:

Как оценить издержки переключения контекста потока?

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

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

Опасность заключается в том, что кеш полностью разрушается между переключениями контекста.

Например, в идеале:

  • image1 загружается в кеш в результате выполнения GrayScale
  • LineDetection будет работать намного быстрее на image1, так как он находится в кеше

Однако это может произойти:

  • image1 загружается в кеш в результате выполнения GrayScale
  • image2 загружается в кеш в результате выполнения GrayScale
  • теперь этап 2 конвейера запускает LineDetection для image1, но image1 больше не находится в кэше.

Заключение

Ничто не сравнится по времени в той же среде, в которой оно будет запущено.

Далее лучше всего смоделировать эту среду, насколько это возможно.

Независимо от этого, понимание дизайна вашей программы должно дать вам представление о том, чего ожидать в новой среде.

Другие вопросы по тегам