语言的速度

来源:互联网 发布:php worker是什么意思 编辑:程序博客网 时间:2024/05/22 13:58

这些年我的日常开发总是在使用C/C++(gcc和vc都用)和汇编,还用过其他各类脚本系统做小的应用,asp、php和jsp的网站也偶尔做接手做做,前几年用C#做过很简单的串口监控小程序,当时感觉还是很好的,去年以来开始做仿真系统就用C#,那种慢已经不是自己可以容忍的了,有时候真的有早年间用486编译linux核心的那种感觉——等吧,喝点水去个厕所就会好。基于java的编程早年间做过,主要闲慢,使用了jit技术后,C#和java的速度其实都有非常高的提升,可是在真正运行到实际系统时候总是感觉有点卡,像ios和android,600M的iphone跑同一款游戏就是比1G的android要快。很多人说是框架问题,说苹果的优化,其实就是语言本身的问题,如果都是运行一个十几亿次的循环加法,使用jit技术的java和C#不会比C或者C++差,可是在无数的语言对象的调用消除下,系统的差异被一点点放大,卡、钝就成了此类系统的特征,现在的android机器已经向2GHz、8核心直奔而去,也许这就是那个瓶颈了。

我们可以说C#和java是伟大的语言,在我的工作领域,没有什么跨平台的开发,都是C/C++/汇编,java的跨平台性我就体会不到了。如果说他们的伟大是因为有无穷的库,我倒是要承认,java和C#的下的框架层出不穷,C++的boost整了多少年才一点点变成标准。我记得09年我做了一个用正则表达式的软件,那还是因为vs2008集成的缘故,可是正则表达式早就在C#和java里面集成使用了,当然alt里面的正则可以忽略,那个真的不好用。如果不是boost有已经变异好的版本,我都懒得用。但是只要用过boost的,对java和C#也就淡然了。

我始终认为并不是C或者C++不好,而是大家为了自己的利益而画地为牢,看看C++和C的发展路线吧,简直就是蜗牛的速度,现在可好,找C#程序员一堆一堆的,C程序员倒是都是,可是没有几个好的。看看国外程序员用vi去做vc的程序,我们简直应该汗颜了。

最后我要说明,我并不是对java或者C#有偏见,这两个语言我都做过软件,我只是认为:语言只是一种工具,在合适的领域使用合适的语言才是一个程序员最合适的选择。



http://chipset04180.blog.163.com/blog/static/27693238200810264270796/


Why Java Will Always Be Slower than C++   

2008-11-26 16:27:00|  分类: 随意|字号 订阅

by Dejan Jelovic

 

"Java is high performance. By high performance we mean adequate. By adequate we mean slow." Mr. Bunny

 

Anybody that has ever used a non-trivial Java program or has programmed in Java knows that Java is slower than native programs written in C++. This is a fact of life, something that we accept when we use Java.

However, many folks would like to convince us that this is just a temporary condition. Java is not slow by design, they say. Instead, it is slow because today's JIT implementations are relatively young and don't do all the optimizations they could.

This is incorrect. No matter how good the JITs get, Java will always be slower than C++.

The Idea

People who claim that Java can be as fast as C++ or even faster often base their opinion on the idea that more disciplined languages give the compiler more room for optimization. So, unless you are going to hand-optimize the whole program, the compiler will do a better job overall.

This is true. Fortran still kicks C++'s ass in numeric computing because it is more disciplined. With no fear of pointer aliasing the compiler can optimize better. The only way that C++ can rival the speed of Fortran is with a cleverly designed active library like Blitz++.

However, in order to achieve overall results like that, the language must be designed to give the compiler room for optimization. Unfortunately, Java was not designed that way. So no matter how smart the compilers get, Java will never approach the speed of C++.

The Benchmarks

Perversely, the only area in which Java can be as fast as C++ is a typical benchmark. If you need to calculate Nth Fibonacci number or run Linpack, there is no reason why Java cannot be as fast as C++. As long as all the computation stays in one class and uses only primitive data types like int and double, the Java compiler is on equal footing with the C++ compiler.

The Real World

The moment you start using objects in your program, Java looses the potential for optimization. This section lists some of the reasons why.

1. All Objects are Allocated on the Heap

Java only allocates primitive data types like int and double and object references on the stack. All objects are allocated on the heap.

For large objects which usually have identity semantics, this is not a handicap. C++ programmers will also allocate these objects on the heap. However, for small objects with value semantics, this is a major performance killer.

