Performance 如何将我的结构变量放入CPU缓存以消除内存页访问时间?选择权

Performance 如何将我的结构变量放入CPU缓存以消除内存页访问时间?选择权,performance,caching,memory,cpu,Performance,Caching,Memory,Cpu,很明显,没有明确的方式或某些系统调用 帮助程序员将变量放入CPU缓存 但我认为某种编程风格或精心设计 算法可以增加 变量可以缓存到CPU缓存中 以下是我的例子: 我想在数组的末尾附加一个8字节的结构 在全局主内存中声明的相同类型的结构 地区 这一过程持续重复400万次。此过程需要6秒,每次操作需要1.5 us。我认为这个结果表明这两个内存区域没有被缓存 我从a那里得到了一些线索,所以我试了几个 加强这一点的方法。到现在为止,没有增强 我认为一些聪明的代码可以减少运行时间,最多10到100次 时代

很明显,没有明确的方式或某些系统调用 帮助程序员将变量放入CPU缓存

但我认为某种编程风格或精心设计 算法可以增加 变量可以缓存到CPU缓存中

以下是我的例子:

我想在数组的末尾附加一个8字节的结构 在全局主内存中声明的相同类型的结构 地区

这一过程持续重复400万次。此过程需要6秒,每次操作需要1.5 us。我认为这个结果表明这两个内存区域没有被缓存

我从a那里得到了一些线索,所以我试了几个 加强这一点的方法。到现在为止,没有增强

我认为一些聪明的代码可以减少运行时间,最多10到100次 时代。请给我指路

-------------------------------------------------------------------------
附加(2011-04-01)


达蒙~谢谢你的评论

在阅读了你的评论之后,我再次分析了我的代码,发现了一些东西 我错过了。我附加的以下代码是我的原始代码的缩写版本

为了精确测量每个操作的执行时间(在原始代码中,有几种不同类型的操作),我使用
clock\u gettime()
函数插入了时间测量代码。我认为,如果我度量每个操作的执行时间并将它们累加起来,就可以避免主循环带来的额外成本

在原始代码中,时间测量代码被宏函数隐藏,所以我完全忘记了它

此代码的运行时间几乎为6秒。但是如果我去掉主循环中的时间测量函数,它会变成0.1秒

由于
clock_gettime()
函数支持非常高的精度(高达1纳秒),在独立线程的基础上执行,并且需要非常大的结构, 我认为该函数导致缓存超出了执行连续插入的主存区域

再次感谢您的评论。对于进一步的增强,任何建议都将非常有助于我优化代码

我认为分层定义的结构变量可能会导致不必要的时间成本, 但首先我想知道它会是多少,然后再将其更改为更C风格的代码


typedef结构t\u ptr{
uint32岛区:1,isNextLeaf:1,ptr:30;
t_ptr(无效){
isleaf=假;
isNextLeaf=false;
ptr=零;
}
}PTR;
typedef结构t_键{
uint32 op:1,键:31;
t_键(无效){
op=op_-INS;
键=0;
}
}钥匙;
typedef结构t_密钥对{
钥匙;
PTR-PTR;
t_密钥对(){
}
t_密钥对(密钥k,PTR p){
key=k;
ptr=p;
}
}钥匙对;
类型定义结构t_op{
钥匙对;
亮片;
t_op(){
seq=0;
}
}OP;
#定义最大操作长度4000000
类型定义结构t_opq{
操作操作[最大操作长度];
整数自由偏移;
int globalSeq;
bool-queueOp(寄存器密钥对密钥对);
}OpQueue;
bool OpQueue::queueOp(寄存器密钥对密钥对){
bool isFull=false;
如果(自由偏移==(int)(最大值为-1)){
isFull=true;
}
ops[freeOffset]。密钥对=密钥对;
ops[freeOffset].seq=globalSeq++;
freeOffset++;
}
OpQueue-OpQueue;
#包括
int main(){
结构timespec开始时间、结束时间、总时间;
对于(int i=0;i<4000000;i++){
时钟获取时间(时钟实时和开始时间);
queueOp(KeyPair());
时钟获取时间(时钟实时和结束时间);
totalTime.tv_sec+=(endTime.tv_sec-startTime.tv_sec);
totalTime.tv_nsec+=(endTime.tv_nsec-startTime.tv_nsec);
}
printf(“\n运行时间:%ld”,totalTime.tv\u sec*10000000000ll+totalTime.tv\u nsec/1000L);
}

