1 2 3
Как заставить settimer квантировать менее 15 мс? в избранное  новое горячее всё    подписка   модер. 
От: Аноним 143 
Дата: 09.02.10 08:43
Windows XP. Ставлю параметр settimer равный 1 мс. Получаю сообщдения WM_TIMER каждые 15-16 мс. Как сделать, чтобы сообщения приходили каждую 1 мс?

Спасибо!



09.02.10 14:26: Перенесено модератором из 'C/C++. Прикладные вопросы' — Кодт
Re: Как заставить settimer квантировать менее 15 мс? в избранное  новое    модер. 
От: OdesitVadim 
Дата: 09.02.10 08:50
Здравствуйте, Аноним, Вы писали:

А>Windows XP. Ставлю параметр settimer равный 1 мс. Получаю сообщдения WM_TIMER каждые 15-16 мс. Как сделать, чтобы сообщения приходили каждую 1 мс?


А>Спасибо!


Отказаться от него. не даст он такую точность — не предназначен для этого. а взамен использовать мультимедийный таймер. Искать в MSDN по слову mmsystem.h
Re: Как заставить settimer квантировать менее 15 мс? в избранное  новое    модер. 
От: Аноним 594 
Дата: 09.02.10 08:53
НИКАК!


Здравствуйте, Аноним, Вы писали:

А>Windows XP. Ставлю параметр settimer равный 1 мс. Получаю сообщдения WM_TIMER каждые 15-16 мс. Как сделать, чтобы сообщения приходили каждую 1 мс?


А>Спасибо!
Re: Как заставить settimer квантировать менее 15 мс? в избранное  новое    модер. 
От: Nik_1 
Дата: 09.02.10 08:57
Оценка: +1
Здравствуйте, Аноним, Вы писали:

А>Windows XP. Ставлю параметр settimer равный 1 мс. Получаю сообщдения WM_TIMER каждые 15-16 мс. Как сделать, чтобы сообщения приходили каждую 1 мс?

А>Спасибо!

WM_TIMER лишь гарантирует, что сообщение будет получено не рание, чем через заданный интервал, а не точно через него. А когда именно будет получено — не горонтируется, может вообще хоть через час придти( если очередь сообщений потока будет постоянно забита более приоритетными сообщениями).
Пробуждение потока каждые 1 мс в Windows XP довольно сложно организовать в пользовательском режиме, не стоит писать прогу закладывающуюся на такое поведение. Для этого есть операционные системы реального времени, ХР тут не подойдет.
Re[2]: Как заставить settimer квантировать менее 15 мс? в избранное  новое    модер. 
От: Аноним 143 
Дата: 09.02.10 10:46
Здравствуйте, Nik_1, Вы писали:

N_>Здравствуйте, Аноним, Вы писали:


А>>Windows XP. Ставлю параметр settimer равный 1 мс. Получаю сообщдения WM_TIMER каждые 15-16 мс. Как сделать, чтобы сообщения приходили каждую 1 мс?

А>>Спасибо!

N_>WM_TIMER лишь гарантирует, что сообщение будет получено не рание, чем через заданный интервал, а не точно через него. А когда именно будет получено — не горонтируется, может вообще хоть через час придти( если очередь сообщений потока будет постоянно забита более приоритетными сообщениями).

N_>Пробуждение потока каждые 1 мс в Windows XP довольно сложно организовать в пользовательском режиме, не стоит писать прогу закладывающуюся на такое поведение. Для этого есть операционные системы реального времени, ХР тут не подойдет.


Если перейти на Windows 7 ситуация станет лучше? или будут те же минимальные 15-16 мс?
Re: Как заставить settimer квантировать менее 15 мс? в избранное  новое    модер. 
От: Qa1888 
Дата: 09.02.10 11:01
может попробовать разбить на несколько потоков, в одном из них просто ставить задержку в 15мс. Короче сделать самому емуляцию WM_TIMER, если задача позволяет.

А>Windows XP. Ставлю параметр settimer равный 1 мс. Получаю сообщдения WM_TIMER каждые 15-16 мс. Как сделать, чтобы сообщения приходили каждую 1 мс?

А>Спасибо!
Re[2]: Как заставить settimer квантировать менее 15 мс? в избранное  новое    модер. 
От: Qa1888 
Дата: 09.02.10 11:03
хотел написать ставить задержку 1мс в потоке

