如何提高用void **实现的动态数组的性能?

时间:2018-10-20 14:59:42

标签: c dynamic-arrays void-pointers

我需要实现一个可以使用任何类型的简单动态数组。

现在,我的void**实现比直接使用int*慢50%:

#define N 1000000000

// Takes ~6 seconds
void** a = malloc(sizeof(void*) * N);
for (int i =0; i < N; i++) {
        *(int*)(&a[i]) = i;

}
printf("%d\n", *(int*)&a[N-1]);

// Takes ~3 seconds
int* b = malloc(sizeof(int) * N) ;
for (int i =0; i < N; i++) {
        b[i] = i;
}
printf("%d\n", b[N-1]);

我不是C专家。有更好的方法吗?

谢谢

修改

看起来像使用void**是一个坏主意。有没有一种方法可以通过void*来实现?

这是在Go中实现的方式:

type slice struct {
        array unsafe.Pointer
        len   int
        cap   int
}

我想做类似的事情。

edit2

我设法用void*来实现。

解决方案非常简单:

 void* a = malloc(sizeof(int) * N);
 for (int i = 0; i < N; i++) {
    ((int*)a)[i] = i;
 }
 printf("%d\n", ((int*)a)[N-1]);

现在的性能是一样的。

3 个答案:

答案 0 :(得分:3)

您的两个替代程序并不相似。在第二个有效的空间中,您分配足以容纳N整数的空间,然后将值分配给该空间的int大小的成员。但是,在第一个中,您要分配足够大的空间以容纳N个指向void的指针,然后在不初始化这些指针的情况下,尝试为它们所指向的对象分配值。即使这些指针已初始化为指向int对象,也存在额外的间接访问级别。

从某种意义上说,您的第一个代码可以更正:

void** a = malloc(sizeof(void*) * N);
for (int i =0; i < N; i++) {
        a[i] = (void *) i;

}
printf("%d\n", (int) a[N-1]);

这依赖于这样的事实,即C允许在指针和整数类型之间进行转换(尽管不一定会造成数据丢失),并且请注意,只有一个间接级别(数组索引),而不是两个。

由于您未定义第一个替代方案的实施行为,因此我们只能推测其运行速度较慢的原因。但是,如果我们假设实现简单明了,那么可能会由于所有数组写入的缓存局部性差而导致性能下降。

答案 1 :(得分:0)

请注意,sizeof(void *)是64位处理器(8字节地址与4字节有符号整数)上sizeof(int)的两倍。如果是这种情况,我敢打赌,区别仅在于页面缓存丢失。您的存储单元需要多加载两倍的页面,这很慢(link for more information here)。

还请注意,C ++向量不是“可以与任何类型一起使用的动态数组”。它们绑定到一个类型,例如:std::vector<int>是一个动态数组,但是您只能在其中存储int

解决您的问题的方法是在C中实现某种std::vector<void *>。但这效率不高:

  • 您需要为每个元素进行2个分配(容器1个,元素本身1个)
  • 每次访问数据时,您需要进行2次间接访问(1次是在容器中获取指针,1次是在元素中获取数据)
  • 您需要在每个元素中存储某种类型信息。如果没有,您将不知道动态数组中的内容

答案 2 :(得分:0)

我设法用void*来实现。

解决方案非常简单:

 void* a = malloc(sizeof(int) * N);
 for (int i =0;i<N;i++) {
  ((int*)a)[i] = i;
 }
 printf("%d\n", ((int*)a)[N-1]);

现在的性能是一样的。

我也碰到了这篇很棒的文章,解释了如何在C中实现通用数据结构:

http://jiten-thakkar.com/posts/writing-generic-stack-in-c