我无法强制缓存,但您可以强制内存不可缓存。如果您有大型的其他数据结构,您可以排除这些数据结构,这样它们就不会污染您的缓存。这可以通过为Windows Virutalallocxx函数指定PAGE_NOCACHE来完成


您不会将结构放入任何缓存中。CPU会自动为您执行此操作。CPU甚至比这更聪明;如果您访问顺序内存,它将在您读取之前开始将内存中的内容放入缓存

事实上,对于这样一个简单的代码,您花费在度量上的时间是执行代码的时间的十倍(在您的情况下,显然是60倍),这应该是常识


既然您对clock_gettime()非常有信心:我建议您连续调用它五次并存储结果,然后打印差异。有分辨率,有精度,还有返回当前时间所需的时间,这真是太长了

8字节*400万大约是32MB。如果这需要6秒钟,而您的CPU还没有使用20年,那么这不仅仅是缓存,肯定还有其他问题。一个合理的新CPU将以每秒几GB的速度连续写入非优化代码。你是不是每次都在重新分配内存?(此外,缓存在大多数情况下都能很好地使用固定步幅顺序访问,CPU会自动地很好地完成这项工作,只是不会通过页面边界)您不会碰巧将_推回std::vector或其他什么东西?--尽管这也不需要太长的时间,因为它会以几何级数增长……Damon~谢谢你的评论!在阅读了你的评论之后,我再次分析了我的代码,发现了一些我遗漏的东西。我在上面附加了遗漏的内容和代码的缩写版本。您可能希望在{}之外的循环之前和之后调用clock_gettime一次。在上面的代码中,您仍然调用它800万次。如果你想要每一次手术
typedef struct t_ptr {
    uint32 isleaf :1, isNextLeaf :1, ptr :30;
    t_ptr(void) {
        isleaf = false;
        isNextLeaf = false;
        ptr = NIL;
    }
} PTR;

typedef struct t_key {
    uint32 op :1, key :31;
    t_key(void) {
        op = OP_INS;
        key = 0;
    }
} KEY;

typedef struct t_key_pair {
    KEY key;
    PTR ptr;
    t_key_pair() {
    }

    t_key_pair(KEY k, PTR p) {
        key = k;
        ptr = p;
    }
} KeyPair;

typedef struct t_op {
    KeyPair keyPair;
    uint seq;
    t_op() {
        seq = 0;
    }
} OP;

#define MAX_OP_LEN 4000000
typedef struct t_opq {
    OP ops[MAX_OP_LEN];
    int freeOffset;
    int globalSeq;
    bool queueOp(register KeyPair keyPair);
} OpQueue;

bool OpQueue::queueOp(register KeyPair keyPair) {
    bool isFull = false;
    if (freeOffset == (int) (MAX_OP_LEN - 1)) {
        isFull = true;
    }
    ops[freeOffset].keyPair = keyPair;
    ops[freeOffset].seq = globalSeq++;
    freeOffset++;
}

OpQueue opQueue;
#include <sys/time.h>
int main() {
    struct timespec startTime, endTime, totalTime;
    for(int i = 0; i < 4000000; i++) {
        clock_gettime(CLOCK_REALTIME, &startTime);
        opQueue.queueOp(KeyPair());
        clock_gettime(CLOCK_REALTIME, &endTime);
        totalTime.tv_sec += (endTime.tv_sec - startTime.tv_sec);
        totalTime.tv_nsec += (endTime.tv_nsec - startTime.tv_nsec);
    }
    printf("\n elapsed time: %ld", totalTime.tv_sec * 1000000LL + totalTime.tv_nsec / 1000L);
}