这些年我的日常开发总是在使用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/
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++.
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++.
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 moment you start using objects in your program, Java looses the potential for optimization. This section lists some of the reasons why.
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.
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
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.
Java programs use about double the memory of comparable C++ programs to store the data. There are three reasons for this:
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.
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.
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.
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.
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.]
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