Q>может попробовать разбить на несколько потоков, в одном из них просто ставить задержку в 15мс. Короче сделать самому емуляцию WM_TIMER, если задача позволяет.


А>>Windows XP. Ставлю параметр settimer равный 1 мс. Получаю сообщдения WM_TIMER каждые 15-16 мс. Как сделать, чтобы сообщения приходили каждую 1 мс?

А>>Спасибо!
Re[3]: Как заставить settimer квантировать менее 15 мс? в избранное  новое    модер. 
От: ononim 
Дата: 09.02.10 11:03
Оценка: +1
А>Если перейти на Windows 7 ситуация станет лучше? или будут те же минимальные 15-16 мс?
В винде размер кванта времени исполнения потока имеет порядок 30..100 мсек. Так что даже если вы и добьетесь от таймера нужного вам разрешения, ваш общий алгоритм не будет корректно работать если он так жестко зависит от времени исполнения участков кода.
Как много веселых ребят, и все делают велосипед...
Re: Как заставить settimer квантировать менее 15 мс? в избранное  новое    модер. 
От: Melamed 
Дата: 09.02.10 12:07
Здравствуйте, Аноним, Вы писали:

Попробуй использовать мултимедийный таймер. Пусть каждую милисекунду он устанавливает объект ядра событие в отмеченное состояние. Лови отмеченное сотояние обекта событие в отдельном потоке и пиши там реакцию на таймер. У меня где-то есть пример, правда на Delphi, ревлизующийй 25-тый кадр по этой технологии. Если найду, выложу.

А>Windows XP. Ставлю параметр settimer равный 1 мс. Получаю сообщдения WM_TIMER каждые 15-16 мс. Как сделать, чтобы сообщения приходили каждую 1 мс?


А>Спасибо!
Re[2]: Как заставить settimer квантировать менее 15 мс? в избранное  новое    модер. 
От: Евгений Музыченкоhttp://eugene.muzychenko.net
Дата: 09.02.10 15:05
Оценка: +2
Здравствуйте, OdesitVadim, Вы писали:

А>>Ставлю параметр settimer равный 1 мс. Получаю сообщдения WM_TIMER каждые 15-16 мс. Как сделать, чтобы сообщения приходили каждую 1 мс?


OV>Отказаться от него. не даст он такую точность — не предназначен для этого. а взамен использовать мультимедийный таймер. Искать в MSDN по слову mmsystem.h


В винде все пользовательские таймеры работают через одни и те же таймеры ядра, у которых максимальное периодическое разрешение — 1 мс, а одноразовое — где-то 0.977. Waitable Timer и Sleep в сочетании с timeBeginPeriod вполне дают миллисекундную точность (при отсутствии посторонней нагрузки, конечно), а в случае с оконным таймером добавляется нехилый оверхэд оконной подсистемы.

Самое оптимальное — использовать WINAPI (Waitable Timers или Sleep в выделенном потоке), они ближе всего к таймерам ядра (работают через NtSetTimer), и допускают непосредственное ожидание. timeSetEvent тоже работает через NtSetTimer, но нуждается в выделенном потоке, через который вызывает заданную функцию, а это дополнительные расходы.
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[4]: Как заставить settimer квантировать менее 15 мс? в избранное  новое    модер. 
От: gear nuke 
Дата: 12.02.10 09:01
Здравствуйте, ononim, Вы писали:

O>В винде размер кванта времени исполнения потока имеет порядок 30..100 мсек. Так что даже если вы и добьетесь от таймера нужного вам разрешения


...а именно вызовите timeBeginPeriod(1); то и квант планировщика будет уменьшен до 1мсек.

O> ваш общий алгоритм не будет корректно работать если он так жестко зависит от времени исполнения участков кода.


Жестко завязывать конечно не стоит.
.
People who are more than casually interested in computers should have at least some idea of what the underlying hardware is like. Otherwise the programs they write will be pretty weird (c) D.Knuth
Re[5]: Как заставить settimer квантировать менее 15 мс? в избранное  новое    модер. 
От: ononim 
Дата: 12.02.10 09:15
O>>В винде размер кванта времени исполнения потока имеет порядок 30..100 мсек. Так что даже если вы и добьетесь от таймера нужного вам разрешения
GN>...а именно вызовите timeBeginPeriod(1); то и квант планировщика будет уменьшен до 1мсек.

