Соответствие звонков начала / конца профилирования - PullRequest
1 голос
/ 06 июля 2011

В настоящее время я внедряю систему профилирования в приложение.

У меня есть две макро-функции, которые определены на основе флага компилятора (NDEBUG). Когда NDEBUG не определен, эти две функции (profilingStart / profilingEnd) генерируют отчеты о профилировании, которые показывают время, когда был вызван profilingStart, по сравнению со временем, когда был вызван profilingEnd.

Проблема заключается в возможности возникновения несоответствия, т. Е. Сценария, когда profilingStart был вызван, а profilingEnd - нет (или наоборот). Мой код уже распознает эти ситуации во время выполнения, но было бы предпочтительно, если бы во время компиляции возникла ошибка из-за этого несоответствия.

Одним из предложений было использование do {...} while (); построить, чтобы гарантировать, что функции профилирования правильно спарены. Функция начального макроса будет содержать do {, а конечный макрос будет содержать} while (). Если один из них отсутствует, мы получили бы ошибку во время компиляции. Однако с этим есть некоторые проблемы - вы можете использовать вызовы profilingStart () и profilingEnd () только в начале и в конце профилируемой функции, так как их использование внутри функции может повлиять на область действия локальных переменных ( так как они могут выйти из области видимости из-за вызова do {...} while ()).

Другая идея, которая у меня возникла, - просто объявить переменную в функции profilingStart, а затем попытаться изменить содержимое этой переменной в функции profilingEnd. Это предотвращает проблемы с областью действия и приведет к ошибке компилятора, если переменная не была объявлена. Однако у меня никогда не было бы способа проверить, что содержимое переменной изменяется в конечной функции. Это помогает только в половине проблемы, поскольку не проверяет вызов функции profilingEnd.

Любые комментарии приветствуются, как всегда. Заранее спасибо.

РЕДАКТИРОВАТЬ: Может быть некоторая путаница в отношении моего комментария (ов) относительно области действия. profilingStart () и profilingEnd () всегда будут вызываться в одной и той же функции. Они могут просто не вызываться в самом начале / самом конце функции. Вот пример того, что я имел в виду:

int DoSomething(void)
{
   profilingStart();
   int a;
   DoMath(a);
   profilingStop();
   return a; // a is out of scope here, as the do{...}while(0) construct has gone out of scope
}

Ответы [ 3 ]

3 голосов
/ 06 июля 2011

В C ++ одним из решений является использование идиомы RAII. Примерно так:

class Profiler {
  public:
    Profiler() { profilingStart(); }
    ~Profiler() { profilingEnd(); }
}

Тогда вы используете это так:

{ // start of block you want to profile
    Profiler prof;
    ...
}

Это гарантирует, что profilingEnd будет вызываться даже при наличии исключений, ранних возвратов, break и т. Д. То есть это абсолютно гарантирует, что вызовы сопряжены.

Для этого требуется поместить код, который вы хотите профилировать в блоке.

[править]

Я упустил, что вы хотите иметь возможность поместить profilingEnd в другой блок, чем profilingStart.

См. Комментарий @ Родди ниже о том, как с этим бороться; например с помощью деструктора, чтобы убедиться, что профилировщик был остановлен к моменту уничтожения объекта. Хотя это не поймет проблему во время компиляции, оно поймает ее «рядом» с проблемой во время выполнения.

0 голосов
/ 07 июля 2011

На ваш вопрос в качестве вопроса я рекомендую ответ @ Nemo. Используйте способность C ++ вызывать деструкторы и придерживайтесь основной лексической области видимости.

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

Вот еще немного о проблемах.

0 голосов
/ 06 июля 2011

Почему бы просто не создать объект, который запускает событие профиля в конструкторе и заканчивает его в деструкторе, а затем, используя класс, подобный scoped_lock, вы можете убедиться, что начало всегда сопряжено.И, видя, как вы можете создавать произвольные области, вы можете делать это где угодно

...