C++和Java性能

eba*_*onp 4 c++ java performance

这个问题只是推测性的.

我在C++中有以下实现:

using namespace std;

void testvector(int x)
{
  vector<string> v;
  char aux[20];
  int a = x * 2000;
  int z = a + 2000;
  string s("X-");
  for (int i = a; i < z; i++)
  {
    sprintf(aux, "%d", i);
    v.push_back(s + aux);
  }
}

int main()
{
  for (int i = 0; i < 10000; i++)
  {
    if (i % 1000 == 0) cout << i << endl;
    testvector(i);
  }
}
Run Code Online (Sandbox Code Playgroud)

在我的框中,这个程序大约执行.12秒; 令人惊讶的是,我在Java中使用了类似的实现[使用String和ArrayList],它的运行速度比我的C++应用程序快了很多(大约2秒).

我知道Java HotSpot在转换为native时会执行很多优化,但我认为如果这样的性能可以用Java完成,它也可以用C++实现......

那么,你认为应该在上面的程序中修改,或者我不知道,在使用的库中或在内存分配器中,在这个东西中达到类似的性能?(写这些东西的实际代码可能会很长,因此,讨论它会很棒)...

谢谢.

Dan*_*ker 12

你必须小心性能测试,因为它很容易欺骗自己或者不喜欢比较.

但是,我已经看到了将C#与C++进行比较的类似结果,并且有许多着名的博客文章关于当遇到这种证据时本机编码器的惊讶.基本上,一个好的现代代压缩GC非常适合大量的小型分配.

在C++的默认分配器中,每个块都被视为相同,因此分配和释放的平均成本非常高.在分代GC中,所有块都非常非常便宜地分配(几乎和堆栈分配一样便宜),如果它们变得短暂,那么它们的清理也非常便宜.

这就是为什么与更现代的语言相比,C++的"快速性能" - 在很大程度上 - 是神话.您必须先将C++程序从所有识别中调整好,然后才能与同等天真编写的C#或Java程序的性能竞争.

  • 我不能同意你的结论.关于C++的表现没有什么神话般的.大量的性能测试一次又一次地证明了这一点.有一些**显着的利基,其中C++的性能比其他语言/引擎差.最值得注意的是字符串处理和l12n/Unicode支持.这些已被广为人知,但它们是一个极端罕见的例外.而且这个规则仍然是:对于大多数计算密集型算法,C++比托管语言快一个数量级. (6认同)
  • 就像准备去升级池分配器一样?获得相同的性能几乎是一线改变. (5认同)
  • @gnud - 为自己尝试比较.在大多数实现中,删除自定义池分配器只会产生令人失望的差异,因为它们已经通过从池中进行子分配来实现malloc.现代GC和JIT语言可以利用有关函数调用的堆栈布局的信息来识别无法访问的对象,它们可以移动对象以消除碎片,它们可以延迟昂贵的工作,直到繁重的工作量.通过在更高层次上工作,他们有更多的优化方式.基于指针的内存操作相对难以优化. (3认同)
  • A,理论上,足够聪明的编译器会意识到向量在填充后被抛弃,并完全优化testvector().我所得到的是,这种做作无用的测试程序实际上毫无意义. (2认同)
  • @ewwicker - 我设法通过使用字符串的boost分配器将c ++版本的运行时间减少到1/3,而不是在每次调用`testvector`之间重新分配向量.请参阅下面的答案.现在,我没有声称在分配新对象时c ++比jvm/cli更快.但我声称他在c ++中分配2000个对象的方式非常愚蠢. (2认同)
  • @Konrad - 我没有估计它 - 在上面的评论中我把它称为一个紧凑的GC很好地服务的问题.再一次,我并不反对*good*C++的强大功能,这是一个隐含地将专家和无限量的开发时间用于表现工作的短语.我每天都在谈论你更愿意把精力集中在正确性,可读性和其他重要价值上的情况.*默认情况下*,托管环境可以很好地处理内存.*默认情况下*C++没有(带有r值引用的C++ 0x会更好). (2认同)
  • Jeez,为什么接受C++以外的其他语言能够表现得如此之难?这个人是对的,C++中的动态分配非常昂贵.是的,池分配器有帮助,但它并不能完全解决问题.它具有固定容量,并且随着时间的推移不能提供相同的压缩,从而改善了局部性.它需要更昂贵的策略来释放内存(例如引用计数). (2认同)

ale*_*gle 6

您的所有程序都以1000步为单位打印数字0..9000.testvector()不执行任何操作的调用可以消除.我怀疑你的JVM注意到了这一点,并且实际上是在优化整个功能.

只需将调用注释掉即可在C++版本中实现类似的效果testvector()!


gnu*_*nud 5

嗯,这是一个非常无用的测试,只测量小对象的分配.也就是说,简单的改变使我的运行时间从大约15秒减少到大约4秒.新版本:

typedef vector<string, boost::pool_allocator<string> > str_vector;    

void testvector(int x, str_vector::iterator it, str_vector::iterator end)
{
    char aux[25] = "X-";
    int a = x * 2000;
    for (; it != end; ++a)
    {
        sprintf(aux+2, "%d", a);
        *it++ = aux;
    }
}

int main(int argc, char** argv)
{
    str_vector v(2000);
    for (int i = 0; i < 10000; i++)
    {
        if (i % 1000 == 0) cout << i << endl;
        testvector(i, v.begin(), v.begin()+2000);
    }
    return 0;
}

real    0m4.089s
user    0m3.686s
sys     0m0.000s
Run Code Online (Sandbox Code Playgroud)

Java版本有时间:

real    0m2.923s
user    0m2.490s
sys     0m0.063s
Run Code Online (Sandbox Code Playgroud)

(这是我原始程序的直接java端口,除了它将ArrayList作为参数传递以减少无用的分配).

总而言之,Java上的小分配速度更快,内存管理在C++中更麻烦一些.但我们已经知道了:)