What small objects? For me these are iterators. I use a lot of them in my designs. Someone else may use complex numbers. A 3D programmer may use a vector or a point class. People dealing with time series data will use a time class. Anybody using these will definitely hate trading a zero-time stack allocation for a constant-time heap allocation. Put that in a loop and that becomes O (n) vs. zero. Add another loop and you get O (n^2) vs. again, zero.

2. Lots of Casts

With the advent of templates, good C++ programmers have been able to avoid casts almost completely in high-level programs. Unfortunately, Java doesn't have templates, so Java code is typically full of casts.

What does that mean for performance? Well, all casts in Java are dynamic casts, which are expensive. How expensive? Consider how you would implement a dynamic cast:

The fastest thing you could do is assign a number to each class and then have a matrix that tells if any two classes are related, and if they are, what is the offset that needs to be added to the pointer in order to make the cast. In that case, the pseudo-code for the cast would look something like this:

DestinationClass makeCast (Object o, Class destinationClass) {    Class sourceClass = o.getClass (); // JIT compile-time    int sourceClassId = sourceClass.getId (); // JIT compile-time    int destinationId = destinationClass.getId ();    int offset = ourTable [sourceClassId][destinationClassId];    if (offset != ILLEGAL_OFFSET_VALUE) {        return <object o adjusted for offset>;    }    else {        throw new IllegalCastException ();    }}

Quite a lot of code, this little cast! And this here is a rosy picture - using a matrix to represent class relationships takes up a lot of memory and no sane compiler out there would do that. Instead, they will either use a map or walk the inheritance hierarchy - both of which will slow things down even further.

3. Increased Memory Use

Java programs use about double the memory of comparable C++ programs to store the data. There are three reasons for this:

  1. Programs that utilize automatic garbage collection typically use about 50% more memory that programs that do manual memory management.
  2. Many of the objects that would be allocated on stack in C++ will be allocated on the heap in Java.
  3. Java objects will be larger, due to all objects having a virtual table plus support for synchronization primitives.

A larger memory footprint increases the probability that parts of the program will be swapped out to the disk. And swap file usage kills the speed like nothing else.

4. Lack of Control over Details

Java was intentionally designed to be a simple language. Many of the features available in C++ that give the programmer control over details were intentionally stripped away.

For example, in C++ one can implement schemes that improve the locality of reference. Or allocate and free many objects at once. Or play pointer tricks to make member access faster. Etc.

None of these schemes are available in Java.

5. No High-Level Optimizations

Programmers deal with high-level concepts. Unlike them, compilers deal exclusively with low-level ones. To a programmer, a class named Matrix represents a different high-level concept from a class named Vector. To a compiler, those names are only entries in the symbol table. What it cares about are the functions that those classes contain, and the statements inside those functions.

Now think about this: say you implement the function exp (double x, double y) that raises x to the exponent y. Can a compiler, just by looking at the statements in that function, figure out that exp (exp (x, 2), 0.5) can be optimized by simply replacing it with x? Of course not!

All the optimizations that a compiler can do are done at the statement level, and they are built into the compiler. So although the programmer might know that two functions are symmetric and cancel each other now, or that the order of some function calls is irrelevant in some place, unless the compiler can figure it out by looking at the statements, the optimization will not be done.

So, if a high-level optimization is to be done, there has to be a way for the programmer to specify the high-level optimization rules for the compiler.

No popular programming language/system does this today. At least not in the totally open sense, like what the Microsoft's Intentional Programming project promises. However, in C++ you can do template metaprogramming to implement optimizations that deal with high-level objects. Temporary elimination, partial evaluation, symmetric function call removal and other optimizations can be implemented using templates. Of course, not all high-level optimizations can be done this way. And implementing some of these things can be cumbersome. But a lot can be done, and people have implemented some snazzy libraries using these techniques.

Unfortunately, Java doesn't have any metaprogramming facilities, and thus high-level optimizations are not possible in Java.

So...

Java, with the current language features, will never be as fast as C++. This pretty much means that it's not a sensible choice for high-performance software and the highly competitive COTS arena. But its small learning curve, its forgiveness, and its large standard library make it a good  choice for some small and medium-sized in-house and custom-built software.

 

 

Notes

1. James Gosling has proposed a number of language features that would help improve Java performance. You can find the text here. Unfortunately, the Java language has not changed for four years, so it doesn't seem like these will be implemented any time soon.

2. The most promising effort to bring generic types to Java is Generic Java. Unfortunately, GJ works by removing all type information when it compiles the program, so what the execution environment sees is the end is again the slow casts.

3. The Garbage Collection FAQ contains the information that garbage collections is slower than customized allocator (point 4 in the above text).

4. There is a paper that claims that Garbage Collection Can Be Faster than Stack Allocation. But the requirement is that there is seven times more physical memory than what the program actually uses. Plus, it describes a stop-and-copy collector and doesn't take concurrency into account. [Peter Drayton: FWIW, this is an over-simplification of the paper, which provides a means of calculating what the cross-over point is, but doesn't claim that 7 is a universal cross-over point: it is merely the crossover point he derives using the sample inputs in the paper.]

 

 

