【问题标题】:Why cache hit takes more time than cache miss?为什么缓存命中比缓存未命中需要更多时间?
【发布时间】:2021-01-29 05:00:50
【问题描述】:

我想在 C 或 C++ 中清除我的缓存行。我的代码如下,我的 gcc 版本是 9.3.0。

#include <chrono>
#include <cmath>
#include <cstring>
#include <iostream>

#include <x86intrin.h>


void clear_cache(char *addr, int size) {
        int round = (size-1) / 64 + 1;
        for (int i = 0; i < round; i++) {
                _mm_clflush(addr);
                addr += 64;
        }
}

float time_test_cache_hit(char * mem_block, size_t len) {
        char tmp;
        std::chrono::high_resolution_clock::time_point tmp1, tmp2;
        tmp1 = std::chrono::high_resolution_clock::now();
        for (int idx = 0; idx < len; idx ++) {
                tmp = *(mem_block + idx);
        }
        tmp2 = std::chrono::high_resolution_clock::now();
        return ((std::chrono::duration<float>)(tmp2 - tmp1)).count();
}

float time_test_cache_miss(char * mem_block, size_t len) {
        char tmp;

        clear_cache(mem_block, sizeof(char)*len);

        std::chrono::high_resolution_clock::time_point tmp1, tmp2;
        tmp1 = std::chrono::high_resolution_clock::now();
        for (int idx = 0; idx < len; idx ++) {
                tmp = *(mem_block + idx);
        }
        tmp2 = std::chrono::high_resolution_clock::now();
        return ((std::chrono::duration<float>)(tmp2 - tmp1)).count();
}


uint64_t tsc_test_cache_hit(char * mem_block, size_t len) {
        char tmp;

        for (int i = 0; i < len; i ++) {
                *(mem_block + i) = i;
        }

        uint64_t time1, time2;
         time1 = __rdtsc();
        for (int idx = 0; idx < len; idx ++) {
                tmp = *(mem_block + idx);
        }
         time2 = __rdtsc();
        return time2 - time1;
}

uint64_t tsc_test_cache_miss(char * mem_block, size_t len) {
        char tmp;

        clear_cache(mem_block, sizeof(char)*len);

        uint64_t time1, time2;
         time1 = __rdtsc();
        for (int idx = 0; idx < len; idx ++) {
                tmp = *(mem_block + idx);
        }
         time2 = __rdtsc();
        return time2 - time1;
}

int main(int argc, char ** argv) 
{

        int len = 100;

        char* mem_block = (char*) malloc(sizeof(char)*len);

        for (int i = 0; i < len; i ++) {
                *(mem_block + i) = i;
        }

        std::cout << "cache hit time: " << time_test_cache_hit(mem_block, len) << "\ncache miss time: " << time_test_cache_miss(mem_block, len) << std::endl;
        std::cout << "cache hit tsc: " << tsc_test_cache_hit(mem_block, len) << "\ncache miss tsc: " << tsc_test_cache_miss(mem_block, len) << std::endl;

        free(mem_block);

        return 0;
}

我的缓存线是 64 字节。至于我的cpu缓存大小,是这样的:

L1d cache:                       1.3 MiB
L1i cache:                       1.3 MiB
L2 cache:                        40 MiB
L3 cache:                        55 MiB

我多次运行这段代码,结果似乎很奇怪,

cache hit time: 1.224e-06
cache miss time: 9.07e-07
cache hit tsc: 1672
cache miss tsc: 2114

如果我将clear_cache 函数更改如下:

void clear_cache(char *addr, int len) {
        for (int idx = 0; idx < len; idx ++) {
                _mm_clflush(mem_block + idx);
        }
}

把调用clear_cache的函数从clear_cache(mem_block, sizeof(char)*len)改成clear_cache(mem_block, len),运行的程序好像有道理,结果是这样的:

cache hit time: 1.195e-06
cache miss time: 3.411e-06
cache hit tsc: 1580
cache miss tsc: 8896

如上所示,为什么我以前写的代码不能产生正常的结果(缓存命中比缓存未命中需要更多时间),但两篇文章都表明缓存未命中需要更多的 tsc(时间戳计数)比缓存命中,尽管它们的缓存未命中 tsc 也有很大不同。为什么代码会这样?或者如果我的编码有问题?非常感谢您的帮助。

【问题讨论】:

  • 请注意,tmp 在循环中的分配没有明显的效果。因此,编译器可能会优化它以及整个循环。检查此程序集:godbolt.org/z/nbWz7d.
  • 我不确定,但实际上,我在进行 GCC 编译时没有指定任何优化参数
  • 一般来说,测量未优化的构建没有多大意义。在这种情况下,甚至循环索引和限制都可以在每次迭代中从内存中读取:godbolt.org/z/o4WrdG

标签: c++ caching clflush


【解决方案1】:

我认为您尝试对某些东西进行微基准测试,但使用了错误的工具。我认为您尝试测量的代码太快无法使用系统时钟进行测量,您应该使用其他工具(详情:here)。测量的开销,可能与操作系统相关的开销与代码的运行时间相同。

【讨论】:

    猜你喜欢
    • 2017-01-14
    • 2013-09-04
    • 2019-04-13
    • 2012-04-21
    • 1970-01-01
    • 2015-07-09
    • 1970-01-01
    • 2017-01-03
    相关资源
    最近更新 更多