繁忙的循环减慢了延迟关键计算

时间:2016-09-13 00:54:35

标签: c++ performance low-latency

我的代码执行以下操作:

  1. 做一些长时间运行的强烈计算(下面称为无用
  2. 执行小型延迟关键任务
  3. 我发现执行延迟关键任务所需的时间比长时间运行计算要高,而不是没有它。

    以下是一些重现此效果的独立C ++代码:

        #include <stdio.h>
        #include <stdint.h>
    
        #define LEN 128
        #define USELESS 1000000000
        //#define USELESS 0
    
        // Read timestamp counter
        static inline long long get_cycles()
        {
                unsigned low, high;
                unsigned long long val;
                asm volatile ("rdtsc" : "=a" (low), "=d" (high));
                val = high;
                val = (val << 32) | low;
                return val;
        }
    
        // Compute a simple hash
        static inline uint32_t hash(uint32_t *arr, int n)
        {
                uint32_t ret = 0;
                for(int i = 0; i < n; i++) {
                        ret = (ret + (324723947 + arr[i])) ^ 93485734985;
                }
                return ret;
        }
    
        int main()
        {
                uint32_t sum = 0;       // For adding dependencies
                uint32_t arr[LEN];      // We'll compute the hash of this array
    
                for(int iter = 0; iter < 3; iter++) {
                        // Create a new array to hash for this iteration
                        for(int i = 0; i < LEN; i++) {
                                arr[i] = (iter + i);
                        }
    
                        // Do intense computation
                        for(int useless = 0; useless < USELESS; useless++) {
                                sum += (sum + useless) * (sum + useless);
                        }
    
                        // Do the latency-critical task
                        long long start_cycles = get_cycles() + (sum & 1);
                        sum += hash(arr, LEN);
                        long long end_cycles = get_cycles() + (sum & 1);
    
                        printf("Iteration %d cycles: %lld\n", iter, end_cycles - start_cycles);
                }
        }
    

    当使用-O3编译并将USELESS设置为10亿时,三次迭代分别进行了588,4184和536次循环。在USELESS设置为0的情况下进行编译时,迭代分别需要394,358和362个周期。

    为什么会发生这种情况(尤其是4184次循环)?我怀疑由于强烈的计算引起的缓存未命中或分支错误预测。但是,如果没有强烈的计算,延迟关键任务的第0次迭代非常快,所以我不认为冷缓存/分支预测器是原因。

1 个答案:

答案 0 :(得分:1)

将我的推测评论转移到答案:

当您的繁忙循环运行时,服务器上的其他任务可能会将缓存的arr数据推出L1缓存,以便hash中的第一个内存访问需要重新加载来自较低级别的缓存。没有计算循环,就不会发生这种情况。您可以尝试将arr初始化移动到计算循环之后,只是为了看看效果是什么。