Java Yavaş mı? : Java’nın Performansı Üzerine – IV

Bir önceki yazıda Monte Carlo simulasyonu ile Pi sayısını hesaplama ve asal sayı bulma amaçlı iki algoritma üzerinden C++ ile Java’nın performanslarını karşılaştırmıştım. Sağolsun bazı okurlarım düşüncelerini iletirken, iki okurum da iki algortimayı kendi makinaların da deneyip sonuçlarını benimle paylaştılar. İki okurumun da sonuçları, benim elde ettiğimin tersine çıkmış durumda, yani onların elde ettiği ölçümlerde bu iki algoritmada C++, Java’dan daha hızlı çalışmış. Hatta kendileri benim bu sonuçları alırken nasıl bir konfigürasyona sahip olduğumu da sordular. Bu durumda ben de bu serinin dördüncüsü olarak yazdığım yazıyı bir öteye itip, araya bu yazıyı aldım.

Öncelikle açıkçası şunu ifade etmem gerekir ki benim “Java Yavaş mı? : Java’nın Performansı Üzerine” yazı dizisinde ispatlamaya çalıştığım şey Java’nın C++’tan hızlı olduğu değildir. Derdim hangisinin hızlı olup olmadığını da anlamak değildir. Bu yazılarda anlatmaya çalıştığım şey öncelikle “hız” kavramının muğlaklığı ve “dilin hızı” ile “uygulamanın hızı” kavramlarının arasındaki fark ve bu noktada mimarinin önemidir. Ve malesef bunlar, bu işin pratiğini yapanların kafasında çok da aydınlanmış kavramlar da değiller. Sonrasında ise hedefim, Java’nın yavaş olduğu iddiasının içinin dolu olmadığını göstermektir. Bunu da olabildiğince delilli yapmaya çalışıyorum, bu blogda devamlı yaptığım gibi. Zaten aşırı ideolojik bir ülkede olmamızdan dolayı üzerine kafa patlatılmış, delilli cümlelerle konuşma yerine kahvehane seviyesinde, bilgilesizce edinilmiş fikirlerle tartıştığımız için, bu dizide ben “Java yavaş” cümlesinin de tam da bu cinsten bir ifade ve delilsiz ve mesnetsiz bir inanış olduğu göstermeye çalışıyorum. Öte yandan 90’lı yılların başından bu yana bu sektörde olan birisi olarak zaten sadece iki algoritma çalıştırmakla diller arasında gerçek bir performans kıyaslaması olamayacağını biliyorum.

Bu açıklamadan sonra şimdi ben kendi kullandığım configürasyonlardan bahsedeyim.

  • Makinam, 16 GB RAM ve 8 çekirdekli i7 CPU’ya sahip, üzerinde El Capitan OS çalışan bir MacBook Pro.
  • C++ için  GNU g++ (GCC) 4.9.2 20141029 kullandım. Derleme komutu ise: g++ -O3 -std=c++11 -o SieveOfAtkin.out SieveOfAtkin.cpp
  • Java için ise Java(TM) SE Runtime Environment (build 1.8.0_45-b14), Java HotSpot(TM) 64-Bit Server VM (build 25.45-b02, mixed mode). Java ile compile ederken de çalıştırıken de hiç bir flag kullanmadım, tamamen varsayılan seçenekler geçerliydi.

Sieve Of Atkin algoritması için farklı n girdisi için mili saniye cinsinden yukarıdaki Java ve C++ konfigürasyonlarının sonuçları aşağıdadır. 5 defa çalıştırma sonucunda elde edilen ortalama çalışma süreleri şunlardır:

Program

10^6

10^7 10^8 10^9

2*10^9

SieveOfAtkin.cpp

g++ -O3 -std=c++11

7

69 1,021 18,434

46,246

SieveOfAtkin.java

16

84 1.025 18,507

47,752