Feedback

I received a lot of feedback about this article. Here are the typical comments, together with my answers:

"You forgot to mention that all methods in Java are virtual, because nobody is using the final keyword."

The fact that people are not using the final keyword is not a problem with the language, but with the programmers using it. Also, virtual functions calls in general are not problematic because of the call overhead, but because of lost optimization opportunities. But since JITs know how to inline across virtual function boundaries, this is not a big deal.

Java can be faster than C++ because JITs can inline over virtual function boundaries.

C++ can also be compiled using JITs. Check out the C++ compiler in .NET.

In the end, speed doesn't matter. Computers spend most of their time waiting on our input.

Speed still maters. I still wait for my laptop to boot up. I wait for my compiler. I wait on Word when I have a long document.

I work in the financial markets industry. Sometimes I have to run a simulation over a huge data set. Speed matters in those cases.

It is possible for a JIT to allocate some objects on a stack.

Sure. Some.

Your casting pseudo-code is naive. For classes a check can be made based on inheritance depth.

First, that's only a tad faster than the matrix lookup.

Second, that works only for classes, which make up what percentage of casts? Low-level details are usually implemented through interfaces.

So we should all use assembly, ha!?

No. We should all use languages that make sense for a given project. Java is great because it has a large standard library that makes many common tasks easy. It's more portable than any other popular language (but not 100% portable - different platforms fire events at different times and in different order). It has garbage collection that makes memory management simpler and some constructs like closures possible.

But, at the same time, Java, just like any other language, has some deficiencies. It has no support for types with value semantics. Its synchronization constructs are not efficient enough. Its standard library relies on checked exceptions which are evil because they push implementation details into interfaces. Its performance could be better. The math library has some annoying problems. Etc.

Are these deficiencies a big deal? It depends on what you are building. So know a few languages and pick the one that, together with the compiler and available libraries, makes sense for a given project.

Trace back: http://www.jelovic.com/articles/why_java_is_slow.htm

耍过Java程序,或者用Java码过程序的人都晓得,Java要比用C++写成的原生程序要慢。这是咱用Java时已经承认的事实。
  不过,很多人想要说服我们说这只不过是暂时的,他们说Java从设计上来讲并不慢,相反,只是现在的JIT实现相对比较嫩,有很多能优化的地方JIT并没有优化到,拖了后腿。其实不然,不管JIT们多牛,Java永远要比C++慢。
  
  我想说...
  宣扬Java不慢于C++的人往往是觉得,(语法)严格的语言,可以让编译有更大的优化空间。因此,除非你想做人肉编译器优化整个程序,否则通常都是编译器做得更好。
  这是真的。在数值计算领域,Fortran仍然胜于C++,的确因为它更严格。不用担心指针瞎搅和,编译器可以更安心地优化。C++想打败Fortran的唯一办法,就是好好设计一个像Blitz++那样的库。

  测试...
   Java可以跟得上C++的地方,就是基准测试。计算起第N个斐波纳契数,或者运行起Linpack,Java没理由不跟C++跑得一样快。当所有的计 算都放在一个类里,并且只使用基本的数据类型,比如说int或者double时,Java编译器的确能跟得上C++的脚步。

  事实...
  当开始在程序中使用对象的时候,Java就放松了潜在的优化。这一节会告诉你为什么。

  1. 所有的对象都是从堆里分配的。
  Java从栈里分配的,就只有基本数据类型,如int,或者double,还有对象的引用。所有的对象都是从堆里分配的。
  当有大量语义上是一回事的对象时,这不成问题。C++同样也是从堆上分配这些对象。但是,当有值语义不同的小对象时,这就是一个主要的性能杀手。
   什么是小对象?对我来说,就是迭代器们。在设计中,我用了很多迭代器。别人可能会用复数。3D程序员可能会矢量或者点类。处理时间序列的人可能会有时间 类。使用这些类的人,无一例外地讨厌把不费时间的栈上分配换成花费固定时间的堆上分配。假如把它放在一个循环里,就变成了O(n)对0了。如果再加一层循 环,没错,又变成O(n^2)对0了。

  2. 大量的转换。
  得益于模板,好的C++程序员甚至可以写于完全没有转换的牛程序。不幸,Java没有模板,所以Java代码总是充满了转换。
  对于性能,它们意味着什么?呃,在Java里所有的转换都是很费时的动态转换。多费时?想想你可能会怎么样实现转换的:
  最快的方法就是,给每一个类赋值一个序号,然后用一个矩阵来描述任意两个类是否相关的。如果是的话,需要给指针加上多少的位移才能进行转换。这种方法的伪码看起来应该是这样的:
