像CPUz这样的程序非常擅长提供有关系统的深度信息(总线速度,内存时序等)
但是,是否有一种编程方式来计算每个核心(以及每个处理器,在每个CPU具有多个核心的多处理器系统中)频率,而无需处理CPU特定信息 .
我正在尝试开发一种反作弊工具(用于时钟限制基准测试),它能够在基准测试期间记录系统中所有活动核心(所有处理器)的CPU时钟 .
我会在这里扩展我的评论 . 这对我来说太大了,不适合评论 .
你要做的事情非常困难 - 由于以下原因导致不切实际:
没有可移植的方法来获得处理器频率 . rdtsc 确实 NOT 由于SpeedStep和Turbo Boost等效果而始终提供正确的频率 .
rdtsc
所有已知的测量频率的方法都需要准确测量时间 . 但是,确定的骗子可以篡改系统中的所有时钟和计时器 .
准确读取处理器频率以及防篡改方式的时间将需要内核级访问 . 这意味着Windows的驱动程序签名 .
There's no portable way to get the processor frequency:
获取CPU频率的方法是两次调用rdtsc,其间有固定的持续时间 . 然后除去差异将给你频率 .
问题是 rdtsc 没有给出处理器的真实频率 . 由于游戏等实时应用程序依赖于它,因此需要通过CPU限制和Turbo Boost保持一致 . 因此,一旦你的系统启动, rdtsc 将始终以相同的速率运行(除非你开始使用SetFSB或其他东西搞乱总线速度) .
例如,在我的Core i7 2600K上, rdtsc 将始终显示 3.4 GHz 的频率 . 但实际上,它通过超频的Turbo Boost乘法器 46x 在 1.6 GHz 空闲时加载 4.6 GHz .
3.4 GHz
46x
1.6 GHz
4.6 GHz
但是一旦你找到了衡量真实频率的方法,(或者你对 rdtsc 感到满意),你就可以使用thread-affinities轻松获得每个核心的频率 .
Getting the True Frequency:
要获得处理器的真实频率,您需要访问MSR(特定于型号的寄存器)或硬件性能计数器 .
这些是内核级指令,因此需要使用驱动程序 . 如果您在Windows中尝试进行此分发,则需要通过正确的驱动程序签名协议 . 此外,代码因处理器品牌和型号而异,因此每个处理器代需要不同的检测代码 .
一旦进入这个阶段,有多种方法可以读取频率 .
在Intel处理器上,硬件计数器可让您计算原始CPU周期 . 结合精确测量实时的方法(下一节),您可以计算真实频率 . MSR使您可以访问其他信息,例如CPU倍频器 .
All known methods to measure frequency require an accurate measurement of time:
这可能是更大的问题 . 您需要一个计时器才能测量频率 . 一个有能力的黑客将能够篡改您可以在C / C中使用的所有时钟 . 这包括以下所有内容:
clock()
gettimeofday()
QueryPerformanceCounter()
等......
这个清单一直在继续 . 换句话说,你不能相信任何计时器,因为一个有能力的黑客将能够欺骗所有这些计时器 . 例如,可以通过直接在OS中更改系统时钟来愚弄 clock() 和 gettimeofday() . 愚弄 QueryPerformanceCounter() 更难 .
Getting a True Measurement of Time:
上面列出的所有时钟都是易受攻击的,因为它们通常以某种方式从相同的系统基本时钟导出 . 并且该系统基本时钟通常与系统基准时钟相关联 - 在系统已经通过超频实用程序启动后可以更改 .
因此,获得可靠且防篡改的时间测量的唯一方法是读取外部时钟,例如HPET或ACPI . 不幸的是,这些似乎也需要内核级访问 .
To Summarize:
构建任何类型的防篡改基准测试几乎肯定需要编写需要Windows签名的内核模式驱动程序 . 对于休闲基准而言,这往往是一个太大的负担作家 .
这导致了防篡改基准的短缺,这可能导致近年来竞争激烈的超频社区整体下滑 .
我意识到这已经得到了解答 . 我也意识到这基本上是一种黑色艺术,所以请把它拿走或留下 - 或提供反馈 .
为了找到受限制(感谢microsft,hp和dell)HyperV主机(不可靠的perf计数器)和HyperV来宾(只能获得库存CPU速度,而不是最新)的时钟速率,我通过试验错误和fluke,创建一个每个时钟只循环一次的循环 .
代码如下 - C#5.0,SharpDev,32位,目标3.5,优化(关键),无debuger活跃(关键)
long frequency, start, stop; double multiplier = 1000 * 1000 * 1000;//nano if (Win32.QueryPerformanceFrequency(out frequency) == false) throw new Win32Exception(); Process.GetCurrentProcess().ProcessorAffinity = new IntPtr(1); const int gigahertz= 1000*1000*1000; const int known_instructions_per_loop = 1; int iterations = int.MaxValue; int g = 0; Win32.QueryPerformanceCounter(out start); for( i = 0; i < iterations; i++) { g++; g++; g++; g++; } Win32.QueryPerformanceCounter(out stop); //normal ticks differs from the WMI data, i.e 3125, when WMI 3201, and CPUZ 3199 var normal_ticks_per_second = frequency * 1000; var ticks = (double)(stop - start); var time = (ticks * multiplier) /frequency; var loops_per_sec = iterations / (time/multiplier); var instructions_per_loop = normal_ticks_per_second / loops_per_sec; var ratio = (instructions_per_loop / known_instructions_per_loop); var actual_freq = normal_ticks_per_second / ratio; Console.WriteLine( String.Format("Perf counhter freq: {0:n}", normal_ticks_per_second)); Console.WriteLine( String.Format("Loops per sec: {0:n}", loops_per_sec)); Console.WriteLine( String.Format("Perf counter freq div loops per sec: {0:n}", instructions_per_loop)); Console.WriteLine( String.Format("Presumed freq: {0:n}", actual_freq)); Console.WriteLine( String.Format("ratio: {0:n}", ratio));
笔记
如果调试器处于活动状态,则每个循环
25条指令
考虑事先运行2或3秒循环来启动处理器(或至少尝试启动,了解这些天服务器受到多大程度的限制)
在64位Core2和Haswell Pentium上测试,并与CPU-Z进行比较
我之前发布过这个主题(以及一个基本算法):here . 据我所知,算法(参见讨论)非常准确 . 例如,Windows 7将我的CPU时钟报告为2.00 GHz,CPU-Z报告为1994-1996 MHz,我的算法报告为1995025-1995075 kHz .
该算法执行大量循环来执行此操作,这会导致CPU频率增加到最大值(因为它也将在基准测试期间),因此速度限制软件将无法发挥作用 .
其他信息here和here .
关于速度限制的问题,除非应用程序使用速度值来确定经过的时间并且时间本身非常重要,否则我真的不认为这是一个问题 . 例如,如果一个除法需要x个时钟周期才能完成,那么CPU是以3 GHz还是300 MHz运行并不重要:它仍然需要x个时钟周期,唯一的区别是它将在十分之一内完成除法在@ 3 GHz的时间 .
最简单的方法之一是使用 RDTSC ,但是看到这是反作弊机制,我把它作为内核驱动程序或超级遮阳板常驻代码 .
RDTSC
您可能还需要滚动自己的计时代码**,这也可以通过 RDTSC 完成(下面的示例中使用的QPC使用 RDTSC ,实际上它非常简单,可以进行逆向工程并使用本地副本,其中意味着篡改它,你需要篡改你的司机) .
void GetProcessorSpeed() { CPUInfo* pInfo = this; LARGE_INTEGER qwWait, qwStart, qwCurrent; QueryPerformanceCounter(&qwStart); QueryPerformanceFrequency(&qwWait); qwWait.QuadPart >>= 5; unsigned __int64 Start = __rdtsc(); do { QueryPerformanceCounter(&qwCurrent); }while(qwCurrent.QuadPart - qwStart.QuadPart < qwWait.QuadPart); pInfo->dCPUSpeedMHz = ((__rdtsc() - Start) << 5) / 1000000.0; }
**我这是为了@Mystical提到的安全性,但由于我从未感受到颠覆低级系统计时机制的冲动,可能会涉及更多,如果Mystical可以添加一些东西会很好:)
人们应该参考这份白皮书:Intel® Turbo Boost Technology in Intel® Core™ Microarchitecture (Nehalem) Based Processors . 基本上,在样本周期T内产生几次UCC固定性能计数器的读数 .
Relative.Freq = Delta(UCC) / T Where: Delta() = UCC @ period T - UCC @ period T-1
从Nehalem架构开始,UCC相对于核心的Unhalted状态增加和减少点击滴答的数量 .
当SpeedStep或Turbo Boost激活时,将相应地测量使用UCC的估计频率;而TSC保持不变 . 例如,Turbo Boost在行动中显示Delta(UCC)大于或等于Delta(TSC)
函数 Core_Cycle 函数中的示例位于Cyring | CoreFreq GitHub .
Core_Cycle
你需要使用CallNtPowerInformation . 这是来自putil项目的code sample . 通过这种方式,您可以获得当前和最大CPU频率 . 据我所知,不可能获得每CPU频率 .
6 回答
我会在这里扩展我的评论 . 这对我来说太大了,不适合评论 .
你要做的事情非常困难 - 由于以下原因导致不切实际:
没有可移植的方法来获得处理器频率 .
rdtsc
确实 NOT 由于SpeedStep和Turbo Boost等效果而始终提供正确的频率 .所有已知的测量频率的方法都需要准确测量时间 . 但是,确定的骗子可以篡改系统中的所有时钟和计时器 .
准确读取处理器频率以及防篡改方式的时间将需要内核级访问 . 这意味着Windows的驱动程序签名 .
There's no portable way to get the processor frequency:
获取CPU频率的方法是两次调用rdtsc,其间有固定的持续时间 . 然后除去差异将给你频率 .
问题是
rdtsc
没有给出处理器的真实频率 . 由于游戏等实时应用程序依赖于它,因此需要通过CPU限制和Turbo Boost保持一致 . 因此,一旦你的系统启动,rdtsc
将始终以相同的速率运行(除非你开始使用SetFSB或其他东西搞乱总线速度) .例如,在我的Core i7 2600K上,
rdtsc
将始终显示3.4 GHz
的频率 . 但实际上,它通过超频的Turbo Boost乘法器46x
在1.6 GHz
空闲时加载4.6 GHz
.但是一旦你找到了衡量真实频率的方法,(或者你对
rdtsc
感到满意),你就可以使用thread-affinities轻松获得每个核心的频率 .Getting the True Frequency:
要获得处理器的真实频率,您需要访问MSR(特定于型号的寄存器)或硬件性能计数器 .
这些是内核级指令,因此需要使用驱动程序 . 如果您在Windows中尝试进行此分发,则需要通过正确的驱动程序签名协议 . 此外,代码因处理器品牌和型号而异,因此每个处理器代需要不同的检测代码 .
一旦进入这个阶段,有多种方法可以读取频率 .
在Intel处理器上,硬件计数器可让您计算原始CPU周期 . 结合精确测量实时的方法(下一节),您可以计算真实频率 . MSR使您可以访问其他信息,例如CPU倍频器 .
All known methods to measure frequency require an accurate measurement of time:
这可能是更大的问题 . 您需要一个计时器才能测量频率 . 一个有能力的黑客将能够篡改您可以在C / C中使用的所有时钟 . 这包括以下所有内容:
clock()
gettimeofday()
QueryPerformanceCounter()
等......
这个清单一直在继续 . 换句话说,你不能相信任何计时器,因为一个有能力的黑客将能够欺骗所有这些计时器 . 例如,可以通过直接在OS中更改系统时钟来愚弄
clock()
和gettimeofday()
. 愚弄QueryPerformanceCounter()
更难 .Getting a True Measurement of Time:
上面列出的所有时钟都是易受攻击的,因为它们通常以某种方式从相同的系统基本时钟导出 . 并且该系统基本时钟通常与系统基准时钟相关联 - 在系统已经通过超频实用程序启动后可以更改 .
因此,获得可靠且防篡改的时间测量的唯一方法是读取外部时钟,例如HPET或ACPI . 不幸的是,这些似乎也需要内核级访问 .
To Summarize:
构建任何类型的防篡改基准测试几乎肯定需要编写需要Windows签名的内核模式驱动程序 . 对于休闲基准而言,这往往是一个太大的负担作家 .
这导致了防篡改基准的短缺,这可能导致近年来竞争激烈的超频社区整体下滑 .
我意识到这已经得到了解答 . 我也意识到这基本上是一种黑色艺术,所以请把它拿走或留下 - 或提供反馈 .
为了找到受限制(感谢microsft,hp和dell)HyperV主机(不可靠的perf计数器)和HyperV来宾(只能获得库存CPU速度,而不是最新)的时钟速率,我通过试验错误和fluke,创建一个每个时钟只循环一次的循环 .
代码如下 - C#5.0,SharpDev,32位,目标3.5,优化(关键),无debuger活跃(关键)
笔记
如果调试器处于活动状态,则每个循环
25条指令
考虑事先运行2或3秒循环来启动处理器(或至少尝试启动,了解这些天服务器受到多大程度的限制)
在64位Core2和Haswell Pentium上测试,并与CPU-Z进行比较
我之前发布过这个主题(以及一个基本算法):here . 据我所知,算法(参见讨论)非常准确 . 例如,Windows 7将我的CPU时钟报告为2.00 GHz,CPU-Z报告为1994-1996 MHz,我的算法报告为1995025-1995075 kHz .
该算法执行大量循环来执行此操作,这会导致CPU频率增加到最大值(因为它也将在基准测试期间),因此速度限制软件将无法发挥作用 .
其他信息here和here .
关于速度限制的问题,除非应用程序使用速度值来确定经过的时间并且时间本身非常重要,否则我真的不认为这是一个问题 . 例如,如果一个除法需要x个时钟周期才能完成,那么CPU是以3 GHz还是300 MHz运行并不重要:它仍然需要x个时钟周期,唯一的区别是它将在十分之一内完成除法在@ 3 GHz的时间 .
最简单的方法之一是使用
RDTSC
,但是看到这是反作弊机制,我把它作为内核驱动程序或超级遮阳板常驻代码 .您可能还需要滚动自己的计时代码**,这也可以通过
RDTSC
完成(下面的示例中使用的QPC使用RDTSC
,实际上它非常简单,可以进行逆向工程并使用本地副本,其中意味着篡改它,你需要篡改你的司机) .**我这是为了@Mystical提到的安全性,但由于我从未感受到颠覆低级系统计时机制的冲动,可能会涉及更多,如果Mystical可以添加一些东西会很好:)
人们应该参考这份白皮书:Intel® Turbo Boost Technology in Intel® Core™ Microarchitecture (Nehalem) Based Processors . 基本上,在样本周期T内产生几次UCC固定性能计数器的读数 .
从Nehalem架构开始,UCC相对于核心的Unhalted状态增加和减少点击滴答的数量 .
当SpeedStep或Turbo Boost激活时,将相应地测量使用UCC的估计频率;而TSC保持不变 . 例如,Turbo Boost在行动中显示Delta(UCC)大于或等于Delta(TSC)
函数
Core_Cycle
函数中的示例位于Cyring | CoreFreq GitHub .你需要使用CallNtPowerInformation . 这是来自putil项目的code sample . 通过这种方式,您可以获得当前和最大CPU频率 . 据我所知,不可能获得每CPU频率 .