为什么 slice allocator 在 glib2.0 中比 malloc 慢?

Why slice allocator is slower than malloc in glib2.0?

我写了两个简单的程序来比较g_slice_alloc()g_malloc()[=49=的速度].

g_slice_alloc()版本:

#include <gtk/gtk.h>


int main(int argc, char *argv[])
{
    gchar *mem[1000000];
    gint i;
    for(i=0;i<1000000;i++){
        mem[i]=g_slice_alloc(50);
    }
    for(i=0;i<1000000;i++){
       g_slice_free1(50,mem[i]);
    }
    return 0;
}

g_malloc()版本

#include <gtk/gtk.h>


int main(int argc, char *argv[])
{
    gchar *mem[1000000];
    gint i;
    for(i=0;i<1000000;i++){
        mem[i]=g_malloc(50);
    }
    for(i=0;i<1000000;i++){
         g_free(mem[i]);
    }
    return 0;
}

编译它们

gcc slice.c -o slice `pkg-config --libs --cflags gtk+-3.0`
gcc malloc.c -o malloc `pkg-config --libs --cflags gtk+-3.0`

并测试它们

$ time ./slice

real    0m0.091s
user    0m0.063s
sys 0m0.025s

$ time ./malloc

real    0m0.071s
user    0m0.050s
sys 0m0.021s

g_slice_alloc() 版本预计 运行 更快,但实际上并非如此。为什么它更慢?

这是一个很好的测试用例吗?

然后我尝试了另一种方法来测试他们的速度。

#include <gtk/gtk.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
    gchar *mem[1000000];
    gint i;
    for(i=0;i<1000000;i++){
        gint j=i>10000?10000:i;
        mem[i]=g_slice_alloc(j);
    }
    for(i=0;i<1000000;i++){
       gint j=i>10000?10000:i;
       g_slice_free1(j,mem[i]);
    }
   return 0;
}

#include <gtk/gtk.h>  
#include <stdlib.h>

int main(int argc, char *argv[])
{
    gchar *mem[1000000];
    gint i;
    for(i=0;i<1000000;i++){
         gint j=i>10000?10000:i;
        mem[i]=g_malloc(j);
    }
    for(i=0;i<1000000;i++){
        gint j=i>10000?10000:i;
        g_free(mem[i]);
    }
    return 0;
 }

这次他们真的很接近,有时g_slice_alloc()更快,有时g_malloc() 更快。

$ time ./malloc

real    0m1.515s
user    0m0.285s
sys 0m1.229s

$ time ./slice

real    0m1.521s
user    0m0.278s
sys 0m1.215s

但是这个测试并不能证明g_slice_alloc()更快,它只是说明g_slice_alloc() 等同于 g_malloc()

glibc 的 malloc 改进了 很多 自从添加 GSlice 以来(当时 glibc 的 malloc 非常慢)。 GSlice 过去比 malloc 快很多,但由于 malloc 中的积极优化,它现在更快了,特别是对于线程繁重的应用程序。同时,GSlice 自添加以来并没有真正发生重大变化。

AFAIK,如今使用 GSlice 的唯一真正原因是它在不同平台上更加稳定(例如,显然 Windows' malloc 对于 GStreamer 来说太慢了)。

综上所述,您上面的内容并不是一个很好的测试。像 GSlice 这样的平板式分配器传统上擅长减少内存碎片,这是由许多不同大小的 allocations/frees 混合在一起引起的。您拥有的是一堆分配,然后是一堆释放。

此外,像 GSlice 这样的分配器依赖于相同大小的分配,因此增加分配大小的测试部分不会很好地工作——GSlice 是针对对象的,不是字符串和缓冲区。