Я пытаюсь как можно точнее измерить время выполнения некоторых фрагментов кода в нескольких потоках, принимая во внимание переключение контекста и время простоя потока. Приложение реализовано на 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
Это особенно проблема с большим количеством потоков, так как все они получат шанс запуститься, поэтому указанные выше тайминги будут выше, в то время как все остальные потоки запустятся до того, как рассматриваемый поток получит еще один шанс запуститься.
Итак, мой вопрос: можно ли как-то рассчитать время, когда поток не работает, а затем соответствующим образом настроить указанные выше тайминги? Я хотел бы полностью исключить (вычесть) этот третий член или, по крайней мере, как можно больше. Код выполняется миллионы раз, поэтому окончательные тайминги рассчитываются на основе множества выборок, а затем усредняются.
Я не ищу продукты для профилирования и т. д. - приложение должно как можно точнее синхронизировать эти отмеченные части. Функции A и B являются сторонними функциями, я не могу их каким-либо образом изменить. Я также знаю о возможных колебаниях при измерении времени с точностью до наносекунды и возможных накладных расходах внутри этих сторонних функций, но мне все еще нужно выполнить это измерение.
Будем очень признательны за любые советы - код сборки C++ или x86 также будет работать.
Редактировать: кажется, что это невозможно реализовать. Представленная ниже идея Скотта (с использованием GetThreadTimes) хороша, но, к сожалению, GetThreadTimes() — это несовершенный API, и он почти никогда не возвращает правильные данные. Спасибо за все отклики!