[.net] System.Threading.Thread.Sleep

state7401281

уперся в то, что точность sleep и datetime кратна ~15мс
есть ли способ повысить эту точность, ведь system.diagnostics.stopwatch умеет нормально считать миллисекунды?
если этот тред имеет отношение к вопросу
http://www.techtalkz.com/microsoft-device-drivers/283300-kew...
подскажите, как вызвать ExSetTimerResolution?
или как сделать sleep 1мс

Dasar

посмотри сколько ждет WaitHandle.Wait(1, false) - должно подойти.

state7401281

WaitHandle.Wait(int, boolean) нет такого :(
WaitOne(int, boolean) подойдет?

state7401281

не waitone тоже нет ... есть waitany, но он не работает

Dasar

не waitone тоже нет ...
сколько ждет?

state7401281

до сих пор ждет

state7401281

System.Threading.WaitHandle.WaitAny(Nothing, 1, False)
так не работает

Dasar

до сих пор ждет
так не бывает

Dasar

кстати Thread.Sleep(1) у меня выполняется за 1.5 мс

state7401281

сек, давай заново, мне5 кажется я что-то не так делаю ...

state7401281

есть вот такой кусочек кода:

Dim tStopWatch As New System.Diagnostics.Stopwatch
tStopWatch.Reset
tStopWatch.Start
Debug.Print(DateTime.Now.TimeOfDay.ToString & " " & tStopWatch.Elapsed.TotalMilliseconds)
System.Threading.Thread.Sleep(1)
Debug.Print(DateTime.Now.TimeOfDay.ToString & " " & tStopWatch.Elapsed.TotalMilliseconds)
System.Threading.Thread.Sleep(1)
Debug.Print(DateTime.Now.TimeOfDay.ToString & " " & tStopWatch.Elapsed.TotalMilliseconds)
System.Threading.Thread.Sleep(1)
Debug.Print(DateTime.Now.TimeOfDay.ToString & " " & tStopWatch.Elapsed.TotalMilliseconds)
System.Threading.Thread.Sleep(1)
Debug.Print(DateTime.Now.TimeOfDay.ToString & " " & tStopWatch.Elapsed.TotalMilliseconds)
System.Threading.Thread.Sleep(1)
Debug.Print(DateTime.Now.TimeOfDay.ToString & " " & tStopWatch.Elapsed.TotalMilliseconds)
System.Threading.Thread.Sleep(1)
Debug.Print(DateTime.Now.TimeOfDay.ToString & " " & tStopWatch.Elapsed.TotalMilliseconds)
System.Threading.Thread.Sleep(1)
Debug.Print(DateTime.Now.TimeOfDay.ToString & " " & tStopWatch.Elapsed.TotalMilliseconds)
System.Threading.Thread.Sleep(1)
Debug.Print(DateTime.Now.TimeOfDay.ToString & " " & tStopWatch.Elapsed.TotalMilliseconds)
System.Threading.Thread.Sleep(1)
Debug.Print(DateTime.Now.TimeOfDay.ToString & " " & tStopWatch.Elapsed.TotalMilliseconds)
System.Threading.Thread.Sleep(1)
Debug.Print(DateTime.Now.TimeOfDay.ToString & " " & tStopWatch.Elapsed.TotalMilliseconds)
System.Threading.Thread.Sleep(1)
Debug.Print(DateTime.Now.TimeOfDay.ToString & " " & tStopWatch.Elapsed.TotalMilliseconds)
System.Threading.Thread.Sleep(1)
Debug.Print(DateTime.Now.TimeOfDay.ToString & " " & tStopWatch.Elapsed.TotalMilliseconds)
System.Threading.Thread.Sleep(1)
Debug.Print(DateTime.Now.TimeOfDay.ToString & " " & tStopWatch.Elapsed.TotalMilliseconds)
System.Threading.Thread.Sleep(1)
Debug.Print(DateTime.Now.TimeOfDay.ToString & " " & tStopWatch.Elapsed.TotalMilliseconds)
System.Threading.Thread.Sleep(1)
Debug.Print(DateTime.Now.TimeOfDay.ToString & " " & tStopWatch.Elapsed.TotalMilliseconds)
System.Threading.Thread.Sleep(1)
Debug.Print(DateTime.Now.TimeOfDay.ToString & " " & tStopWatch.Elapsed.TotalMilliseconds)
System.Threading.Thread.Sleep(1)
Debug.Print(DateTime.Now.TimeOfDay.ToString & " " & tStopWatch.Elapsed.TotalMilliseconds)

результат:

18:32:11.4375000 0.0382
18:32:11.4687500 23.0864
18:32:11.4843750 38.6783
18:32:11.5000000 55.6054
18:32:11.5312500 86.391
18:32:11.5468750 101.2287
18:32:11.5625000 116.7236
18:32:11.5781250 132.34
18:32:11.5937500 148.7801
18:32:11.6093750 163.6587
18:32:11.6250000 179.202
18:32:11.6406250 194.8297
18:32:11.6562500 210.4842
18:32:11.6718750 226.1031
18:32:11.6875000 241.7189
18:32:11.7031250 257.3155
18:32:11.7187500 272.9358
18:32:11.7343750 288.7993

state7401281

все sleep(1) спали по ~15 мс

state7401281

ну и в DateTime.Now.TimeOfDay.ToString округление до точности ~15мс тоже заметно

Dasar

вот такой код у меня выдает в районе 1.5 мс
var stopwatch = new System.Diagnostics.Stopwatch;
foreach (var i in Enumerable.Range(0, 100
{
stopwatch.Reset;
stopwatch.Start;
System.Threading.Thread.Sleep(1);
stopwatch.Stop;
Console.WriteLine("{0}", stopwatch.Elapsed);

}
извращения с wait-ом выдают разброс 10-20мс
var stopwatch = new System.Diagnostics.Stopwatch;
var handle = new System.Threading.ManualResetEvent(false);
foreach (var i in Enumerable.Range(0, 100
{
stopwatch.Reset;
stopwatch.Start;
handle.WaitOne(1, true);
//System.Threading.Thread.Sleep(1);
stopwatch.Stop;
Console.WriteLine("{0}", stopwatch.Elapsed);

}
ps
15 мс - кстати, это не разрешение таймера.
это размер кванта (непрерывное время работы, которое выделяется одному треду)

pitrik2

~15мс
эээ
а разве это не настройка ос?
т.е. типа из-под проги ты этого сделать не сможешь, нужно спецом это для всей ос менять

Dasar

еще кстати есть Thread.SpinWait, но там придется подбирать число взависимости от компа.

state7401281

> ps 15 мс - кстати, это не разрешение таймера.
> это размер кванта (непрерывное время работы, которое выделяется одному треду)
его можно как-то уменьшить?
херня какая-то ....

Dim tStopWatch As New System.Diagnostics.Stopwatch
For Each i As Integer In Enumerable.Range(0, 20)
tStopWatch.Reset
tStopWatch.Start
System.Threading.Thread.Sleep(1)
tStopWatch.Stop
Debug.Print(DateTime.Now.TimeOfDay.ToString & " " & i & " " & tStopWatch.ElapsedTicks / Stopwatch.Frequency * 1000)
Next

один комп:

19:16:44.5625000 0 63.8339287241625
19:16:44.5625000 1 1.13260156806842
19:16:44.5625000 2 63.5631575196009
19:16:44.5781250 3 63.5234069850321
19:16:44.5781250 4 1.61838275124733
19:16:44.5781250 5 63.5761960085531
19:16:44.5781250 6 63.5570669992872
19:16:44.5781250 7 63.4373467569494
19:16:44.5781250 8 1.64360085531005
19:16:44.5781250 9 63.6239044903778
19:16:44.5781250 10 63.5709597291518
19:16:44.5937500 11 63.6500534568781
19:16:44.5937500 12 63.6469290805417
19:16:44.5937500 13 63.5654269422666
19:16:44.5937500 14 1.61183178902352
19:16:44.5937500 15 63.6458267997149
19:16:44.5937500 16 63.6488360655738
19:16:44.5937500 17 63.593267997149
19:16:44.5937500 18 1.62230862437634
19:16:44.6093750 19 1.66457020669993

другой комп:

19:18:50.8750000 0 15.5420209580838
19:18:50.8906250 1 4.44920658682635
19:18:50.9062500 2 12.3622874251497
19:18:50.9218750 3 14.8963053892216
19:18:50.9375000 4 12.3847305389222
19:18:50.9531250 5 14.9188982035928
19:18:50.9687500 6 13.130754491018
19:18:50.9843750 7 13.7322844311377
19:18:51 8 13.7489790419162
19:18:51.0156250 9 15.2361137724551
19:18:51.0312500 10 14.6206047904192
19:18:51.0468750 11 13.4371407185629
19:18:51.0625000 12 12.8650568862275
19:18:51.0781250 13 14.7166976047904
19:18:51.0937500 14 14.8667664670659
19:18:51.1093750 15 14.0274580838323
19:18:51.1250000 16 6.51919760479042
19:18:51.1406250 17 13.8118862275449
19:18:51.1562500 18 12.5790898203593
19:18:51.1718750 19 14.7640299401198

state7401281

> нужно спецом это для всей ос менять
я в целом не против

lubanj

в релизе запускаешь?

state7401281

запусти плиз с DateTime.Now.TimeOfDay.ToString
у тебя там будет дискретно по ~15мс?
> еще кстати есть Thread.SpinWait, но там придется подбирать число взависимости от компа
типа Stopwatch.Frequency? оно у меня тоже на разных компах разное

state7401281

> в релизе запускаешь?
и в релизе и в дебаге
в релизе так:

Dim str As String = ""
Dim tStopWatch As New System.Diagnostics.Stopwatch
For Each i As Integer In Enumerable.Range(0, 20)
tStopWatch.Reset
tStopWatch.Start
System.Threading.Thread.Sleep(1)
tStopWatch.Stop
str &= DateTime.Now.TimeOfDay.ToString & " " & i & " " & tStopWatch.ElapsedTicks / Stopwatch.Frequency * 1000 & vbCrLf
Next
MsgBox(str)

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

Dasar

один комп:
code:
19:16:44.5625000 0 63.8339287241625
19:16:44.5625000 1 1.13260156806842
сервер?
у них кванты обычно длинные

Dasar

типа Stopwatch.Frequency?
да, скорее всего, через него должно как раз сработать

state7401281

> сервер?
тот, где по ~15мс stopwatch идет - 2k3 server x64
где то 1.5мс, то 60мс - xp x64
компы свеже перезагружены, форум читаю с третьего, но на нем нет студии
был еще один комп с xp professional x32 на нем было тоже ~15мс

state7401281

на xp x64 добился такого результата:

19:39:23.2500000 0 1.43306771204562
19:39:23.2500000 1 1.88172665716322
19:39:23.2500000 2 1.92198503207413
19:39:23.2500000 3 1.94819066286529
19:39:23.2500000 4 1.94409230220955
19:39:23.2500000 5 1.94893692088382
19:39:23.2656250 6 1.94767640769779
19:39:23.2656250 7 1.9647131147541
19:39:23.2656250 8 2.02750463292944
19:39:23.2656250 9 1.84416500356379
19:39:23.2656250 10 -60.2873588738418
19:39:23.2656250 11 64.1413774055595
19:39:23.2656250 12 1.94365431218817
19:39:23.2656250 13 1.94816037063436
19:39:23.2812500 14 1.94897255880257
19:39:23.2812500 15 1.94821917320028
19:39:23.2812500 16 1.93897042052744
19:39:23.2812500 17 1.94814682822523
19:39:23.2812500 18 1.94885245901639
19:39:23.2812500 19 64.1559337134711

не понимаю откуда отрицательные цифры, это связано с многоядерностью?

Dasar

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

state7401281

не вкурсе, есть патч, который меняет квант?

Dasar

в реестре, вроде что-то менять можно было, но сильно не советовали.
конкретику не помню

state7401281

> 15 мс - кстати, это не разрешение таймера.
> это размер кванта (непрерывное время работы, которое выделяется одному треду)
мне все-таки кажется это именно разрешение таймера, та переодичность, с которой планировщик тредов получает управление/перегружает контексты и прочую ботву и уже как следствие это размер кванта
нужно как-то разогнать это время, с 15мс невозможно работать :( всё мимо

pitrik2

нужно как-то разогнать это время, с 15мс невозможно работать всё мимо
я когда с этим столкнулся, то мне все коллеги сказали что это настройка операционки и что ее менять это бред
бред - и потому что моя прога не одна будет запускаться в своей ос, и потому что для этого нужны админские права и мою прогу не только админы должны запускать ну и т.д.
мне это не один коллега сказал, а сразу несколько
я вот чото даж не стал гуглить так оно или не так, поверил что в дойчебанке работники соображают, раз говорят :)
дык вот
как мне сказали сделать я так и сделал
у проги появился новый параметр timerGranularity
если надо заснуть на время t то засыпаем на время t / timerGranularity а на оставшиеся t % timerGranularity жжем процессор

Time toWakeUp = currentTime + t;
if(t / timerGranularity > 0) {
sleep(t / timerGranularity);
}
while(true) {
if(currentTime >= toWakeUp) {
break;
}
}

state7401281

> бред - и потому что моя прога не одна будет запускаться в своей ос, и потому что
> для этого нужны админские права и мою прогу не только админы должны запускать ну и т.д.
в моем случае все наоборот, на компе можно не запускать ни одного приложения кроме этого, ни на каком другом это приложение запускать не нужно, всех юзеров можно сделать админами итд
> у проги появился новый параметр timerGranularity
> если надо заснуть на время t то засыпаем на время t / timerGranularity
> а на оставшиеся t % timerGranularity жжем процессор
сейчас так и делаю, не нравится, хочу попробовать с разрешением таймера если еще реально -уточни плиз у парней из дойче как это можно сменить?

evgen5555

Запусти 15 тредов!111

freezer

мне все-таки кажется это именно разрешение таймера
Совершенно верно. Уберите слип, поставьте цикл с тупым выводом времени (пофиг, System.DateTime.Now или boost:: posix_time ::microsec_clock обнаружите дискретность в 16 с чем-то мс. Причём, под линухом такой большой дискретности не будет

pitrik2

уточни плиз у парней из дойче как это можно сменить?
откуда им знать?
мы же под соляру программим

pitrik2

вбил в гугл windows timer granularity
попал на всякие ссылки про енто дело
нигде не пишут как поменять зато пишут что можно использовать другие таймеры
например multimedia timer имеет гранулярность 1 ms
сделано это специально для игрушек, чтобы достигать нужно значения FPS
вот ссылка на msdn про multimedia timer: http://msdn.microsoft.com/en-us/library/ms704986%28VS.85%29....
о блин
смотри как люди извращаются чтобы высокую точность получить
http://code.google.com/p/sklepseq/source/browse/trunk/xsync....

state7401281

> Запусти 15 тредов!111
запустил 122, все равно 15мс

state7401281

на соляре тоже 15мс? не пойму это os-зависимая цифра или нет?

evgen5555

Радномно слипай, пока между разными тредами не будет желаемой разницы!

murmashik

Сталкивался с такой проблемой, когда надо было генерить UDP пакеты с заданной частотой, но чаще, чем раз в 12 мс.
Могу ошибаться, но, по-моему, проблема была связана с тем, что такт процессора занимает ~12 мс и меньше этого времени фреймворк отмерить не может.
Проблема решилось использованием QueryPerformanceCounter и QueryPerformanceFrequency из kernel32.dll.
То есть придется примерно так использовать внешние функции:
[System.Runtime.InteropServices.DllImport("kernel32.dll" System.Security.SuppressUnmanagedCodeSecurity]
static extern bool QueryPerformanceCounter(out Int64 PerformanceCount);
[System.Runtime.InteropServices.DllImport("kernel32.dll" System.Security.SuppressUnmanagedCodeSecurity]
static extern bool QueryPerformanceFrequency(out Int64 PerformanceFrequency);
Для дальнейшего понимания как оно работает, предлагаю обратиться к гуглу

freezer

такт процессора занимает ~12 мс
ого =) это что за процессор такой, тактовой частотой 80Hz? :grin:

murmashik

Почитал тред. 12 мс не такт, а квант для процесса. Да, именно так и было.
Указанные функции тебя спасут

state7401281

> Радномно слипай, пока между разными тредами не будет желаемой разницы!
Асет, не тупи, желаемой разницы не будет

murmashik

Да я уж понял, что тупанул, когда отправил

state7401281

> Почитал тред. 12 мс не такт, а квант для процесса. Да, именно так и было.
> Указанные функции тебя спасут
№;%:№;, да я про них с самого начала написал, что они не подходят, потому что если тред таки заснет, то проснется только через квант (15мс)

state7401281

и варианты тут - либо кипятить процессор, либо изменить квант

Dasar

вот это у тебя кстати что выдает:

[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
public struct TimeCaps
{
public UInt32 wPeriodMin;
public UInt32 wPeriodMax;
};


[System.Runtime.InteropServices.DllImport("winmm.dll", SetLastError = true)]
static extern UInt32 timeGetDevCaps(ref TimeCaps timeCaps,
int sizeTimeCaps);

var caps = new TimeCaps;
timeGetDevCaps(ref caps, System.Runtime.InteropServices.Marshal.SizeOf(caps;

Console.WriteLine(caps.wPeriodMin);
Console.WriteLine(caps.wPeriodMax);

state7401281

ща, давай начнем с этого

#include <windows.h>
#include <stdio.h>
#include <conio.h>

typedef NTSTATUS (CALLBACK *GETPROCOUT PULONG, OUT PULONG, OUT PULONG);
typedef NTSTATUS (CALLBACK *SETPROCIN ULONG, IN BOOLEAN, OUT PULONG);

void main(void)
{
HINSTANCE hinstLib;
GETPROC ProcGet;
SETPROC ProcSet;
ULONG MinBefore;
ULONG MaxBefore;
ULONG ActBefore;
NTSTATUS RetGetBefore;
ULONG MinAfter;
ULONG MaxAfter;
ULONG ActAfter;
NTSTATUS RetGetAfter;

BOOL SetResol = TRUE;
ULONG ActNew = 1;
ULONG ActSet;
NTSTATUS RetSet;

hinstLib = LoadLibrary(TEXT("NTDLL";

if (NULL != hinstLib)
{
ProcGet = (GETPROC) GetProcAddress(hinstLib, "NtQueryTimerResolution");
ProcSet = (SETPROC) GetProcAddress(hinstLib, "NtSetTimerResolution");

if (NULL != ProcGet && NULL != ProcSet)
{
RetGetBefore = (ProcGet) (&MinBefore, &MaxBefore, &ActBefore);
RetSet = (ProcSet) (ActNew, SetResol, &ActSet);
RetGetAfter = (ProcGet) (&MinAfter, &MaxAfter, &ActAfter);
}
FreeLibrary(hinstLib);
}

printf("Before:\n Minimum: %d\n Maximum: %d\n Actual: %d\n Result: %d\n\n", MinBefore, MaxBefore, ActBefore, RetGetBefore);
printf("Setting:\n Actual: %d\n Result: %d\n\n", ActSet, RetSet);
printf("After:\n Minimum: %d\n Maximum: %d\n Actual: %d\n Result: %d\n\n", MinAfter, MaxAfter, ActAfter, RetGetAfter);
getch;
}

это решило проблему

state7401281

для тех, кто понял о чем тема, рекомендую поставить код из предыдущего поста в атозагрузку
ATTENTION: после этого CPU Utilization может ЗАМЕТНО вырасти (но мне именно это и нужно было)

freezer

ща, давай начнем с этого
 
Before:
Minimum: 156250
Maximum: 10000
Actual: 9766
Result: 0

Setting:
Actual: 9766
Result: 0

After:
Minimum: 156250
Maximum: 10000
Actual: 9766
Result: 0

и ничего не изменилось

Dasar

и ничего не изменилось
а что ты ожидал что изменится?
sleep(1ms) у тебя сколько занимает?

freezer

а что ты ожидал что изменится?
величина Actual

state7401281

она у тебя уперлась в 10000, которая походу захардкожена где-то,
но в целом - забей, у тебя и до запуска квант 1мс был

Dasar

но в целом - забей, у тебя и до запуска квант 1мс был
насколько я понял - там чуть хитрее
квант - вроде и остается большим, но если квант не использовался, то через это время (которые ты как раз менял он повторно выдается приложениям

state7401281

то что я изменил - это время генерации прерываний от real-time clock (irq0 вроде task sheduler висит на прерывании таймера, task sheduler работает с тредами (не процессами квант треда это целое число, для винды 2k3 server это 36, с каждого тика квант активного треда уменьшается на 3, тред переключается когда квант 0
формально - квант не изменился, но до 0 он теперь сползает в 15 раз быстрее

Dasar

с каждого тика квант активного треда уменьшается на 3, тред переключается когда квант 0
в оригинале - это называлось приоритет кванта, а не сам квант.

state7401281

> в оригинале - это называлось приоритет кванта, а не сам квант.
наверное, но мысль была разогнать именно task sheduler, вроде получилось

freezer

а у тебя эта прога что выводила? И винда какая?

state7401281


Before:
Minimum: 156250
Maximum: 10000
Actual: 156250
Result: 0

Setting:
Actual: 9765
Result: 0

After:
Minimum: 156250
Maximum: 10000
Actual: 9765
Result: 0

state7401281

win 2k3 server x64

pitrik2

на соляре тоже 15мс? не пойму это os-зависимая цифра или нет?
узнал как енто в соляре менять
напишу тут чтоб в флокалных архивах осталось

file /etc/system

* The hires tick provides finer granularity of timing mechanisms using the default
* system clock. Functions such as sleeps and timestamps are restricted by
* this resolution unless they implement a real-time clock. Setting hires_tick
* runs the system clock interrupt at 1000 Hz instead of 100 Hz, giving resolution
* of ~1msec instead of ~10ms.

# Use hires tick (increases system clock from 100 Hz to 1000 Hz
set hires_tick=1

* Disabling the dynamic adjustment of the interrupt rate may eliminate latency
* from network operations. These are recommended as best practice
* for high throughput/low-latency applications.

# Disable dynamic adjustment of interrupt rate
set dld:dld_opt = 2

там есть еще какой-то параметр который можно не как делитель а прям в герцах задавать
тогда можно меньше миллисекунды получить

state7401281

круто, может это даже и мне пригодится, спс
в соляре моно фурычит?
Оставить комментарий
Имя или ник:
Комментарий: