Показать сообщение отдельно

Стабильность микропаузы
  #7  
Старый 17.11.2009, 18:23
slesh
Познавший АНТИЧАТ
Регистрация: 05.03.2007
Сообщений: 1,985
Провел на форуме:
3288241

Репутация: 3349


По умолчанию Стабильность микропаузы

Небольшое дополнение статейки от меня )

Цель:
Проверить стабильность паузы на интервалах сравнимых с микросекундами. Да и вообще возможность задания стабильных пауз данной длительности

Инструменты:
1) Delphi
2) 7z / WinRAR + фильм на 2 гига.
3) Процессор Intel Dual Core E5200 ~ 2.5 Ггц
4) Win XP SP 3
Остальное не играет значительной роли.

Метод проверки(Теоретическая часть):
Как всем известно, реальной многопоточного выполнения кода в процах (Intel/ADM) практически нет (исключение - многоядерные и/или многопоточные (Hyper Threading)) GPU учитывать не будем, потому как они не стоят как основа системы.
В виду вышесказанного для реализации многопоточности приходится использовать прерывание выполнения одного потока и передачу управления другому. Этим самым достигается эффект параллельного выполнения кода. т.е. пауза настолько мала, что этого практически не видно при обычном использовании компьютера. По этому было введено такое понятие как - процессорное время выделяемое потоку. Когда оно заканчивается, то управление получает другой поток системы и так пока все потоки не получат управление.
Так что теоретически пауза которая будет во время пока поток не получит сново управление, может быть примерно составлять произведение кол-ва поток в системе, на процессорное время каждого из них.

Практически это значение будет разным, т.к. зависит от приоритета потока, состояния потока (активный/неактивны), а также от загруженности процессора.

Хоть это время и мало, но на малых интервалах времени это уже будет заметно.

Так что основная задача будет - вычислить примерное значение этой паузы при разных условиях и сравнить её длительность с 1-10 микросекундами


Метод проверки(Алгоритмическая часть):
Т.к. значения очень малы и GetTickCount на не сможет жать настолько точные данные, то придется делать вычисления в самых точных единицах - тактах процессора, т.к. их кол-во легко и быстро получается и оно довольно стабильно.

Алгоритм действия будет таков:
0) установить нужный приоритет потока
1) засечь текущее кол-во тактов
2) сохранить это значение в массиве
3) выполнить действия минимально использующие ресурсы системы за исключением процессора. работа с памятью и процессором (без математических вычислений). В нашем случае это будет сохранение в массиве
3) сново получить кол-во тактов
4) сохранить это значение в массиве
5) проделать большое кол-во раз операции 1 - 4
Это будет сделано для того, чтобы точно попасть на время когда закончится процессорное время патока.
6) вернуть приоритет
7) пройтись по массиву и вычислить минимум и максимум.
Максимум - наибольшая время выполнение команды (в тактах)
Минимум - реальное время выполнения команды

нам как раз и будет интересовать максимум. А минимум мы будем использовать для проверки точности данных.

разница между максимум и минимум как раз и будет время паузы в тактах.

Метод проверки(Практическая часть часть):
Код:
var
  x : array [0..100000] of int64; // массивы для хранения
  y : array [0..100000] of int64; // кол-ва таквто

// массив приоритетов 
 TestPrior : array[0..3] of integer = (THREAD_PRIORITY_LOWEST, THREAD_PRIORITY_NORMAL, THREAD_PRIORITY_HIGHEST, THREAD_PRIORITY_TIME_CRITICAL);
// массив названий приоритета
 TestName : array[0..3] of string = ('Низкий', 'Средний', 'Высокий', 'Очень высокий');

// процедура проверки
// max_time будет хранить максимум
// min_time будет хранить минимум
procedure GetMaxMinByPrior(var max_time : int64; var min_time : int64; prior : integer);stdcall;
var
  i : integer;
  Thread : Cardinal;