DestinationClass makeCast (Object o, Class destinationClass) {
    Class sourceClass = o.getClass (); // JIT compile-time
    int sourceClassId = sourceClass.getId (); // JIT compile-time

    int destinationId = destinationClass.getId ();

    int offset = ourTable [sourceClassId][destinationClassId];

    if (offset != ILLEGAL_OFFSET_VALUE) {
        return <object o adjusted for offset>;
    }
    else {
        throw new IllegalCastException ();
    }
}
好一堆代码。这只是一个简单的情景——用矩阵来表示类的关系浪费了一部分内存,没有哪个成熟的编译器会这样子做。他们会使用map或者遍历继承树,这样会变得更慢。

  3. 攀升的内存占用。
  Java程序储存数据占用的内存大概是相当的C++程序的两倍。原因如下:
  1. 启用了垃圾收集的程序一般都比不使用垃圾收集的程序多花50%的内存。
  2. 本来C++里在栈上分配的对象,到了Java就在堆上分配了。
  3. Java对象比较大,因为所有的对象都有一个虚表,还要加上对(线程)同步的原生支持。
大的内存映像让程序更大概率被放到磁盘的交换区去。没有什么比交换文件更慢的了。
  4. 缺少更细致的控制。
Java原来就是作为一种简单的语言来设计的。很多在C++里让程序员控制细节的特性在Java里都被一脚踢开了。
  比如说,在C++里可以改进引用的位置(?)。或者一次申请和释放很多个对象。或者用指针耍一些小技巧,更快地访问成员。
  5. 没有高层次的优化。
  程序员处理高层次的概念。而编译器处理剩下的低层次概念。对于程序员来说,一个叫Matrix的类就代表了比一个叫Vector的类更高层次的概念。而对于编译器来说,这些名字都是符号表的一个入口。他们只关心类里面有哪些函数,函数里面有哪些语句。
  这样想一下,比如说要实现一个exp(double x, double y)函数,计算出x的y次幂。对于一个编译器,它能只看一下这个函数,然后指出,exp(exp(x, 2), 0.5)可以优化成x自己吗?当然不行。
  编译器能做的优化只是语句层面的,而y是在编译器里面的。即使程序员知道两个函数是对称的,可以把它们都消去,或者函数的调用顺序只是相反的,除非编译器能只瞄一下语句,然后指出来,不然优化是不可能完成的。
  所以,如果想要完成一个高水平的优化,必须存在某种方法,可以让程序员来告诉编译器优化的规则。

   没有哪个流行的程序语言/系统可以做到这点,至少已知的方法,比如微软承诺的智能语言,都不能。即便如此,在C++里可以用模板元编程来实现对高层次对 象的优化。临时消除,部分求值,对称函数调用的消去,和其它可以用模板实现的优化。当然,不是所有的高层次优化都可以这样做。并且实现这些东西相当麻烦。 但是大多数都可以完成,有人已经用这些技术实现了好些时髦的库。

  不幸的是,Java没有任何元编程的特质,因此在Java中不会有这种高层次的优化。
  所以...

  由于存在这种语言特性,Java不可能达到C++这种速度。这相当程序上暗示了,对于要求高性能的软件和竞争激烈的COTS舞台上,使用Java不是一种明智的选择。但是因为它和缓的学习曲线,它的容错,和它庞大的标准库,所以适合开发中小型自用和定制软件。

  附记...
  1. 有人向James Gosling(谁?google之...)提交了很多可以改进Java性能的语言特性。文本在这里。不幸的是,Java语言已经有四年没有改动过了,所以看起来这些提议似乎不会在一夜之间被实现。

  2. 最有可能往Java里加入泛型的是Generic Java。又很不幸的是,GJ只是通过在编译时把所有类型信息去掉来支持泛型。所以最后面执行环境看到的,仍然是缓慢的转换。

  3. 垃圾收集的FAQ包含了关于垃圾收集慢于定制分配器的信息(上面第四点)。

   4. 这里是一篇宣称垃圾收集比栈分配的快的文章。但是它的要求是物理内存必须是程序实际需要的内存的七倍之多。还有,它描述的是一种stop- and-copy(是不是那种执行到一半,然后停下来,把内存拷到另外一块内存,同时清除垃圾的那种方法?),而且还不是并发的。

  反馈...
  我收到很多关于这篇文章的反馈。附上一些典型的评论,还有我的回答:

  “你还忘记了指出在Java里所有的方法都是虚方法,因为没有人会加上final关键字。”
  事实上,不使用final关键字不是问题的关键所在,使用者才是。同时,虚函数也没有问题,但是却失去了优化机会。自从JIT们知道怎么样内联虚函数,这就变得不那么显著了。

  JIT可以内联虚函数,所以Java可以比C++更快。
  C++也可以使用JIT编译。不信的可以看看.NET的C++编译器。

  到最后的时候,速度并不重要。电脑浪费了大部份时间在等待用户输入。
  速度仍然很重要。我仍然在等我的笔记本启动起来,我在等我的编译器停下来,我还要等Word打开一个超长的文档。
  我在一个金融公司工作。有时候我必须对一个很大的数据集进行模拟。速度在这种情况下都很重要。

  有些JIT可以在栈上分配一些对象。
  当然,一些。

  你的转换代码看起来很丑。可以在类的继承层次上检查类。
  首先,这样只比矩阵查找快一点点而已。
  第二,这样只能查找类,类只占多少?低层次的细节往往是通过接口来实现的。

  哈,那么我们都应该使用汇编?
  不是的,我们都要使用对业务有用的语言。Java提供了庞大的标准库,让很多任务变得容易,因此Java是伟大的。它比其它所有的语言更容易移植(但并非100%可移植——不同的平台有不同问题)。它具有垃圾收集机制,简化了内存管理,同时也让某些构造如闭包可实现。
  但是,同时,Java和所有的语言一样,也有瑕疵。在值语义的类型上缺少支持。它的同步并不是很有效率。它的标准库建立在异常检查之上,把实现拖进了接口。它的性能可以更好。它的数学库有些恼人的问题。诸如此类。

  这些缺憾都是大问题吗?看你用它做什么。因此,在几种语言里,连同它的编译器以及可以选择的类库里选择对你的工程有利的一种。
