【问题标题】:C++ Performance of structs used as a safe packaging of arrays用作数组安全封装的结构的 C++ 性能
【发布时间】:2010-08-05 21:46:47
【问题描述】:

在 C 或 C++ 中,不检查数组是否越界。解决此问题的一种方法是将其与结构一起打包:

struct array_of_foo{
  int length;
  foo *arr; //array with variable length.
};

然后,就可以初始化了:

array_of_foo *ar(int length){
  array_of_foo *out = (array_of_foo*) malloc(sizeof(array_of_foo));
  out->arr = (foo*) malloc(length*sizeof(foo));
}

然后访问:

foo I(array_of_foo *ar, int ix){ //may need to be foo* I(...
   if(ix>ar->length-1){printf("out of range!\n")} //error
   return ar->arr[ix];
}

终于解放了:

void freeFoo(array_of_foo *ar){ //is it nessessary to free both ar->arr and ar?
  free(ar->arr); free(ar);
}

这样它可以警告程序员越界。但是这种包装会大大减慢性能吗?

【问题讨论】:

  • 如果你使用 C++,你应该使用 std::vector。并且更少考虑性能。
  • 我们在这件事上听起来像是破纪录,但你的意思不是 c 就是 c++:这些语言不仅提供不同的功能,而且它们带有不同的文化和习语。请明确您对哪个感兴趣。
  • @kiamlaluno @SoapBox 不要重新标记或编辑问题以改变其含义。谢谢。
  • 我只是从标题中删除了标签;我没有改变问题的意思。

标签: c++ c performance arrays struct


【解决方案1】:

我同意std::vector 的建议。此外,您可以尝试 boost::array 库,其中包括固定大小的数组容器的完整(和测试)实现:

http://svn.boost.org/svn/boost/trunk/boost/array.hpp

【讨论】:

  • 提醒:问题已从 [c++] 重新标记为 [c]。
  • @James 我有点不清楚为什么;问题多次提到 C++
  • @James:是的,但这不是凯文做的。 (尽管我同意该代码没有任何 C++。)
  • @sbi,迈克尔:哦,对不起;我没有看到其他人重新提出了这个问题。我的意思是这更像是一个“提醒”,这样人们就不会因为它与(当前)问题标签不匹配而开始反对它。
  • @James @sbi 这个问题显然是被一个白痴重新标记的 - 我已经回滚了。
【解决方案2】:

在 C++ 中,无需自己编写不完整的vector 版本。 (要对vector 进行边界检查,请使用.at() 而不是[]。如果超出范围,它将引发异常。)

在 C 中,这不一定是一个坏主意,但我会将指针放在初始化函数中,然后返回结构。它有一个 int 和一个指针,不会很大,通常不超过指针大小的两倍。无论如何,您可能不希望在访问函数中包含随机的printfs,因为如果您确实超出了界限,您会收到随机消息,即使您查找它们也不会很有帮助。

【讨论】:

    【解决方案3】:

    主要的性能损失很可能来自检查每次访问的索引,从而破坏处理器中的流水线,而不是额外的间接性。在我看来,优化器不太可能在绝对不需要时找到优化检查的方法。
    例如,这在遍历整个数组的长循环中会非常明显——这是一种相对常见的模式。

    只是为了它:
    - 你也应该在ar()
    中初始化长度字段 - 你应该在I() 中检查ix < 0

    【讨论】:

    • static inline int check(int i, int *l) { return i > *l; } int main() { int a = 5; for (int i = 0; i < 5; ++i) { if (check(i,&a)) printf("never"); }。带有 -O3 的 gcc 优化了整个循环,因此优化器在某些情况下有一些希望删除边界检查。我不知道 C 编译器是否会像 JIT 那样用单个检查替换多个边界检查。
    • 如果程序足够清晰,一个好的编译器应该能够优化常见情况(编译器必须能够跟踪保存长度和索引的变量的所有别名)。至于处理器流水线,我想看看一些基准;处理器也有乐观的分支执行,正是为了更好地利用流水线。
    • gcc 没有做我希望的循环提升。 check 和以前一样,然后是 int main(int argc, char **argv) { int a = atoi(argv[1]); int b = 1; for (int i = 0; i < 7; ++i) { if (check(i,&a)) b = 0; } printf("%d\n", b);}。生成的代码调用 atoi,然后依次将结果与 0 .. 5 进行比较。随着循环展开。
    • 在遍历整个数组的长循环中,应该可以完美预测分支。
    • @Steve, @caf:因为每个测试都是针对任意内存位置(而不是常量,如您的示例)进行的,可以使用不可预测的值进行设置(例如系统的结果调用),循环展开变得无关紧要,优化也不是微不足道的。
    【解决方案4】:

    我没有任何正式的研究可以引用,但我从数组绑定检查是可选的语言中得到的回声是,关闭它很少会明显加快程序速度。

    如果您有想要更安全的 C 代码,您可能会对 Cyclone 感兴趣。

    【讨论】:

      【解决方案5】:

      您可以自己测试,但在某些机器上,您可能会在不同情况下遇到严重的性能问题。如果您要循环数百万个元素,那么每次检查边界都会导致大量缓存未命中。这将产生多大的影响取决于您的代码在做什么。同样,您可以很快地对此进行测试。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-11-03
        • 1970-01-01
        • 1970-01-01
        • 2013-04-03
        • 2011-10-24
        • 1970-01-01
        • 2014-11-10
        相关资源
        最近更新 更多