Точно измерить время выполнения кода в потоке (с#)

Я пытаюсь как можно точнее измерить время выполнения некоторых битов кода в нескольких потоках, принимая во внимание переключение контекста и время простоя потока. Приложение реализовано на языке 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 являются ли оба potenitally длинными функциями, поэтому очень вероятно, что произойдет хотя бы один контекстный переключатель во время их казни - возможно, не один. Таким образом, код получает lTimestamp1, затем запускается другой поток (и текущий поток ждет). В конце концов текущий поток возвращает управление и извлекает lTimestamp2.

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

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

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

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

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

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

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

любые советы были бы очень признательны - код сборки C++ или x86 также будет работать.

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

2 ответов


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

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

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


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

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

EDIT:

An интересная статья с бенчмарками:сколько времени требуется для переключения контекста?