Yukarıdaki tablodaki ölçümlerden Java için olanları tekrar etmedim, verileri daha önceki ölçümlerdir. Sadece C++ ölçümlerini tekrar ettim, çünkü okurlardan C++ ölçümlerine itiraz edenler olmuştu. Makinamdaki C++ derleyicisini yeniledim. C++ kodunu Compile ederken de “-O3” flagini kullandım. Bu şekilde C++ ölçümlerinin ciddi olarak iyileştiği görülüyor. Bu sonuçları Java açısından yorumlarsak, Java’nın yavaş olmadığı, C++ ile başa-baş bir performans sergilediği görülüyor.

Daha önceki ve şimdiki C++ ölçümlerini kıyaslamak gerekirse, kullandığım farklı opsiyonlarla performanslar aşağıdaki gibi olmaktadır. Bu sonuçlardan özellikle “-O3” performans flaginin ciddi bir etiye sahip olduğu anlaşılıyor. Fakat merak ettiğim şey neden bu flagin varsayılan halde geçerli olmadığı? Uzun süredir C++ ile ilgilenmediğim için belli ki bu konulara detaylıca bakmam gerekli.

Program

10^6

10^7 10^8 10^9

2*10^9

SieveOfAtkin.cpp

GNU g++ (GCC) 4.9.2

18

218 2,461 34,871

82,059

SieveOfAtkin.cpp

GNU g++ (GCC) 4.9.2

g++ -O3 -std=c++11 -o

7

69 1.021 18,434

46,246

 

Öte taraftan benzer ölçümleri uzun süre C ve C++ ile çalışmış bir arkadaşımdan da yapmasını istedim. Sağolsun Sieve of Atkin algoritması için detaylı ölçümler yapmış. Her farklı n girdisi için 3 ayrı çalıştırma yapmış ve ortalamasını ile beraber kaydetmiş. Elde ettiği ölçümler aşağıdaki gibi:

10^6
Konfigürasyon

1. run

2. run 3. run Ort.
GNU GCC (Release) -O3 -std=c++11

59

54 62 58
Java

74

90 75 80

 

10^7
Konfigürasyon

1. run

2. run 3. run Ort.
GNU GCC (Release) -O3 -std=c++11

408

396 411 405
Java

386

385 382 384

 

10^8
Konfigürasyon

1. run

2. run 3. run Ort.
GNU GCC (Release) -O3 -std=c++11

3,782

3,851 3,772 3,802
Java

3,430

3,415 3,388 3,411

 

Arkadaşım, n = 10^9 için bir kaç farklı C++ compilerı kullanarak çok farklı sonuçlar elde etmiş.

10^9
Konfigürasyon

1. run

2. run 3. run Ort.
GNU GCC mingw32-g++ (Debug)  -O2 -std=c++11

39,818

40,140 39,764 39,907
GNU GCC (Release) -O2 -std=c++11

37,604

37,598 38,642 37,948
GNU GCC (Release) -O3 -std=c++11

37,751

37,642  37,816 37,731
Visual C++ 2010 (Debug)

69,142

 – 69,142
Visual C++ 2010 (Release) /Ox (Maximum Optimization)

49,072

49,072
Java

34,619

34,418 34,409 34,482

 

2*10^9
Konfigürasyon

1. run

2. run 3. run Ort.
GNU GCC (Release) -O3 -std=c++11

75,175

75,175
Java

67,552

67,552

 

Arkadaşımın ölçümleri elde ettiği makinasının konfigürasyonu da şöyle:

  • Intel Core i7-2670QM CPU @ 2.2GHz 8 GB Bellek
  • 64 Bit Windows 7 İşletim Sistemi (service Pack 1)

 

Yukarıdaki ölçümler, farklı makinalarda ve farklı compilerlarla yapılmış olması açısından Java’nın performansı noktasında daha objektif sonuçlar verdiği kesin. Benzer şekilde, bu sonuçlar Java ile C++’ın algoritmik performanslarının kıyaslanabilir olduğunu gösteriyor. Elde ettiğimiz sonuçlar, bu iki dil arasında CPU’yu çalıştırma noktasında çok da fazla bir performansının farkının olmadığını, eğer varsa da, girdi arttıkça performansın Java lehine ilerlediğini gösteriyor. Sonraki yazılarda bu fark üzerine konuşacağız.

Bol performanslı günler dilerim.

 

Bu yazı toplam 459 defa görüntülenmiştir.