语言的速度
這些年我的日常開發總是在使用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-timeint sourceClassId = sourceClass.getId (); // JIT compile-timeint 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:
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)譯,我僅僅是拷貝林杰杰的!]
總結
- 上一篇: python:小乌龟turtle
- 下一篇: 工作好找吗?