线程缓存的对象引用

时间:2012-12-29 23:22:25

标签: c++ c memory

我需要创建一种共享对象(无论出于何种原因)。它不仅限于单线程使用。通常在这种情况下,可以采用互锁操作(例如Win32上的InterlockedIncrementInterlockedDecrement)。

尽管对象引用计数应该在任何场景中都能正常工作,但我希望针对单线程使用进行优化。互锁操作比标准算术操作重得多。根据我的测量结果,一个互锁操作(发出完整的内存屏障)大约需要40个CPU周期才能完成#34;典型的" CPU,而标准算术值低于任何测量精度(由于CPU缓存)。

在内存分配方面,有类似的技术。存在堆实现,例如" TCMalloc",它由集中的内存分区机制组成,由适当的同步对象保护,加上每线程缓存。在最常见的情况下,在每线程缓存上分配/释放的内存,根本不涉及任何互锁操作,加上CPU缓存的概率很高。

因此,我有可能为参考支持对象做类似的事情。任何想法如何实现这一目标?原创的想法也很受欢迎。

在我的场景中可以延迟实际的对象破坏一段时间,如果这样可以提高性能。

1 个答案:

答案 0 :(得分:1)

我不会打扰。我刚刚运行了这个基准:

#include<stdio.h>

#define SIZE 1000000

static __inline__ unsigned long long rdtsc(void)
{
    unsigned hi, lo;
    __asm__ __volatile__ ("rdtsc" : "=a"(lo), "=d"(hi));
    return ( (unsigned long long)lo)|( ((unsigned long long)hi)<<32 );
}


void print_avg(const char *str, const int *diff, int size)
{
    int i;
    long sum = 0;
    int max = -1, min = 10000;
    for(i = 0; i < size; i++)
    {
    int t = diff[i];
    sum += t;
    if (t > max) max = t;
    if (t < min) min = t;
    }

    printf("%s average =%f clocks, max =%d, min =%d\n", str, (double)sum / size, max, min);
}



int main()
{
    unsigned long long a, b;
    int diff[SIZE];
    int value = 0;
    int i;


    for(i = 0; i < SIZE; i++)
    {
    a = rdtsc();

    __sync_fetch_and_add(&value, 2);
    b = rdtsc();

    diff[i] = (int)(b - a);
    }

    print_avg("Locked", diff, SIZE);

    for(i = 0; i < SIZE; i++)
    {
    a = rdtsc();
        value += 2;
    b = rdtsc();
    diff[i] = (int)(b - a);
    }
    print_avg("Not locked", diff, SIZE);

    return 0;
}

使用gcc -O2编译它会得到以下结果:

Locked average =105.672402 clocks, max =38756, min =86  
Not locked average =80.540389 clocks, max =23433, min =73

我跑了几次,结果每次非常相似。请忽略max的最大值 - 当处理器中断或其他什么时 - 它来自我为不同目的编写的一些代码,我只是回收了这个测试。这种小差异应该适用于所有现代处理器(Intel iCore和AMD Athlon64以及那些代)

除非由于某种原因您的编译器没有内联InterlockedIncrement,否则在代码上添加if语句很可能至少花费5个周期,因此您最多可以节省10个周期。希望你做的不是增加和减少参考计数器。

编辑:添加内存屏障也不会产生很大的影响 - 大约10个周期。

不可否认,如果我在第二个循环中添加十个加法,则每个循环大约需要五个时钟周期(平均每个加法半个时钟),其中锁定加法每个加法大约需要20个时钟。在我看来,仍然不值得添加if语句。但是如果你想添加“if(nr_threads == 1)a = a + 1;否则a = __sync_fetch_and_add(a,1);”,[或者无论你需要做什么]我都不会阻止你。但请确保您对整个应用程序进行基准测试,并确保其提高1%以上 - 我对此表示怀疑。请回来告诉我们有什么区别。我在Linux内核的“deallocate page-table entries”中添加的if语句使得它慢了2-5%,所以不值得。但是如果你在代码中发现它是值得的,那就是我的客人吧。我是根据经验说话,而且我有数字来展示它,但如果你想为自己尝试,那么为什么不这样做。