Квант планировщика не будет уменьшен до 1мсек. Будет уменьшено разрешение системного таймера.
Как много веселых ребят, и все делают велосипед...
Re[6]: Как заставить settimer квантировать менее 15 мс? в избранное  новое    модер. 
От: ononim 
Дата: 12.02.10 09:41
Здравствуйте, ononim, Вы писали:

O>>>В винде размер кванта времени исполнения потока имеет порядок 30..100 мсек. Так что даже если вы и добьетесь от таймера нужного вам разрешения

GN>>...а именно вызовите timeBeginPeriod(1); то и квант планировщика будет уменьшен до 1мсек.
O>
O>Квант планировщика не будет уменьшен до 1мсек. Будет уменьшено разрешение системного таймера.
вот мелкая приблуда которая меряет время отводимое потоку:


static volatile LONG last_thread_switch_delta = 0;
void __cdecl thread_func(void *)
{
    static volatile LONG last_thread_id = 0;
    static volatile LONG last_thread_switch = 0;
    for (LONG self_thread_id = ::GetCurrentThreadId();;)
    {
        if (::InterlockedExchange(&last_thread_id, self_thread_id)!=self_thread_id)
        {
            LONG ticks = (LONG)::GetTickCount();
            LONG prev = ::InterlockedExchange(&last_thread_switch, ticks);
            ::InterlockedExchange(&last_thread_switch_delta, (DWORD)ticks - (DWORD)prev);
        }
    }    
}

void common(HDC dc)
{
    ::SetProcessAffinityMask(::GetCurrentProcess(), 1);
    timeBeginPeriod(1);
    _beginthread(thread_func, 0, 0);
    _beginthread(thread_func, 0, 0);
    char txt[0x100];
    for(;;)
    {
        ::Sleep(1000);
        if (dc)
        {
            int n = sprintf(txt, "last_thread_switch_delta=%u msec   ", last_thread_switch_delta);
            ::TextOutA(dc, 0, 0, txt, n);
            MSG m;
            while (::PeekMessage(&m, 0, 0, 0, PM_REMOVE))
            {
                ::DispatchMessage(&m);
            }
        }
        else
            printf("last_thread_switch_delta=%u msec   \r", last_thread_switch_delta);
    }

}

void wmain(int argc, wchar_t **argv)
{
    common(0);
}

int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     int       nCmdShow)
{
    HWND wnd = ::CreateWindow(TEXT("STATIC"), TEXT(""), WS_POPUP|WS_VISIBLE, 0, 0, 400, 100, 0, 0, 0, 0);
    HDC dc = ::GetDC(wnd);
    common(dc);
}


Забавно наблюдать разницу между разными версиями винды и тем какое время отводится консольным и не консольным прогам когда они в фореграунде и в бэкграунде.
timeBeginPeriod на это время никак не влияет — он просто уменьшает период системного таймера, но не влияет на квант времени отводимое потоку — просто этот квант чаще проверяется на "истечение".
Кроме того когда один потом уходит в ожидание, второй начинает исполнятся сразу же на этом процессоре, не дожидаясь чеголибо связанного с системным таймером.
Так что timeBeginPeriod не влияет ни на что, кроме дополнительного оверхеда за счет более частых дерганий шедулера и кроме разрешения системных таймеров (которые проверяются тем же шедулером).
Как много веселых ребят, и все делают велосипед...
Re[7]: Как заставить settimer квантировать менее 15 мс? в избранное  новое    модер. 
От: gear nuke 
Дата: 12.02.10 10:23
Здравствуйте, ononim, Вы писали:

O>вот мелкая приблуда которая меряет время отводимое потоку:


Интересный код, можно привести обоснования, почему результаты следует считать квантом планировщика? Иначе можно сказать, что тесты показывают время нескольких квантов, планировщик решает что так лучше.

Я предлагаю другой тест здесь, делался он давно, еще до появления в MSDN подробного описания Sleep:

This function causes a thread to relinquish the remainder of its time slice and become unrunnable for an interval based on the value of dwMilliseconds. — отдаём остаток кванта планировщику.