begin
  Thread := GetCurrentThread;
  SetThreadPriority(Thread, TestPrior[prior]); // установим потоку нужный приоритет

  Sleep(100); // сделаем паузу чтобы приоритет вступил в действия наверняка. 

  asm
    lea esi, x // начало массива
    lea edi, y// начало массива

    mov ecx, 100000 // 100k раз повторим замеры
    @m1:
    rdtsc // получим кол-во тактов
    mov dword [esi], eax // сохраним младшую часть
    mov dword [esi+4], edx // сохраним старшую часть

    rdtsc // опять получим кол-во тактов
   // опять сохраним
    mov dword [edi], eax 
    mov dword [edi+4], edx

    add esi, 8 // перейдем на следующий элемент массива
    add edi, 8
    loop @m1 // повторим цикл
  end;
  // вернем обратно приоритет
  SetThreadPriority(Thread, THREAD_PRIORITY_NORMAL);
  max_time := 0;
  min_time := $FFFFFFFFFFFF;

  for i := 0 to 99999 do // пройдемся по элементам
  begin
    // будим искать максимум
    if max_time < y[i] - x[i] then max_time := y[i] - x[i];
   // будим искать минимум
    if min_time > y[i] - x[i] then min_time := y[i] - x[i];
  end;

end;


procedure TForm1.Button1Click(Sender: TObject);
const
  N = 10; // 10 раз будем повторять один и тотже тест

var
  max : int64;
  min : int64;
  x : integer;
  y : integer;
  smax, smin : int64;
begin
  for y := 0 to 3 do // выполним все тесты
  begin
    memo1.Lines.Add('---------- '+TestName[y]+' ----------');
    smax := 0;
    smin := $FFFFFFFFFFF;

    for x := 1 to N do // повторы выполнения теста
    begin
      GetMaxMinByPrior(max, min, y);
      // сразу найдем максимум и минимум среди повторов
      if smax < max then smax := max;
      if smin > min then smin := min;
    end;
    // выведем данные
    memo1.Lines.Add(inttostr(smax) + #9 + inttostr(smin))
  end;
end;
Анализ полученных данных:
1) При простое процессора
Низкий 76825 25
Средний 29575 25
Высокий 19275 25
Очень высокий 23188 25

2) При загруженности процессора упаковкой фильма через winrar
Низкий 143088 25
Средний 858150 25
Высокий 53737 25
Очень высокий 23387 25

3) Тестирование упаковке через 7z (с поддержкой многоядерных систем). Тест для того, чтобы загрузить оба ядра сразу.

Низкий 2092750 25
Средний 3358850 25
Высокий 80412 25
Очень высокий 35487 25

Из этого выводы такие:
1) приоритет играет малую роль на паузу, при выполнение небольшого кол-ва данных. По этому пауза может быть на высоком приоритете больше чем на низком.
2) загруженность процессора довольно хорошо влияет.
3) минимальное значение за которое может процессор выполнить команды - 25 тактов.
4) Также зависит от работы планировщика задач. т.е. при некоторых тестах иногда вылазели данные 10458800, что соответсовало бы 4 микросекундам


И так, ради чего это проверяли:
Будет брать паузу только на нормальном приоритете
29550 тактов (без загруженности )
858125 тактов (с загруженностью 1 ядра)
3358825 тактов (с загруженность 2 ядер)
Если учесть что процессор имеет частоту 2,5 Ггц
то это 2,5 миллиарда тактов в секунду.
Или же 2,5 миллиона тактов в микросекунду.
Или же 2500 тактов в наносекунду.
Из полученных банных можно говорить что
1) при простое выходит 0,011 микросекунды пауза
2) при загруженности 1 ядра выходит 0,343 микросекунды пауза
3) при загруженности 2 ядер 1,3 микросекунды

Как видно, - современные компьютеры могут обеспечить паузу сравнимую с 1 микросекундой.
Но всё что больше - это уже будет иметь погрешности.
Да и при условии минимальной загруженности процессора.

Также можно было заметить что при загруженности 2-х ядер сразу пауза вышла за пределы 1 микросекунды.
Для одноядерных процессоров результат будет еще хуже.

Вот и всё
(С) SLESH

Последний раз редактировалось slesh; 17.11.2009 в 18:34..