如何在 .NET/C# 中获取刻度精度的时间戳?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1416139/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to get timestamp of tick precision in .NET / C#?
提问by
Up until now I used DateTime.Now
for getting timestamps, but I noticed that if you print DateTime.Now
in a loop you will see that it increments in descrete jumps of approx. 15 ms. But for certain scenarios in my application I need to get the most accurate timestamp possible, preferably with tick (=100 ns) precision. Any ideas?
到目前为止,我一直DateTime.Now
用于获取时间戳,但我注意到,如果您DateTime.Now
在循环中打印,您会看到它以大约 . 15 毫秒。但是对于我的应用程序中的某些场景,我需要获得最准确的时间戳,最好是刻度 (=100 ns) 精度。有任何想法吗?
Update:
更新:
Apparently, StopWatch
/ QueryPerformanceCounter
is the way to go, but it can only be used to measure time, so I was thinking about calling DateTime.Now
when the application starts up and then just have StopWatch
run and then just add the elapsed time from StopWatch
to the initial value returned from DateTime.Now
. At least that should give me accurate relative timestamps, right? What do you think about that (hack)?
显然,StopWatch
/QueryPerformanceCounter
是要走的路,但它只能用于测量时间,所以我在考虑DateTime.Now
在应用程序启动时调用,然后刚刚StopWatch
运行,然后将经过的时间StopWatch
从DateTime.Now
. 至少这应该给我准确的相对时间戳,对吧?你怎么看(黑客)?
NOTE:
笔记:
StopWatch.ElapsedTicks
is different from StopWatch.Elapsed.Ticks
! I used the former assuming 1 tick = 100 ns, but in this case 1 tick = 1 / StopWatch.Frequency
. So to get ticks equivalent to DateTime use StopWatch.Elapsed.Ticks
. I just learned this the hard way.
StopWatch.ElapsedTicks
不同于StopWatch.Elapsed.Ticks
! 我使用前者假设 1 tick = 100 ns,但在这种情况下 1 tick = 1 / StopWatch.Frequency
。因此,要获得与 DateTime 等效的刻度,请使用StopWatch.Elapsed.Ticks
. 我只是通过艰难的方式学到了这一点。
NOTE 2:
笔记2:
Using the StopWatch approach, I noticed it gets out of sync with the real time. After about 10 hours, it was ahead by 5 seconds. So I guess one would have to resync it every X or so where X could be 1 hour, 30 min, 15 min, etc. I am not sure what the optimal timespan for resyncing would be since every resync will change the offset which can be up to 20 ms.
使用秒表方法,我注意到它与实时不同步。大约10个小时后,它领先了5秒。所以我想人们必须每隔 X 左右重新同步一次,其中 X 可能是 1 小时、30 分钟、15 分钟等。我不确定重新同步的最佳时间跨度是多少,因为每次重新同步都会改变偏移量最多 20 毫秒。
采纳答案by MusiGenesis
The value of the system clock that DateTime.Now
reads is only updated every 15 ms or so (or 10 ms on some systems), which is why the times are quantized around those intervals. There is an additional quantization effect that results from the fact that your code is running in a multithreaded OS, and thus there are stretches where your application is not "alive" and is thus not measuring the real current time.
DateTime.Now
读取的系统时钟值仅每 15 毫秒左右(或在某些系统上为 10 毫秒)更新一次,这就是围绕这些间隔量化时间的原因。由于您的代码在多线程操作系统中运行,因此存在额外的量化效果,因此您的应用程序不“活跃”,因此无法测量实际当前时间。
Since you're looking for an ultra-accurate time stamp value (as opposed to just timing an arbitrary duration), the Stopwatch
class by itself will not do what you need. I think you would have to do this yourself with a sort of DateTime
/Stopwatch
hybrid. When your application starts, you would store the current DateTime.UtcNow
value (i.e. the crude-resolution time when your application starts) and then also start a Stopwatch
object, like this:
由于您正在寻找一个超准确的时间戳值(而不是仅仅对任意持续时间进行计时),因此Stopwatch
该类本身不会满足您的需求。我认为你必须自己用一种DateTime
/Stopwatch
混合来做到这一点。当您的应用程序启动时,您将存储当前DateTime.UtcNow
值(即应用程序启动时的粗解析时间),然后还启动一个Stopwatch
对象,如下所示:
DateTime _starttime = DateTime.UtcNow;
Stopwatch _stopwatch = Stopwatch.StartNew();
Then, whenever you need a high-resolution DateTime
value, you would get it like this:
然后,每当您需要高分辨率DateTime
值时,您会得到这样的结果:
DateTime highresDT = _starttime.AddTicks(_stopwatch.Elapsed.Ticks);
You also may want to periodically reset _starttime and _stopwatch, to keep the resulting time from getting too far out of sync with the system time (although I'm not sure this would actually happen, and it would take a long time to happen anyway).
您可能还想定期重置 _starttime 和 _stopwatch,以防止结果时间与系统时间太不同步(尽管我不确定这是否会真正发生,而且无论如何都需要很长时间才能发生) .
Update: since it appears that Stopwatch doesget out of sync with the system time (by as much as half a second per hour), I think it makes sense to reset the hybrid DateTime
class based on the amount of time that passes between calls to check the time:
更新:由于看起来秒表确实与系统时间不同步(每小时最多半秒),我认为DateTime
根据调用之间经过的时间来重置混合类是有意义的时间:
public class HiResDateTime
{
private static DateTime _startTime;
private static Stopwatch _stopWatch = null;
private static TimeSpan _maxIdle =
TimeSpan.FromSeconds(10);
public static DateTime UtcNow
{
get
{
if ((_stopWatch == null) ||
(_startTime.Add(_maxIdle) < DateTime.UtcNow))
{
Reset();
}
return _startTime.AddTicks(_stopWatch.Elapsed.Ticks);
}
}
private static void Reset()
{
_startTime = DateTime.UtcNow;
_stopWatch = Stopwatch.StartNew();
}
}
If you reset the hybrid timer at some regular interval (say every hour or something), you run the risk of setting the time back before the last read time, kind of like a miniature Daylight Savings Time problem.
如果您以固定的时间间隔(例如每小时或其他时间)重置混合计时器,则可能会在上次读取时间之前将时间设置回原点,这有点像小型夏令时问题。
回答by Jason Kresowaty
It does return the most accurate date and time known to the operating system.
它确实返回操作系统已知的最准确的日期和时间。
The operating system also provides higher resolution timing through QueryPerformanceCounter
and QueryPerformanceFrequency
(.NET Stopwatch
class). These let you time an interval but do not give you date and time of day. You might argue that these would be able to give you a very accurate time and day, but I am not sure how badly they skew over a long interval.
操作系统还通过QueryPerformanceCounter
和QueryPerformanceFrequency
(.NETStopwatch
类)提供更高分辨率的计时。这些让您计时一个间隔,但不给您日期和时间。您可能会争辩说,这些可以为您提供非常准确的时间和日期,但我不确定它们在很长一段时间内的偏差有多严重。
回答by Henk Holterman
The 15 ms (actually it can be 15-25 ms) accuracy is based on the Windows 55 Hz/65 Hz timer resolution. This is also the basic TimeSlice period. Affected are Windows.Forms.Timer, Threading.Thread.Sleep, Threading.Timer, etc.
15 毫秒(实际上可以是 15-25 毫秒)精度是基于 Windows 55 Hz/65 Hz 计时器分辨率。这也是基本的 TimeSlice 周期。受影响的是Windows.Forms.Timer、Threading.Thread.Sleep、Threading.Timer等。
To get accurate intervals you should use the Stopwatch class. It will use high-resolution if available. Try the following statements to find out:
要获得准确的间隔,您应该使用Stopwatch 类。如果可用,它将使用高分辨率。请尝试以下语句以找出答案:
Console.WriteLine("H = {0}", System.Diagnostics.Stopwatch.IsHighResolution);
Console.WriteLine("F = {0}", System.Diagnostics.Stopwatch.Frequency);
Console.WriteLine("R = {0}", 1.0 /System.Diagnostics.Stopwatch.Frequency);
I get R=6E-08 sec, or 60 ns. It should suffice for your purpose.
我得到 R=6E-08 秒,或 60 ns。它应该足以满足您的目的。
回答by Piotr Czapla
回答by Peter Mortensen
1). If you need high resolution absolute accuracy: you can't use DateTime.Now
when it is based on a clock with a 15 ms interval (unless it
is possible "slide" the phase).
1)。如果您需要高分辨率绝对精度:DateTime.Now
当它基于间隔为 15 ms 的时钟时不能使用(除非可能“滑动”相位)。
Instead, an external source of better resolution absolute
accuracy time (e.g. ntp), t1
below, could be combined with the high
resolution timer (StopWatch / QueryPerformanceCounter).
取而代之的是t1
,可以将以下分辨率更高的绝对准确度时间(例如 ntp)的外部源与高分辨率计时器(StopWatch / QueryPerformanceCounter)结合使用。
2). If you just need high resolution:
2)。如果您只需要高分辨率:
Sample DateTime.Now
(t1
) once together with a value from the
high resolution timer (StopWatch / QueryPerformanceCounter)
(tt0
).
与来自高分辨率计时器 (StopWatch / QueryPerformanceCounter) ( )的值一起采样DateTime.Now
( t1
) 一次tt0
。
If the current value of the high resolution timer is tt
then the
current time, t
, is:
如果高分辨率计时器的当前值为tt
当前时间t
,则为:
t = t1 + (tt - tt0)
3). An alternative could be to disentangle absolute time and order of the financial events: one value for absolute time (15 ms resolution, possibly off by several minutes) and one value for the order (for example, incrementing a value by one each time and store that). The start value for the order can be based on some system global value or be derived from the absolute time when the application starts.
3)。另一种方法是解开金融事件的绝对时间和顺序:一个绝对时间值(15 毫秒分辨率,可能相差几分钟)和一个顺序值(例如,每次将一个值增加一并存储那)。订单的起始值可以基于某个系统全局值或从应用程序启动时的绝对时间导出。
This solution would be more robust as it is not dependent on the underlying hardware implementation of the clocks/timers (that may vary betweens systems).
该解决方案将更加健壮,因为它不依赖于时钟/定时器的底层硬件实现(可能因系统而异)。
回答by Nathaniel
This is too much work for something so simple. Just insert a DateTime in your database with the trade. Then to obtain trade order use your identity column which should be an incrementing value.
对于这么简单的事情来说,这工作量太大了。只需在您的数据库中插入一个 DateTime 进行交易。然后使用您的身份列获取交易订单,该列应该是一个递增值。
If you are inserting into multiple databases and trying to reconcile after the fact then you will be mis-calculating trade order due to any slight variance in your database times (even ns increments as you said)
如果您插入多个数据库并试图在事后进行协调,那么您将由于数据库时间的任何轻微差异而错误计算交易订单(即使是您所说的 ns 增量)
To solve the multiple database issue you could expose a single service that each trade hits to get an order. The service could return a DateTime.UtcNow.Ticks and an incrementing trade number.
要解决多数据库问题,您可以公开每个交易点击的单个服务以获取订单。该服务可以返回 DateTime.UtcNow.Ticks 和递增的交易编号。
Even using one of the solutions above anyone conducting trades from a location on the network with more latency could possibly place trades first (real world), but they get entered in the wrong order due to latency. Because of this the trade must be considered placed at the database, not at users' consoles.
即使使用上述解决方案之一,从网络上具有更多延迟的位置进行交易的任何人都可能首先进行交易(现实世界),但由于延迟,它们以错误的顺序进入。因此,交易必须被视为放置在数据库中,而不是在用户的控制台中。
回答by Ian Mercer
[The accepted answer does not appear to be thread safe, and by its own admission can go backwards in time causing duplicate timestamps, hence this alternative answer]
[接受的答案似乎不是线程安全的,并且它自己承认可以及时倒退导致重复的时间戳,因此这个替代答案]
If what you really care about (per your comment) is in fact, a unique timestamp that is allocated in strict ascending order and which corresponds as closely as possible to the system time, you could try this alternative approach:
如果您真正关心的(根据您的评论)实际上是一个按严格升序分配且尽可能与系统时间对应的唯一时间戳,您可以尝试以下替代方法:
public class HiResDateTime
{
private static long lastTimeStamp = DateTime.UtcNow.Ticks;
public static long UtcNowTicks
{
get
{
long orig, newval;
do
{
orig = lastTimeStamp;
long now = DateTime.UtcNow.Ticks;
newval = Math.Max(now, orig + 1);
} while (Interlocked.CompareExchange
(ref lastTimeStamp, newval, orig) != orig);
return newval;
}
}
}
回答by user3091460
I'd add the following regarding MusiGenesisAnswer for the re-sync timing.
我会添加以下关于重新同步时间的MusiGenesis答案。
Meaning: What time should I use to re-sync ( the _maxIdle
in MusiGenesisanswer's)
含义:我应该使用重新同步什么时间(_maxIdle
在MusiGenesis答案的)
You know that with this solution you are not perfectly accurate, thats why you re-sync. But also what you implicitly want is the same thing as Ian Mercersolution's:
您知道使用此解决方案您并不完全准确,这就是您重新同步的原因。但你隐含想要的是与Ian Mercer解决方案相同的东西:
a unique timestamp that is allocated in strict ascending order
按严格升序分配的唯一时间戳
Therefore the amount of time between two re-sync ( _maxIdle
Lets call it SyncTime) should be function of 4 things:
因此,两次重新同步之间的时间量(_maxIdle
我们称之为SyncTime)应该是 4 件事的函数:
- the
DateTime.UtcNow
resolution - the ratio of accuracy you want
- the precision level you want
- An estimation of the out-of-sync ratio of your machine
- 该
DateTime.UtcNow
决议 - 你想要的准确率
- 你想要的精度水平
- 估计您机器的不同步率
Obviously the first constraint on this variable would be :
显然,这个变量的第一个约束是:
out-of-sync ratio <= accuracy ratio
不同步率 <= 准确率
For example : I dont want my accuracy to be worst than 0.5s/hrs or 1ms/day etc... (in bad English: I dont want to be more wrong than 0.5s/hrs=12s/day).
例如:我不希望我的准确度低于 0.5s/hrs 或 1ms/day 等......(英语不好:我不想比 0.5s/hrs=12s/day 更糟糕)。
So you cannot achieve a better accuracy than what the Stopwatch offer you on your PC. It depends on your out-of-sync ratio, which might not be constant.
因此,您无法获得比秒表在您的 PC 上为您提供的精度更高的精度。这取决于您的不同步比率,该比率可能不是恒定的。
Another constraint is the minimum time between two resync:
另一个限制是两次重新同步之间的最短时间:
Synctime >= DateTime.UtcNow
resolution
同步时间 >=DateTime.UtcNow
分辨率
Here accuracy and precision are linked because if you using a high precision (for example to store in a DB) but a lower accuracy, You might break Ian Mercer statementthat is the strict ascending order.
这里准确性和精度是相关联的,因为如果您使用高精度(例如存储在数据库中)但精度较低,您可能会破坏严格升序的Ian Mercer 语句。
Note: It seems DateTime.UtcNow may have a bigger default Res than 15ms (1ms on my machine)Follow the link: High accuracy DateTime.UtcNow
注意:似乎 DateTime.UtcNow 的默认分辨率可能大于 15ms(在我的机器上为 1ms)点击链接: High precision DateTime.UtcNow
Let's take an example:
让我们举个例子:
Imagine the out-of-sync ratio commented above.
想象一下上面评论的不同步比率。
After about 10 hours, it was ahead by 5 seconds.
大约10个小时后,它领先了5秒。
Say I want a microsec precision. My timer res is 1ms (see above Note)
假设我想要微秒精度。我的计时器 res 是 1ms (见上文注)
So point by point:
所以一点一点:
- the
DateTime.UtcNow
resolution : 1ms - accuracy ratio >= out-of-sync ratio, lets take the most accurate possible so : accuracy ratio = out-of-sync ratio
- the precision level you want : 1 microsec
- An estimation of the out-of-sync ratio of your machine : 0.5s/hour(this is also my accuracy)
- 在
DateTime.UtcNow
分辨率:1毫秒 - 准确率 >= 不同步率,让我们尽可能准确:准确率 = 不同步率
- 您想要的精度级别:1 微秒
- 估计你机器的不同步率:0.5s/小时(这也是我的准确率)
If you reset every 10s, imagine your at 9.999s, 1ms before reset. Here you make a call during this interval. The time your function will plot is ahead by : 0.5/3600*9.999s eq 1.39ms. You would display a time of 10.000390sec. After UtcNow tick, if you make a call within the 390micro sec, your will have a number inferior to the previous one. Its worse if this out-of-sync ratio is random depending on CPU Load or other things.
如果你每 10 秒重置一次,想象一下你在 9.999 秒,重置前 1 毫秒。您可以在此时间间隔内拨打电话。您的函数将绘制的时间提前:0.5/3600*9.999s eq 1.39ms。您将显示 10.000390 秒的时间。在 UtcNow 勾选后,如果您在 390 微秒内拨打电话,您的号码将低于前一个。如果这种不同步比率是随机的,取决于 CPU 负载或其他因素,那就更糟了。
Now let's say I put SyncTimeat its minimum value > I resync every 1ms
现在假设我将SyncTime 设为最小值 > 我每 1 毫秒重新同步一次
Doing the same thinking would put me Ahead of time by 0.139 microsec < inferior to the precision I want. Therefore if I call the function at 9.999 ms, so 1microsec before reset I will plot 9.999. And just after I will plot 10.000. I will have a good order.
做同样的想法会让我提前 0.139 微秒 < 低于我想要的精度。因此,如果我在 9.999 毫秒调用该函数,那么在重置前 1 微秒我将绘制 9.999。就在我将绘制 10.000 之后。我会有一个很好的订单。
So here the other constraint is : accuracy-ratio x SyncTime < precision level , lets say to be sure because number can be rounded up that accuracy-ratio x SyncTime < precision level/2is good.
所以这里的另一个约束是:accuracy-ratio x SyncTime < precision level ,可以说是肯定的,因为数字可以四舍五入,accuracy-ratio x SyncTime < precision level/2是好的。
The issue is resolved.
问题已解决。
So a Quick recap would be :
所以快速回顾一下:
- Retrieve your timer resolution.
- Compute an estimate of the out-of-sync ratio.
- accuracy ratio >= out-of-sync ratio estimate , Best accuracy = out-of-sync ratio
- Choose your Precision Level considering the following:
- timer-resolution <= SyncTime <= PrecisionLevel/(2*accuracy-ratio)
- The best Precision you can achieve is timer-resolution*2*out-of-sync ratio
- 检索您的计时器分辨率。
- 计算不同步比率的估计值。
- 准确率 >= 不同步率估计,最佳准确率 = 不同步率
- 考虑以下因素选择您的精度级别:
- 定时器分辨率 <= SyncTime <= PrecisionLevel/(2*accuracy-ratio)
- 您可以达到的最佳精度是计时器分辨率*2*不同步比率
For the above ratio (0.5/hr) the correct SyncTime would be 3.6ms, so rounded down to 3ms.
对于上述比率 (0.5/hr),正确的 SyncTime 将是 3.6ms,因此四舍五入为3ms。
With the above ratio and the timer resolution of 1ms. If you want a one-tick Precision level(0.1microsec), you need an out-of-sync ratio of no more than : 180ms/hour.
具有上述比例和1ms的定时器分辨率。如果您想要一键式精度级别(0.1 微秒),则需要不超过180 毫秒/小时的不同步率。
In its last answer to its own answer MusiGenesisstate:
在对自己的回答MusiGenesis 的最后一个回答中:
@Hermann: I've been running a similar test for the last two hours (without the reset correction), and the Stopwatch-based timer is only running about 400 ms ahead after 2 hours, so the skew itself appears to be variable (but still pretty severe). I'm pretty surprised that the skew is this bad; I guess this is why Stopwatch is in System.Diagnostics. – MusiGenesis
@Hermann:过去两个小时我一直在运行类似的测试(没有重置校正),基于秒表的计时器在 2 小时后仅提前 400 毫秒运行,因此偏斜本身似乎是可变的(但是还是挺严重的)。我很惊讶偏斜这么严重;我想这就是为什么秒表在 System.Diagnostics 中的原因。– 音乐创世纪
So the Stopwatch accuracy is close to 200ms/hour, almost our 180ms/hour. Is there any link to why our number and this number are so close ? Dont know. But this accuracy is enough for us to achieve Tick-Precision
所以秒表的精度接近 200 毫秒/小时,几乎是我们的 180 毫秒/小时。是否有任何链接可以说明为什么我们的号码和这个号码如此接近?不知道。但是这个精度足以让我们达到Tick-Precision
The Best PrecisionLevel: For the example above it is 0.27 microsec.
最佳精度水平:对于上面的例子,它是0.27 微秒。
However what happen if I call it multiple times between 9.999ms and the re-sync.
但是,如果我在 9.999 毫秒和重新同步之间多次调用它会发生什么。
2 calls to the function could end-up with the same TimeStamp being returned the time would be 9.999 for both (as I dont see more precision). To circumvent this, you cannot touch the precision level because it is Linked to SyncTime by the above relation. So you should implement Ian Mercersolution's for those case.
对该函数的 2 次调用可能最终返回相同的时间戳,两者的时间都是 9.999(因为我没有看到更精确的)。为了避免这种情况,您不能触及精度级别,因为它通过上述关系链接到 SyncTime。因此,您应该针对这些情况实施Ian Mercer解决方案。
Please don't hesitate to comment my answer.
请不要犹豫,评论我的回答。
回答by Jamezor
These suggestions all look too hard! If you're on Windows 8 or Server 2012 or higher, use GetSystemTimePreciseAsFileTime
as follows:
这些建议看起来都太难了!如果您使用的是 Windows 8 或 Server 2012 或更高版本,请GetSystemTimePreciseAsFileTime
按如下方式使用:
[DllImport("Kernel32.dll", CallingConvention = CallingConvention.Winapi)]
static extern void GetSystemTimePreciseAsFileTime(out long filetime);
public DateTimeOffset GetNow()
{
long fileTime;
GetSystemTimePreciseAsFileTime(out fileTime);
return DateTimeOffset.FromFileTime(fileTime);
}
This has much, much better accuracy than DateTime.Now
without any effort.
这比DateTime.Now
不费吹灰之力要好得多。
See MSDN for more info: http://msdn.microsoft.com/en-us/library/windows/desktop/hh706895(v=vs.85).aspx
有关详细信息,请参阅 MSDN:http: //msdn.microsoft.com/en-us/library/windows/desktop/hh706895(v=vs.85).aspx
回答by Marcus.D
To get a high-resolution tick-count, please, use the static Stopwatch.GetTimestamp()-method:
要获得高分辨率滴答计数,请使用静态 Stopwatch.GetTimestamp() 方法:
long tickCount = System.Diagnostics.Stopwatch.GetTimestamp();
DateTime highResDateTime = new DateTime(tickCount);
just take a look at the .NET Source Code:
看看 .NET 源代码:
public static long GetTimestamp() {
if(IsHighResolution) {
long timestamp = 0;
SafeNativeMethods.QueryPerformanceCounter(out timestamp);
return timestamp;
}
else {
return DateTime.UtcNow.Ticks;
}
}
Source Code here: http://referencesource.microsoft.com/#System/services/monitoring/system/diagnosticts/Stopwatch.cs,69c6c3137e12dab4
源代码在这里:http: //referencesource.microsoft.com/#System/services/monitoring/system/diagnosticts/Stopwatch.cs,69c6c3137e12dab4