The system clock "ticks" at a constant rate. If dwMilliseconds is less than the resolution of the system clock, the thread may sleep for less than the specified length of time. — это мне не совсем ясно, подозреваю что врут (копи-паст из SleepEx) ибо противоречит: "Suspends the execution of the current thread for at least the specified interval"

If dwMilliseconds is greater than one tick but less than two, the wait can be anywhere between one and two ticks, and so on. — тут, что бы показать ложность моего утверждения, должно следовать, что тред просыпается по таймеру без участия планировщика?

To increase the accuracy of the sleep interval, call the timeGetDevCaps function to determine the supported minimum timer resolution and the timeBeginPeriod function to set the timer resolution to its minimum. Use caution when calling timeBeginPeriod, as frequent calls can significantly affect the system clock, system power usage, and the scheduler. — а вот явное указание, что таймер влияет на шедюлер, правда непонятно как.
.
People who are more than casually interested in computers should have at least some idea of what the underlying hardware is like. Otherwise the programs they write will be pretty weird (c) D.Knuth
Re[8]: Как заставить settimer квантировать менее 15 мс? в избранное  новое    модер. 
От: ononim 
Дата: 12.02.10 11:27
GN>Интересный код, можно привести обоснования, почему результаты следует считать квантом планировщика? Иначе можно сказать, что тесты показывают время нескольких квантов, планировщик решает что так лучше.
Квант планировщика и квант времени исполнения потока, он же time slice — вещи разные. Данный код меряет именно второе. И именно про второе я писал в самом первом сообщении.
обоснования (вернее, допущения) простые —
1) время исполнения участка цикла на порядок меньше time slice'а потока
2) потоки, работающие в этой программе во время теста потребляют на несколько порядков больше time slice'ов в единицу времени чем все остальные потоки в системе.
Основываясь на этих допущениях, и предположив их порядки, можете посчитать вероятность того что программа выведет реальный timeslice, если вам не впадла. Мне впадла, — мне и так все очевидно, а тут еще работу надо работать.
Можете еще пойти посмотреть исходники винды. Впрочем знаю вашу черту характера никогда не признавать свою неправоту (по кр мере на рсдн) я самоустраняюсь от дальнейшей дискуссии
Как много веселых ребят, и все делают велосипед...
Re[9]: Как заставить settimer квантировать менее 15 мс? в избранное  новое    модер. 
От: gear nuke 
Дата: 12.02.10 12:52
Здравствуйте, ononim, Вы писали:

O>Квант планировщика и квант времени исполнения потока, он же time slice — вещи разные.


Это следует из того, что отдельной функции "планировщик" нет, и можно понимать под ним разный набор функций? Типа "обычное планирование по очередям готовности и приоритетам" и "внеочередное, по wait-функциям и очередям таймеров".

Ок, пусть квант планировщика не меняется. Пусть моё утверждение следует переписать так: "timeBeginPeriod(1) позволит планировать выполнение тредов с точностью 1мс, даже не меняя длительность кванта планировщика" Sleep вызывает планировщик явно.

O> Данный код меряет именно второе. И именно про второе я писал в самом первом сообщении.


Топикстартера интересует управление временными интервалами выполнения треда, о чем писал я, а так же здесь
Автор: Евгений Музыченко
Дата: 09.02.10


O>Основываясь на этих допущениях, и предположив их порядки, можете посчитать вероятность того что программа выведет реальный timeslice, если вам не впадла. Мне впадла, — мне и так все очевидно, а тут еще работу надо работать.


Да мне то не впадла, только результаты расходятся с другим измерением — путём Sleep, а там есть некоторое обоснование основанное на доке, а не допущениях. Ну ок, есть 2 разных кванта

O>Можете еще пойти посмотреть исходники винды.


Нет, не могу, например, HalpClockInterrupt мне пришлось ковырять в IDA, когда разбирался с действием timeBeginPeriod. Совет похож на "иди смотри че попало"

O> Впрочем знаю вашу черту характера никогда не признавать свою неправоту (по кр мере на рсдн) я самоустраняюсь от дальнейшей дискуссии


Ох уж эти тонкие подмены темы, удалялся бы — не правил бы это сообщение
.
People who are more than casually interested in computers should have at least some idea of what the underlying hardware is like. Otherwise the programs they write will be pretty weird (c) D.Knuth
Re[9]: Как заставить settimer квантировать менее 15 мс? в избранное  новое    модер. 
От: gear nuke 
Дата: 12.02.10 13:02
Здравствуйте, ononim, Вы писали:

