Raymond Chen 2007 年 7 月 5 日
这篇文章引起了我的注意:
我需要在我的应用程序中生成一些唯一的数字。 我可以使用GUID,但它对我来说太大了 (我需要保留许多唯一的标识符)。我发现了类似这样的东西:
[System.Runtime.InteropServices.DllImport("Kernel32.dll")] private static extern int QueryPerformanceFrequency(ref System.Int64 frequency); [System.Runtime.InteropServices.DllImport("Kernel32.dll")] private static extern int QueryPerformanceCounter(ref System.Int64 performanceCount); public static long GenerateUniqueId() { System.Int64 id = 0; QueryPerformanceFrequency(ref id); QueryPerformanceCounter(ref id); return id; }
这段代码生成Int64(长整型)唯一数字(至少我希望它是唯一的)。 唯一性是在进程范围内的。 因此,两个进程可能生成相同的数字, 但它在单个进程中应该是唯一的 (我不确定两个线程调用同一个GenerateUniqueId()方法时会怎样。
QueryPerformanceCounter
检索高精度性能计数器的当前值,但不能保证每次调用该函数都会返回不同的数字。
高精度性能计数器的频率由硬件抽象层(HAL)确定。
你可能会认为 RDTSC
指令非常适合这个目的,因为它返回CPU时钟周期的数量,这个值总是以非常高的速率递增。
但是 RDTSC
存在许多问题。
例如,变速处理器意味着CPU时钟流逝的速率随时间变化。
当计算机使用壁挂式电源运行时,一百万次时钟跳动可能只需要一毫秒,而使用电池供电时则需要两毫秒。
如果HAL不能使用 RDTSC
,它会使用什么替代方案呢?
正如我所说,这取决于HAL找到合适的替代方案。
旧的主板必须使用每秒大约1,193,182个滴答的可编程间隔计时器(大约每滴答0.8微秒)。
新的主板可以使用每秒大约3,579,545个滴答的ACPI计时器(大约每滴答0.3微秒)。
我的办公室里的一台机器使用ACPI计时器作为其高精度性能计数器,所以我快速编写了一个程序,看看我通过连续调用 QueryPerformanceCounter
能否接近赶上ACPI计时器。
在一台1.80GHz的处理器上,计算机能够足够快地调用 QueryPerformanceCounter
,以至于连续调用之间只有ACPI计时器的四个滴答流逝。
我们几乎可以连续调用 QueryPerformanceCounter
两次并从ACPI计时器得到相同的值。
当然,如果计算机使用的是可编程间隔计时器,它就已经非常接近了,升级到3GHz的处理器就会让我们超过这个极限。
换句话说,你今天可能很幸运,因为你的 CPU 还没有快到可以调用两次 QueryPerformanceCounter 并得到相同的返回值,但看起来我们确实有可能很快达到这个目标。
然而,所有这些粗略的计算都是多余的。你只需要一台有多个处理器的机器。让两个处理器同时(或几乎同时)调用 QueryPerformanceCounter
,它们就会得到相同的计时器值。
如果你想生成唯一的64位值,你可以直接使用 InterlockedIncrement64
。