Точно измерить время выполнения кода в потоке (C #) - PullRequest
11 голосов
/ 29 сентября 2011

Я пытаюсь максимально точно измерить время выполнения некоторых битов кода на нескольких потоках, учитывая переключение контекста и время простоя потоков. Приложение реализовано на C # (VS 2008). Пример:

public void ThreadFunc ()
{
    // Some code here

    // Critical block #1 begins here
    long lTimestamp1 = Stopwatch.GetTimestamp ();

    CallComplex3rdPartyFunc (); // A

    long lTimestamp2 = Stopwatch.GetTimestamp ();
    // Critical block #1 ends here

    // Some code here

    // Critical block #2 begins here
    long lTimestamp3 = Stopwatch.GetTimestamp ();

    CallOtherComplex3rdPartyFunc (); // B

    long lTimestamp4 = Stopwatch.GetTimestamp ();
    // Critical block #2 ends here

    // Save timestamps for future analysis.
}

public int Main ( string[] sArgs )
{
    // Some code here

    int nCount = SomeFunc ();

    for ( int i = 0; i < nCount; i++ )
    {
        Thread oThread = new Thread ( ThreadFunc );
        oThread.Start ();
    }

    // Some code here

    return ( 0 );
}

Я бы хотел измерить время выполнения двух вышеупомянутых критических блоков кода как можно точнее. Два вызова, помеченные как A и B , представляют собой потенциально длинные вызовы функций, выполнение которых может занять несколько секунд, но в некоторых случаях они могут завершиться за несколько миллисекунд.

Я запускаю приведенный выше код на нескольких потоках - где-то от 1 до 200 потоков, в зависимости от ввода пользователя. Компьютеры с этим кодом имеют 2-16 ядер - пользователи используют меньшее количество потоков на более слабых машинах.

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

Это означает, что длительность между lTimestamp1 и lTimestamp2 включает время, когда поток фактически не выполнялся - он ожидал повторного планирования, пока выполнялись другие потоки. Однако количество тиков в любом случае увеличивается, поэтому продолжительность теперь действительно

Время блока кода = A + B + некоторое время, проведенное в других потоках

пока я хочу, чтобы оно было только

Время блока кода = A + B

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

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

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

Любой совет будет принят с благодарностью - ассемблерный код C ++ или x86 также будет работать.

Редактировать: кажется невозможным реализовать это. Идея Скотта ниже (с использованием GetThreadTimes) хороша, но, к сожалению, GetThreadTimes () является ошибочным API и почти никогда не возвращает правильные данные. Спасибо за все ответы!

Ответы [ 2 ]

10 голосов
/ 29 сентября 2011

Это можно сделать с помощью вызова API Native GetThreadTimes .Вот статья о CodeProject , которая его использует.

Второй вариант - использование QueryThreadCycleTime .Это не даст вам времени, но даст количество циклов, выполняемых текущим потоком.

Имейте в виду, что вы не можете просто напрямую преобразовать cycles->seconds из-за того, что многие процессоры (особенно мобильные) не работают с фиксированной скоростью, поэтому нет постоянного числа, на которое вы могли бы умножиться, чтобы получитьпрошедшее время в секундах.Но если вы используете процессор, который не меняет свою скорость, тогда будет просто математическая задача получить время настенных часов из циклов.

2 голосов
/ 29 сентября 2011

Вы можете использовать Stopwatch.Start () и Stopwatch.Stop () для приостановки / продолжения измерения времени, оно не сбрасывается Elapsed / *Значение 1007 * ElapsedMilliseconds , поэтому, возможно, вы сможете использовать это.

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

РЕДАКТИРОВАТЬ:

Интересная статья с тестами: Сколько времени занимает переключение контекста?

...