O>Квант планировщика и квант времени исполнения потока, он же time slice — вещи разные.

O>Данный код меряет именно второе.

Поздравляю, ты меня окончательно запутал

То есть Sleep в моём коде не уменьшает квант потока, но уменьшая квант планировщика делает выполнения треда более точно квантироованным?

Чушь всё это про разные кванты. Ну хранятся они в разных структурах, а у потока он вообще меняется при вызовах планировщика, как его можно мерять каким-то кодом в юзерденде?

Есть кванты через которые будут переключаться треды — не важно к чему они относятся, важно что timeBeginPeriod на них влияет.
.
People who are more than casually interested in computers should have at least some idea of what the underlying hardware is like. Otherwise the programs they write will be pretty weird (c) D.Knuth
Re[10]: Как заставить settimer квантировать менее 15 мс? в избранное  новое    модер. 
От: ononim 
Дата: 12.02.10 14:19
O>>Квант планировщика и квант времени исполнения потока, он же time slice — вещи разные.
O>>Данный код меряет именно второе.
GN>Поздравляю, ты меня окончательно запутал
GN>То есть Sleep в моём коде не уменьшает квант потока, но уменьшая квант планировщика делает выполнения треда более точно квантироованным?
GN>Чушь всё это про разные кванты. Ну хранятся они в разных структурах, а у потока он вообще меняется при вызовах планировщика, как его можно мерять каким-то кодом в юзерденде?
GN>Есть кванты через которые будут переключаться треды — не важно к чему они относятся, важно что timeBeginPeriod на них влияет.

возвращаясь к теме
— изменение разрешения таймера это ExSetTimerResolution
— ExSetTimerResolution это HalSetTimeIncrement
— HalSetTimeIncrement это изменение периода системного таймера
— при тиках системного времени hal дергает KeUpdateSystemTime и KeUpdateRunTime из ntosrknl
KeUpdateRunTime — считает кванты/time slice'ы потоков, когда надо дергает KiQuantumEnd.. тот переключает потоки.. красота.
KeUpdateSystemTime как следует из названия обновляет системное время. А еще.. а еще она проверяет на то что какие-то таймеры сработали. И тоже вызывает KeUpdateRunTime при необходимости. Которая уже зажигает нужный поток если какой то таймер сработал. Ух

Так вот. Фокус в том что KeUpdateSystemTime вызывается на каждый тик системного таймера, а KeUpdateRunTime — так, чтобы квант шедулера не менялся, с учетом текущего разрешения.
В результате мы имеем то что имеем — resolution Sleep'а (и прочих таймеров) зависит от ExSetTimerResolution, а резолюшн шедулера — не зависит. Удивительно правда? ExSetTimerResolution не влияет на резолюшн шедулера, а тока таймера.. Ужасть, совсем неочевидно
Как много веселых ребят, и все делают велосипед...
Re[11]: Как заставить settimer квантировать менее 15 мс? в избранное  новое    модер. 
От: ononim 
Дата: 12.02.10 14:50
O>возвращаясь к теме
O>- изменение разрешения таймера это ExSetTimerResolution
O>- ExSetTimerResolution это HalSetTimeIncrement
O>- HalSetTimeIncrement это изменение периода системного таймера
O>- при тиках системного времени hal дергает KeUpdateSystemTime и KeUpdateRunTime из ntosrknl
O>KeUpdateRunTime — считает кванты/time slice'ы потоков, когда надо дергает KiQuantumEnd.. тот переключает потоки.. красота.
O>KeUpdateSystemTime как следует из названия обновляет системное время. А еще.. а еще она проверяет на то что какие-то таймеры сработали. И тоже вызывает KeUpdateRunTime при необходимости. Которая уже зажигает нужный поток если какой то таймер сработал. Ух

O>Так вот. Фокус в том что KeUpdateSystemTime вызывается на каждый тик системного таймера, а KeUpdateRunTime — так, чтобы квант шедулера не менялся, с учетом текущего разрешения.

O>В результате мы имеем то что имеем — resolution Sleep'а (и прочих таймеров) зависит от ExSetTimerResolution, а резолюшн шедулера — не зависит. Удивительно правда? ExSetTimerResolution не влияет на резолюшн шедулера, а тока таймера.. Ужасть, совсем неочевидно

