понедельник, 17 мая 2010 г.

Отслеживание прогресса исполнения задачи в реальном времени

Любая сколь-нибудь долгосрочная задача зачастую требует периодического опроса её состояния. Однако, не существует универсального механизма, который бы позволял с минимальными потерями в производительности делать такие замеры, т.к. все задачи разные.

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

Тем не менее, такой механизм, с некоторыми ограничениями, реализовать не так сложно как кажется.

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

Возьмем, для примера самую элементарную задачу "счетчик":
for( Counter = 0, i = 0 ; i < Size ; ++i ){
 ++Counter;
}
Здесь четко выделена итерация:
++Counter;
А теперь, представим, что значение этого счетчика, в ходе его наполнения, необходимо периодически "запирать" во внешней переменной, которую так же периодически тестирует другой процесс/поток. Этот процесс/поток и будет отслеживать прогресс исполнения задачи.

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

Во первых, задачу можно реализовать так:
for( Counter = 0, i = 0 ; i < Size ; ++i ){
 ++Counter;
 Latch = Counter;
}
где Latch - условная внешняя переменная, которая и является нашим синхронизатором с процессом/потоком отслеживания. В данном случае, сопутствующий код, раскрывающий эксклюзивный доступ к этой переменной опущен, т.к. его реализация не является существенной.

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

Причем, можно отметить тот факт, что операции отслеживания работают заведомо на более низких частотах нежели сама задача, следовательно, можно использовать это время непосредственно на решение самой задачи.

Возьмем к примеру каждую сотую итерацию для синхронизации:
for( Counter = 0, i = 0 ; i < Size ; ++i ){
 ++Counter;
 if( !( i % 99 ) ){
  Latch = Counter;
 }
}
if( i % 99 ){
 Latch = Counter;
}
Итак, подобрав на любую такую задачу соответствующий коэффициент, можно добиться вполне преемлемых результатов: и волки сыты и овцы целы.

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

Та самая, дискретная сетка, которая настраивается этим коэффициентом, скорее всего будет расстроена, т.е. частота обновления переменной Latch будет изменена, что естественно скажется на работе процессов отслеживания, что в каком-то конечном счете может сказаться на точности планирования, представления промежуточных результатов.

чтобы этого избежать, необходимо привязать этот коэффициент к аппаратуре, на которой задача исполняется. Как это сделать конечно же известно: любое вычислительное устройство работает на своей частоте, и в этом плане, привязав коэффициент, например к Intel'овому RDTSC, он перестанет быть зависимым.

Для примера, будем считать, что Intel - венец творений, т.к. нас не интересует аппаратная переносимость реализации такой привязки. Будем просто иметь ввиду, что для других платформ будут и другие команды, принцип работы которых идентичен.

Но как реализовать привязку коэффициента?

Перед выполнением задачи вычислим число циклов, которые будут пройдены за некоторое время, например за 1 секунду:
#include <intrin.h>
...
Temp = __rdtsc();
::Sleep( 1000 );
MSSize = __rdtsc() - Temp;
Это известная формула, аналогичных на нее вариаций в Интернет достаточно. После этого, MSSize / 1000 составляет искомое число циклов. Теперь, при расчете дискретной сетки, будем опираться именно на это значение:
dt = msIteration * MSSize / 1000;
for( Counter = 0, i = 0 ; i < Size ; ){
 for( start = __rdtsc() ; ( ( __rdtsc() - start ) <  dt ) && ( i < Size ); ++i ){
  ++Counter;
 }
 Latch = Counter; 
}
где msIteration - число миллисекунд, через которые отслеживающий процесс будет синхронизироваться с расчетной задачей.

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

Таким образом, задача отслеживания прогресса исполнения задачи в реальном времени может быть выполнена в достаточно простой форме

Комментариев нет:

Отправить комментарий