Big*_*Bug 1 performance assembly execution instructions
您如何计算指令的执行时间?是否仅通过检查芯片制造商在完成一个动作可能需要多少个时钟周期之后说的话就可以完成?我还有其他应该知道的吗?感觉我想念一些东西...
据我所知,RDTSC指令非常准确。
我认为,如果您要查找确切的周期数,那么在较短的可提升部分的情况下,您可能会遇到Mysticial提到的同时性问题...
但是,如果超-超-超高精度不是一个障碍...也就是说,如果您能够生存下来,并且知道在某些情况下您的结果是......我不知道...说9到80个周期...那么我敢肯定,使用RDTSC还是可以得到非常准确的结果...尤其是当人们认为9到80除以32亿是一个很小的数字时:)
数字9和80是任意选择的(也许您也没有以3.2GHz的CPU速度运行),因为我不知道确切的错误量是多少...但是我很确定它在那个范围内:)
这是我使用的定时器功能的RDTSC摘录:
//High-Rez Setup
__asm
{
push eax
push edx
rdtsc
mov [AbsoluteLow],eax
mov [AbsoluteHigh],edx
pop edx
pop eax
}
Run Code Online (Sandbox Code Playgroud)
实际上,我将继续介绍整个过程……此代码假定类型“ double”是一个64位浮点数……这可能不是通用的编译器/体系结构假设:
double AbsoluteTime;
double AbsoluteResolution;
ulong AbsoluteLow;
ulong AbsoluteHigh;
void Get_AbsoluteTime (double *time)
{
//Variables
double current, constant;
double lower, upper;
ulong timelow, timehigh;
//Use the Intel RDTSC
__asm
{
push eax
push edx
rdtsc
sub eax, [AbsoluteLow]
sbb edx, [AbsoluteHigh]
mov [timelow], eax
mov [timehigh], edx
pop edx
pop eax
}
//Convert two 32bit registers to a 64-bit floating point
//Multiplying by 4294967296 is similar to left-shifting by 32 bits
constant = 4294967296.0;
lower = (double) timelow;
upper = (double) timehigh;
upper *= constant;
current = lower + upper;
current /= AbsoluteResolution;
current += AbsoluteTime;
*time = current;
}
void Set_AbsoluteTime (double time, double scale)
{
//Variables
double invScale;
//Setup
AbsoluteTime = time;
//High-Rez Setup
__asm
{
push eax
push edx
rdtsc
mov [AbsoluteLow],eax
mov [AbsoluteHigh],edx
pop edx
pop eax
}
//Fetch MHZ
if (1)
{
//Local Variables
int nv;
ulong mhz;
char keyname[2048];
//Default assumption of 3.2ghz if registry functions fail
mhz = 3200;
//Registry Key
sprintf (keyname, "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0");
nv = Reg_Get_ValueDW (keyname, "~MHz", (ulong *)&mhz);
//Transform into cycles per second
mhz *= 1000000;
//Calculate Speed Stuff
AbsoluteResolution = (double) mhz;
invScale = 1.0;
invScale /= scale;
AbsoluteResolution *= invScale;
}
}
Run Code Online (Sandbox Code Playgroud)
您想在使用Get函数之前在某个地方调用Set_AbsoluteTime ...如果没有第一次对Set的首次调用,则Gets将返回错误的结果...但是一旦进行一次一次性调用,您就可以使用...
这是一个例子:
void Function_to_Profile (void)
{
//Variables
double t1, t2, TimeElapsed;
//Profile operations
Get_AbsoluteTime (&t1);
...do stuff here...
Get_AbsoluteTime (&t2);
//Calculate Elapsed Time
TimeElapsed = (t2 - t1);
//Feedback
printf ("This function took %.11f seconds to run\n", TimeElapsed);
}
void main (void)
{
Set_AbsoluteTime (0.000, 1.000);
Function_to_Profile();
}
Run Code Online (Sandbox Code Playgroud)
如果出于某种原因,您希望时间测量以半速倒流(对于游戏编程可能很方便),则初始调用为:Set_AbsoluteTime(0.000,-0.500);
Set的第一个参数是添加到所有结果中的基准时间
我敢肯定,这些功能比目前公开使用的最高分辨率的Windows API计时器更准确...我认为在快速处理器上,它们的误差小于1纳秒,但我不确定100%: )
对于我的目的,它们足够准确,但是请注意,40个前导字节的标准初始化(由“当前”,“常量”,“较低”,“较高”,“ timelow”,“ timehigh”组成)大多数C编译器会设置为0xCC或0xCD会占用一些周期...在每个Get_AbsoluteTime调用的底部执行的数学运算也会...
因此,对于真正原始的准确性,最好是将任何要在RDTSC“内联”中配置的内容都加以构架...我将利用扩展的x64寄存器来存储答案供以后的减法操作,而不是搞乱较慢的内存访问...
例如,像这样的东西...顺便说一下,这主要是概念,因为从技术上讲,VC2010不允许您通过__asm关键字发出x64-Assembly :( ...但是我认为它将为您提供概念性的途径去旅行:
typedef unsigned long long ulonglong;
ulonglong Cycles;
__asm
{
push rax
push rdx
rdtsc
mov r9, edx
shl r9, 32
and rax, 0xFFFFFFFF
or r9, rax
pop rdx
pop rax
}
...Perform stuff to profile here
__asm
{
push rax
push rdx
rdtsc
mov r10, edx
shl r10, 32
and rax, 0xFFFFFFFF
or r10, rax
sub r10, r9
mov qword ptr [Cycles], r10
pop rdx
pop rax
}
printf ("The code took %s cycles to execute\n", ULONGLONG_TO_STRING (Cycles));
Run Code Online (Sandbox Code Playgroud)
使用该代码,我认为经过的周期数的最终答案将是在r10中(一个64位寄存器...)或在一个周期中(一个64位无符号整数)在...中,只有少数几个由移位引起的错误周期和堆栈操作...只要所分析的代码不会撕碎r9和r10,嘿...我忘记了最稳定的Extended-x64寄存器是什么...
同样,“ and rax,0xFFFFFFFF”可能是多余的,因为我不记得RDTSC是否将RAX的高32位清零...所以我包括了AND操作,以防万一:)
| 归档时间: |
|
| 查看次数: |
3357 次 |
| 最近记录: |