поправка в деталях (чтобы не жаловался что дескать редактирую) походу я чуть некорректно описал. Я ща внимательнее посмотрел, — похоже "основной" вызов KeUpdateRunTime, происходит только из KeUpdateSystemTime. KeUpdateSystemTime ориентируется на KeMaximumIncrement при обновлении системного времени и периодическом вызове KeUpdateRunTime. Так что вывод тот же — периодичность вызова шедулера (KeUpdateRunTime — переключение потоков по time slice'ами) не зависит от периодичности системного таймера (KeUpdateSystemTime — обновление системного времени в USER_SHARED_DATA, проверка таймеров и вызов системного обработчика KiSwapContext при срабатывании какого-нить таймера (Sleep — это тоже таймер), который начинается с subttl "Dispatch Interrupt" в wrk-v1.2\base\ntos\ke\i386\ctxswap.asm)
Как много веселых ребят, и все делают велосипед...
Re[11]: Как заставить settimer квантировать менее 15 мс? в избранное  новое    модер. 
От: gear nuke 
Дата: 12.02.10 19:15
Здравствуйте, ononim, Вы писали:

O>возвращаясь к теме

O>- изменение разрешения таймера это ExSetTimerResolution
O>- ExSetTimerResolution это HalSetTimeIncrement
O>- HalSetTimeIncrement это изменение периода системного таймера

В предлагаемых для просмотра сорцах этого
Автор: gear nuke
Дата: 17.09.05
нет, потому неточность вполне понятна. Из юзермода цепочка timeBeginPeriod --> NtSetTimerResolution --> HalSetTimeIncrement. Последняя функция не изменяет период, а шедюлит его изменение, которое фактически произойдёт в HalpClockInterrupt.

O>- при тиках системного времени hal дергает KeUpdateSystemTime и KeUpdateRunTime из ntosrknl


Да, это делает HalpClockInterrupt.

O>KeUpdateRunTime — считает кванты/time slice'ы потоков, когда надо дергает KiQuantumEnd.. тот переключает потоки.. красота.


Да, дёргает — хорошее слово, поскольку предлагаемых для просмотра сорцах вызова нет, хотя это и не важно, проще скажу что разбираться впадла

O>KeUpdateSystemTime как следует из названия обновляет системное время. А еще.. а еще она проверяет на то что какие-то таймеры сработали. И тоже вызывает KeUpdateRunTime при необходимости. Которая уже зажигает нужный поток если какой то таймер сработал. Ух


И ох смотрим цепочку SleepEx --> NtDelayExecution --> KeDelayExecutionThread и видим что за таймиры ставились после которых вызовом KiSwapThread выполнение текущего треда прекратилось. То есть произошел явный вызов диспетчера и следующего треда на выполнение.

O>Так вот. Фокус в том что KeUpdateSystemTime вызывается на каждый тик системного таймера


Да, и вызывает выполнение кода после Sleep();

O>, а KeUpdateRunTime — так, чтобы квант шедулера не менялся, с учетом текущего разрешения.


Разрешение это 100ns, так что как раз точность таймеров получится 1мс, если посмотреть калькуляции с таблицей HalpRtcTimeIncrements, которой тоже нет в предлагаемых для просмотра сорцах.

O>В результате мы имеем то что имеем — resolution Sleep'а (и прочих таймеров) зависит от ExSetTimerResolution,


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

O> а резолюшн шедулера — не зависит. Удивительно правда? ExSetTimerResolution не влияет на резолюшн шедулера, а тока таймера.. Ужасть, совсем неочевидно


Неочевидно, что KeUpdateSystemTime меняет System**Time в SharedUserData, при этом делается это не на каждый тик InterruptTime, и минимум на 10, как и вызовы KeUpdateRunTime, но по умолчанию это число больше. Что бы это увидеть придётся смотреть HalpClockInterrupt и KeTimeIncrement, да вот жаль, этого нет в предлагаемых для просмотра сорцах есть только мои сильно покоцанные в этом месте
.
People who are more than casually interested in computers should have at least some idea of what the underlying hardware is like. Otherwise the programs they write will be pretty weird (c) D.Knuth
1 2 3