[林杰杰翻译,不是我(Chipset)译,我仅仅是拷贝林杰杰的!]

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 私处刮毛外面皮肤不小心弄伤怎么办 老婆骂孩子不准老公带饿小孩怎么办 结婚3年妻子不让丈夫碰怎么办 老婆出轨现在没证据他要离婚怎么办 结婚后老婆不让碰分房睡离婚怎么办 电动车调速把手变速挡坏了怎么办 路边车辆贴条了超过15天了怎么办 违停交警拍照了没贴条 照片怎么办 支付宝违章缴费罚单输错了怎么办 交警查酒驾跑了把警车撞了怎么办 禁止进入待行区的时候进入了怎么办 今天开车把72岁老太婆撞了怎么办 在这种路口遇到行人突然横穿怎么办 在左拐车道却直行了怎么办 路边简易房让拆除不想拆怎么办 英国护照的名和姓印颠倒了怎么办 加热圈功率小加不到设定温度怎么办 本田飞度05年车尾气不好怎么办 文件在lr中打开后找不到了怎么办 手机安装软件成功屏幕上没有怎么办 脸过敏发红痒怎么办用什么药膏 脸上又红又肿又痒怎么办 宝宝把皮革咬烂吃掉了怎么办 自动档皮革挡把防尘套烂了怎么办 pu包用酒精擦坏了怎么办 白色的面料被84弄黄了怎么办 要账的人赖在家不走怎么办 两塔吊的安全距离不够2米怎么办 腿上被蚊子咬后留下的黑疤怎么办 携程订的酒店酒店一直没确认怎么办 客斤的无窗挨着厨房怎么办 餐桌和墙紧挨着容易脏怎么办 vivo手机被设置成英文该怎么办 电脑开机后显示英文字该怎么办? 看到文言文就不会翻译该怎么办呢 房间楼层太高闷热不通风怎么办 对方全责不理赔没有财产怎么办啊 买二手房过户后发现房顶漏水怎么办 钻石绣的钻两个粘在一起怎么办 开修理部的一天不干活都着急怎么办 调好的车漆时间长了有点稠怎么办