[英]C++ and 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);
}
}
在我的框中,這個程序大約執行。 12秒; 令人驚訝的是,我在Java中使用類似的實現[使用String和ArrayList],它的運行速度比我的C ++應用程序快很多(大約2秒)。
我知道Java HotSpot在轉換為native時會執行很多優化,但我認為如果這樣的性能可以用Java完成,它也可以用C ++實現......
那么,你認為應該在上面的程序中修改,或者我不知道,在使用的庫中或在內存分配器中,在這個東西中達到類似的性能? (寫這些東西的實際代碼可能會很長,因此,討論它會很棒)...
謝謝。
你必須小心性能測試,因為它很容易欺騙自己或者不喜歡比較。
但是,我已經看到了將C#與C ++進行比較的類似結果,並且有許多着名的博客文章關於當遇到這種證據時本機編碼器的驚訝。 基本上,一個好的現代代壓縮GC非常適合大量的小型分配。
在C ++的默認分配器中,每個塊都被視為相同,因此分配和釋放的平均成本相當高。 在分代GC中,所有塊都非常非常便宜地分配(幾乎和堆棧分配一樣便宜),如果它們變得短暫,那么它們的清理也非常便宜。
這就是為什么與更現代的語言相比,C ++的“快速性能” - 在很大程度上 - 是神話。 您必須先將C ++程序從所有識別中調整好,然后才能與同等天真編寫的C#或Java程序的性能競爭。
您的所有程序都以1000的步長打印數字0..9000。對testvector()
的調用什么都不做,可以消除。 我懷疑你的JVM注意到了這一點,並且本質上是在優化整個功能。
只需注釋掉對testvector()
的調用,就可以在C ++版本中實現類似的效果!
嗯,這是一個非常無用的測試,只測量小對象的分配。 也就是說,簡單的改變使我的運行時間從大約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
Java版本有時間:
real 0m2.923s
user 0m2.490s
sys 0m0.063s
(這是我原始程序的直接java端口,除了它將ArrayList作為參數傳遞以減少無用的分配)。
總而言之,Java上的小分配速度更快,內存管理在C ++中更麻煩一些。 但我們已經知道了:)
Hotspot優化了代碼中的熱點。 通常,執行10000次的任何事情都會嘗試優化。
對於此代碼,在5次迭代之后,它將嘗試優化內部循環,將字符串添加到向量。 它將做的更多優化將包括逃避分析方法中的變量。 向量是一個局部變量,永遠不會逃避本地上下文,它很可能會刪除方法中的所有代碼並將其轉換為無操作。 要對此進行測試,請嘗試從該方法返回結果。 即使這樣,也要小心對結果做一些有意義的事情 - 例如,只要得到它的長度就可以優化,因為horpsot可以看到結果總是與循環中的迭代次數相同。
所有這些都指向動態編譯器(如熱點)的關鍵優勢 - 使用運行時分析,您可以優化在運行時實際執行的操作並消除冗余代碼。 畢竟,自定義C ++內存分配器的效率並不重要 - 不執行任何代碼總是會更快。
在我的框中,這個程序大約執行。 12秒; 令人驚訝的是,我在Java中使用類似的實現[使用String和ArrayList],它的運行速度比我的C ++應用程序快很多(大約2秒)。
我不能重現那個結果。
為了解釋Alex提到的優化,我修改了代碼,以便Java和C ++代碼在testvector
方法的末尾打印出v
向量的最后結果。
現在,C ++代碼(使用-O3
編譯)的運行速度與您的一樣快(12秒)。 Java代碼(直截了當,使用ArrayList
而不是Vector
雖然我懷疑這會影響性能,這要歸功於轉義分析)大約需要兩倍的時間。
所以這個結果絕不顯著我沒有做大量的測試。 它只是表明讓這些測試完全錯誤是多么容易,以及單個測試對真實性能的評價很少。
僅供記錄,測試是在以下配置上運行的:
$ uname -ms
Darwin i386
$ java -version
java version "1.6.0_15"
Java(TM) SE Runtime Environment (build 1.6.0_15-b03-226)
Java HotSpot(TM) 64-Bit Server VM (build 14.1-b02-92, mixed mode)
$ g++ --version
i686-apple-darwin9-g++-4.0.1 (GCC) 4.0.1 (Apple Inc. build 5490)
如果你使用Vector::reserve
在循環之前為v
z
元素保留空間應該會有所幫助(但是同樣的事情也應該加速這段代碼的java等價物)。
為了說明為什么C ++和java的性能不同,看兩者的來源都很重要,我可以在C ++中看到一些性能問題,對於一些人來說,看看你是否在java中做同樣的事情會很有用(例如通過std :: endl刷新輸出流,你是否調用System.out.flush()或只是附加一個'\\ n',如果后來你剛剛給了java一個明顯的優勢)?
你在這里真正想要測量的是什么? 將整數放入向量中?
您可以首先將空間預先分配到具有已知向量大小的向量中:
代替:
void testvector(int x)
{
vector<string> v;
int a = x * 2000;
int z = a + 2000;
string s("X-");
for (int i = a; i < z; i++)
v.push_back(i);
}
嘗試:
void testvector(int x)
{
int a = x * 2000;
int z = a + 2000;
string s("X-");
vector<string> v(z);
for (int i = a; i < z; i++)
v.push_back(i);
}
在你的內循環中,你將ints推入一個字符串向量。 如果你只是在機器代碼級別單步執行,我敢打賭你發現很多時間用於分配和格式化字符串,然后一段時間進入回退(更不用說你釋放時的釋放)矢量)。
根據開發人員對人們合理想要做的事情的理解,這可能會在運行時庫實現之間輕松變化。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.