我有一个简单的程序,该程序首先将一些本机x86指令写入已声明的缓冲区,然后将函数指针设置为此缓冲区并进行调用。但是,当该缓冲区在堆栈上分配时(而不是在堆上,甚至在全局数据区中),我注意到严重的性能下降。我验证了数据缓冲区中指令序列的开头在16字节边界上(我假设这就是cpu要求(或希望)的状态)。我不知道为什么在执行过程中执行指令会有所不同,但是在下面的程序中,“ GOOD”在双核工作站上执行4秒钟,而“ BAD”则需要6分钟左右。这里是否存在某种对齐/ i缓存/预测问题?我对VTune的评估许可刚刚结束,因此我什至无法对此进行分析:(。谢谢。



#include <stdio.h>
#include <string.h>
#include <stdlib.h>

typedef int (*funcPtrType)(int, int);

int foo(int a, int b) { return a + b; }

void main()
{
  // Instructions in buf are identical to what the compiler generated for "foo".
  char buf[201] = {0x55,
                   0x8b, 0xec,
                   0x8b, 0x45, 0x08,
                   0x03, 0x45, 0x0c,
                   0x5D,
                   0xc3
                  };

  int i;

  funcPtrType ptr;

#ifdef GOOD
  char* heapBuf = (char*)malloc(200);
  printf("Addr of heap buf: %x\n", &heapBuf[0]);
  memcpy(heapBuf, buf, 200);
  ptr = (funcPtrType)(&heapBuf[0]);
#else // BAD
  printf("Addr of local buf: %x\n", &buf[0]);
  ptr = (funcPtrType)(&buf[0]);
#endif

  for (i=0; i < 1000000000; i++)
    ptr(1,2);
}




运行此命令的结果是:

$ cl -DGOOD ne3.cpp
适用于80x86的Microsoft(R)32位C / C ++优化编译器版本11.00.7022
版权所有(C)Microsoft Corp 1984-1997。版权所有。

ne3.cpp
Microsoft(R)32位增量链接器版本5.10.7303
版权所有(C)Microsoft Corp 1992-1997。版权所有。

/out:ne3.exe
ne3.obj
$ time ./ne3
堆缓冲区的地址:410eb0

真正的0m 4.33s
用户0m 4.31s
sys 0m 0.01秒
$
$
$ cl ne3.cpp
适用于80x86的Microsoft(R)32位C / C ++优化编译器版本11.00.7022
版权所有(C)Microsoft Corp 1984-1997。版权所有。

ne3.cpp
Microsoft(R)32位增量链接器版本5.10.7303
版权所有(C)Microsoft Corp 1992-1997。版权所有。

/out:ne3.exe
ne3.obj
$ time ./ne3
本地缓冲区的地址:12feb0

真正的6m41.19s
用户6m40.46s
sys 0m 0.03秒
$

谢谢。


沙桑克

最佳答案

堆栈保护的安全性?

作为一个疯狂的猜测,您可能会遇到基于MMU的堆栈保护方案。许多安全漏洞是基于故意的缓冲区溢出,从而将可执行代码注入到堆栈中。解决这些问题的一种方法是使用不可执行的堆栈。这将导致进入操作系统的陷阱,我认为操作系统或某些病毒软件可能在执行某些操作。

负i-cache相干性交互?

另一种可能性是,同时使用代码和数据访问附近地址会破坏CPU缓存策略。我相信x86实现了本质上是自动的代码/数据一致性模型,该模型很可能导致在任何存储器写操作中大量附近的缓存指令失效。您不能通过将程序更改为不使用堆栈来真正解决此问题(显然,您可以移动动态代码),因为堆栈始终由机器代码编写,例如,每次压入参数或返回地址时过程调用。

相对于DRAM甚至外层高速缓存环而言,CPU的速度确实如此快,因此破坏内部高速缓存环的任何事情都将是非常严重的,加上其实现可能涉及CPU实现中的某种微陷阱,其次是硬件中的“循环”使事物无效。英特尔或AMD不必担心速度,因为对于大多数程序而言,它永远不会发生,并且当它执行时,通常在加载程序后只会发生一次。

07-27 14:07