Yaşar Safkan

Her konuda yazılar...

  • Ağırından
  • Hafifinden
  • Kısa Kısa
  • Google
  • Teknik
    • Programlama Taosu
  • Trafik
    • Park Başarıları
  • Üniversite
  • Link
Buradasınız : Ana Sayfa / Teknik / JDK8: Başlangıç, İlk Denemeler: Stream ve Paralellik

JDK8: Başlangıç, İlk Denemeler: Stream ve Paralellik

20/03/2014 By Yaşar Safkan 4 Yorum

Tam söz verilen günde olmasa da, JDK8 nihayet burada. Uzun zamandır Java dilinde yapılmış olan en büyük değişikliğe denk geliyor. Bundan önceki en büyük değişiklik, “Generics” eklenmesi olarak düşünülebilir, o da JDK5 ile gelmişti ki, o şu anda yapılan değişikliklerin yanında ufak kalır…

Hemen bir tur bineyim dedim. Java işlerini, virtual makina içinde Ubuntu var, onun üzerinde yapıyorum. (Neden direkt makinaya kurmuyorsun derseniz, driver falan uğraşmak işime gelmiyor. Bir de, virtual makinada backup falan kolay. İrice bir USB stick varsa, onun içinde taşınabiliyor da. Artık VMWare player da bedava.)

Sonuçta sistem Ubuntu. Java için de yıllardır Eclipse kullanırım. (Alışkanlık. Başka IDE’den hoşlanıyorsanız alınmayın, tepkilenmeyin.) Şimdi bize neler lazım. Bir JDK8 lazım. Maalesef openjdk henüz apt-get ile alınamadığından, Oracle’ın JDK8’ini indirecek bir şekil buldum. Eclipse developer arkadaşlar da boş durmamış, onun için de Eclipse’in kendi derleyicisini güncellemişler. İki cümlede anlattığım meseleyi, üç saat kadar bir sürede, indirdimdi, güncelledimdi, eski Java versiyonun sistemden yok ettimdi derken tamamladım.

Ayarlar yapıldı. Direksiyona geçtim, nereye gideceğimi bilmiyorum. Ne yazsam ki şimdi? İlginci, notasyon da bilmiyorum. Saat de geç olmuştu. Sırt üstü yatıp dokümantasyon okudum biraz. Daha önce de okumuştum ama, şimdi maksatlı okuduğumdan, kod örneklerine bakarak okudum.

Denemeye değer duran şeylerden biri, Java’nın stream() meselesi ve bunun paralel versiyonu (ki parallelStream() ile oluyor). Yani, eğer Microsoft taraflarını biliyorsanız, oradaki Linq işlerine denk geliyor. Bana sorarsanız isimlendirmesi daha iyi olmuş en azından…

Şöyle bir deneyelim dedim ve ilk olarak aşağıdaki kodu yumurtladım:

package com.safkanyazilim.java8;

import java.time.Clock;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class Main {

	public static void main(String[] args) {
		List<Integer> list = new ArrayList<Integer>();
		Clock clock = Clock.systemDefaultZone();
		Random random = new Random();

		for (int i=0; i < 10_000_000; i++) {
                        list.add(random.nextInt(1_000_000));
                } 		 		

                Instant start = clock.instant(); 		

                double sum = list.stream().mapToInt(x -> x).sum();

		Instant end = clock.instant();

		System.out.println("Sum: " + sum);

		Duration duration = Duration.between(start, end);

		System.out.println("Duration : " + duration);
	}
}

Yeniliğin sürüsüne bereket! İkinci satırda Clock geçiyor. Clock, yeni bir şey, java.time paketinin içinden çıkıyor. Eğer bugüne kadar java.util.Date, java.util.Calendar manyaklığından daraldıysanız, size iyi gelecek. Artık zamanı ölçmek için Clock kullanıyoruz (mantıklı!). Zaman aralığı tutmak için Duration var (evet bu da mantıklı!). Test işleri için “durmuş” saat de yapabiliyoruz. Deliliğe son! System.currentTimeMillis() halen kullanılabilir, ama yukarıdaki koda bakarsanız, sanırım bir daha kullanmak istemeyeceksiniz. Benim kullanasım yok.

Random, bildiğimiz Random. Burada yeni bir şey yok. On milyon elemanlı bir ArrayList yaratıyorum. İçinde de sıfırdan bir milyona kadar tam sayılardan var. Kodun içindeki on milyon ve bir milyon sayılarını biraz fazla kolay okumuş olabilir misiniz? JDK8’de bu da yeni. Sayıların istediğiniz yerine “underscore” veya “alt tire” dediğimiz karakteri koyabiliyorsunuz. Mantıklı bir şekilde üçer üçer ayırırsanız, hayat tam olarak bayram olmasa da, gözleriniz şaşı olmuyor, ekranınızı parmaklamıyorsunuz veya içinizden sayarak sıfır tuşuna basmıyorsunuz artık. (Nerdeydiniz lan siz?)

Clock ile ilgili işleri açıklamıyorum. Arife tarif gerekmez. Zaman işleri olması gerektiği gibi basit olmuş.

Mesele şu satırda olup bitiyor:

double sum = list.stream().mapToInt(x -> x).sum();

JDK8 ile, tüm Collection sülalesi, stream() diye yeni bir metod sahibi olmuş. Yaptığı iş, “Map Reduce” tarzı işler. Yani, her bir eleman üzerinden iterate edilecek yapılabilecek işleri, böyle de yapabiliyorsunuz. (Başka dillerde yıllardır var diyecekler hele bir geri dursun, biliyoruz onu. Geç oldu, güç de oldu, ama gel gelelim oldu, anlatıyoruz.) Bu satırda şöyle diyoruz: Listeyi al. Akıt onu dere gibi. Sonra her elemanı “int”e çevir (Map et diyesim var. Türkçesine ne diyeceğiz bunun? Evir?) Bunu da nasıl yapacağını söylemek lazım tabi. Burada yepisyeni “Lambda Expression” karşınızda. “x -> x” özetle şu demek: Argüman olarak x alan ve sonuç olarak x dönen bir fonksiyon. Son adım, “reduce” adımı, yani sonuçları bir araya getirme, toplama. Burada da, sum() diye hazır bir reduce fonksiyonu mevcut. Yani, bu satır ile, tüm sayıları toplamış oluyorum… Lambda ifadesi olarak, x -> x*x deseydim de, karelerini toplamış olacaktım.

Çalıştırdık. Çıktısı şöyle oldu:

Sum: -8.20944701E8
Duration : PT0.045S

Sizde her iki sayı da farklı çıkabilir tabii. Duration çıktısı böyle biraz garip, ama özetle iş 45 milisaniye sürdü diyor. Eh, on milyon sayı topladın, sen de haklısın. Biz bunları neden yaptık? Paralellik görecektik. Abiler diyor ki, işlemi paralel yapmak için, stream() yerine parallelStream() yazmanız yeterli. Eğer tabi tanımladığınız işlem için olabilecek gibiyse… Bizimki basit toplama, bunun için olmayacaksa ne için olacak? Haydi değiştirdik satırı, şöyle yaptık:

double sum = list.parallelStream().mapToInt(x -> x).sum();

Haydi bir daha çalıştır. Süper olacak şimdi her şey. Çalıştırdık. Sonuç:

Sum: 8.9358852E7
Duration : PT0.063S

Lan?!? Hani daha hızlı olacaktı ya bu? Yavaşladı resmen!

Ne oldu? Aslında düşününce, olanı bulmak çok zor değil. Elemanları toplamak o kadar, o kadar basit bir işlem ki, işlemi paralelleştirme maliyeti, işlemin kendi maliyetinin ötesine geçiyor. Bu meselenin etkisini görmek için, daha zor bir iş yaptırmak gerek. Yani, her bir eleman üzerinde yapılacak olan işlem biraz daha uzun sürmeli ki, paralelleştirdiğimize değsin.

Ne yapayım? Zaman alacak bir şey yaptırayım deyince de insanın aklına bir şey gelmiyor. Yıllar önce hesap makinasında bulduğum bir oyun geldi aklıma. Hesap makinasındaki “cos” tuşuna tekrar tekrar basarak, belli bir sayıya yakınsayabiliyorsunuz. İlk sayı ne olursa olsun. Bu sayı sıfır da olmuyor. (x = cos(x) denklemin çözümü oluyor.)  Onun için kodu şu hale soktum:

package com.safkanyazilim.java8;

import java.time.Clock;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class Main {

	public static double transform(double x) {
		for (int i = 0; i < 100; i++) {
			x = Math.cos(x);
		}

		return x;
	}

	public static void main(String[] args) {
		List<Double> list = new ArrayList<Double>();
		Clock clock = Clock.systemDefaultZone();
		Random random = new Random();

		for (int i=0; i < 10_000_000; i++) {
			list.add(random.nextDouble());
		}

		Instant start = clock.instant();

		double sum = list.stream().mapToDouble(Main::transform).sum();

		Instant end = clock.instant();

		System.out.println("Sum: " + sum);

		Duration duration = Duration.between(start, end);

		System.out.println("Duration : " + duration);
	}
}

Tabii bir kaç değişiklik var. Kosinüs kullanmaya niyet edince, sayılar double’a döndü. Bir de, lambda ifadesi olan yere, Main::transform yazdık. Bu da JDK8’de yeni. Metod referansı. Argüman tipleri falan doğru olunca, metodu böyle verip, “al bunu kullan” diyebiliyoruz artık. Bunun yerine x -> Main.transform(x) de diyebilirdik, aynı anlama gelirdi.

Bastık, çalıştırdık… Sonuç:

Sum: 7390851.332151607
Duration : PT40.925S

Hah. cos(cos(cos(cos(cos…. (x….))))) hesaplatınca, makinayı yeterince yorduk. Çalışırken, “top” ekranına bakarsak, durum şöyle görünüyor:

top - 21:15:00 up 11:40,  2 users,  load average: 0.47, 0.30, 0.20
Tasks: 215 total,   1 running, 214 sleeping,   0 stopped,   0 zombie
Cpu0  :100.0%us,  0.0%sy,  0.0%ni,  0.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Cpu1  :  0.0%us,  0.3%sy,  0.0%ni, 99.7%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Cpu2  :  0.0%us,  0.0%sy,  0.0%ni,100.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Cpu3  :  0.3%us,  0.0%sy,  0.0%ni, 99.7%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Mem:   4042108k total,  3379976k used,   662132k free,   110600k buffers
Swap:  4192252k total,     2824k used,  4189428k free,  1039376k cached

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
12665 safkan    20   0 3320m 416m  11m S  100 10.5   0:10.49 java
 1331 root      20   0  240m  92m 9968 S    0  2.3   3:54.91 Xorg
 2597 safkan    20   0  384m  18m  14m S    0  0.5   0:53.43 vmtoolsd
12663 safkan    20   0 17460 1404  980 R    0  0.0   0:00.05 top
    1 root      20   0 24580 2356 1352 S    0  0.1   0:01.79 init
    2 root      20   0     0    0    0 S    0  0.0   0:00.02 kthreadd
    3 root      20   0     0    0    0 S    0  0.0   0:02.86 ksoftirqd/0
    5 root       0 -20     0    0    0 S    0  0.0   0:00.00 kworker/0:0H
    7 root       0 -20     0    0    0 S    0  0.0   0:00.00 kworker/u:0H
    8 root      RT   0     0    0    0 S    0  0.0   0:02.99 migration/0
    9 root      20   0     0    0    0 S    0  0.0   0:00.00 rcu_bh

Java prosesi, tam olarak bir işlemcinin canına okuyor. Yani, beklediğimiz üzere, paralel olan biten herhangi bir durum yok. Olay kırk saniye sürdüğü için de, “top” ekranına bakmak mümkün oluyor.

Şimdi bakalım dananın kuyruğu kopacak mı? Satırı yeniden değiştirip, JDK8’den paralel işlem yapmasını istiyoruz:

double sum = list.parallelStream().mapToDouble(Main::transform).sum();

Haydi çalıştırdık, gözümüz saatte…. Sonuç:

Sum: 7390851.332151607
Duration : PT14.921S

Hızlandı gerçekten! Çalışırken, CPU fan, sağlam turbo vaziyetine de geçti. Bu esnada (elimizi çabuk tutup) top ekranına bakarsak (işlemin sonuna doğru) şöyle görünüyor:

top - 21:22:31 up 11:47,  2 users,  load average: 1.09, 0.52, 0.32
Tasks: 215 total,   1 running, 214 sleeping,   0 stopped,   0 zombie
Cpu0  : 99.3%us,  0.7%sy,  0.0%ni,  0.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Cpu1  : 99.3%us,  0.7%sy,  0.0%ni,  0.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Cpu2  : 99.7%us,  0.3%sy,  0.0%ni,  0.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Cpu3  :100.0%us,  0.0%sy,  0.0%ni,  0.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Mem:   4042108k total,  3385228k used,   656880k free,   110812k buffers
Swap:  4192252k total,     2824k used,  4189428k free,  1040508k cached

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
12782 safkan    20   0 3515m 416m  11m S  399 10.6   0:41.27 java
 1331 root      20   0  242m  94m 9968 S    0  2.4   4:01.91 Xorg
 2597 safkan    20   0  384m  18m  14m S    0  0.5   0:54.23 vmtoolsd
 5859 safkan    20   0 5801m 1.0g  30m S    0 25.7  12:51.79 java
12780 safkan    20   0 17460 1404  980 R    0  0.0   0:00.05 top
    1 root      20   0 24580 2356 1352 S    0  0.1   0:01.79 init
    2 root      20   0     0    0    0 S    0  0.0   0:00.02 kthreadd
    3 root      20   0     0    0    0 S    0  0.0   0:02.99 ksoftirqd/0
    5 root       0 -20     0    0    0 S    0  0.0   0:00.00 kworker/0:0H
    7 root       0 -20     0    0    0 S    0  0.0   0:00.00 kworker/u:0H
    8 root      RT   0     0    0    0 S    0  0.0   0:03.00 migration/0
    9 root      20   0     0    0    0 S    0  0.0   0:00.00 rcu_bh

Java gerçekten CPU’ları sağlam pataklıyor. Peki, neden 10-11 saniye olmadı da 14.2 saniye oldu? Her ne kadar virtual makinada dört CPU tanımlı ise de, altında yatan makina iki core sahibi, hyperthreading bir makina. Yani, iki CPU gerçek, diğer ikisi biraz fasulyeden…

Tabii bir anda bayram etmelik bir durum yok. “Yaşasın her şey hızlanacak” gibi bir durum da yok. Toplam gibi, paralelleşmeye çok süper uygun bir işlem kullandık. Ama, bir çok Collection için tüm elemanlara bağımsız olarak uygulanacak işlemleri, artık fazla efor kullanmadan paralelleştirmek mümkün…

Java’daki bu değişiklikler oldukça kökten. Java, “Kingdom of Nouns”, yani “İsimlerin Krallığı” olarak nitelenirdi. Yani, “birinci sınıf” olan şeyler, hep objelerdi. Objeden başka argüman, değişken olmadı hiç. Şimdi, bu değişime uğruyor. Artık metodlara referans verebiliyoruz. Lambda ifadelerimiz var, onunla biraz daha esnek sulara doğru yelken açıyoruz.

Java, “minimalist” bir dildi de. Yani, “bir şeyi yapmanın bir yolu vardır” geçerliydi. Her şeyi uzun yoldan yazardık, kolaylık sağlayan şeyler, dilin içinde yoktu. Bu stream() meselesi, bunun kırıldığı noktalardan biri. Keza, java.time için de aynı şey düşünülebilir; nihayet kendimiz “DateUtil.java” yazmadan Java kodu yazabileceğiz gibi görünüyor.

Daha bir kaç yazı daha yazarım diye umudum var… Bakalım ne olacak…

VN:F [1.9.13_1145]
please wait...
Rating: 4.8/5 (6 votes cast)
JDK8: Başlangıç, İlk Denemeler: Stream ve Paralellik, 4.8 out of 5 based on 6 ratings
8 I like This

Bunu paylaş:

  • Twitter üzerinde paylaşmak için tıklayın (Yeni pencerede açılır)
  • Facebook'ta paylaşmak için tıklayın (Yeni pencerede açılır)
  • Linkedln üzerinden paylaşmak için tıklayın (Yeni pencerede açılır)
  • WhatsApp'ta paylaşmak için tıklayın (Yeni pencerede açılır)
  • Arkadaşınıza e-posta ile bağlantı göndermek için tıklayın (Yeni pencerede açılır)

İlgili

Kategori:Teknik

Yorumlar

  1. Emre Sevinç der ki

    04/04/2014 ile 11:39

    Eline saglik! Keyifle okudum, birkac sey ogrenip sevindim.

    2 eften puften not:

    1- top elbette baki lakin htop bir apt-get uzaginda. http://hisham.hm/htop/

    2- Su yazilari bir kenara not ettiydim, en yakin zamanda okumak uzere, senin de dusuncelerini merak ediyorum bu baglamda: A Java™ Parallel Calamity ve A Java™ Fork-Join Calamity

    VA:F [1.9.13_1145]
    please wait...
    Rating: 5.0/5 (1 vote cast)
  2. Erkin PEHLİVAN der ki

    16/05/2014 ile 15:08

    Teşekkürler, güzel yazı olmuş.
    Yalnız sayılarda “underscore” veya “alt tire” kullanımı JDK 7 ile geldi.
    İlgiyle okudum.

    VA:F [1.9.13_1145]
    please wait...
    Rating: 0.0/5 (0 votes cast)
  3. Yaşar Safkan der ki

    22/06/2014 ile 20:45

    Evet öyleymiş. O ara ben Java yazmamışım :-).

    VN:F [1.9.13_1145]
    please wait...
    Rating: 0.0/5 (0 votes cast)
  4. malik der ki

    26/04/2015 ile 09:42

    kodcu.com sitesinde 100 sayfalik ek java 8 ozellikleri mevcut. rahman usta yazmis
    http://kodcu.com/java-8-ebook/
    iyi calismalar

    VA:F [1.9.13_1145]
    please wait...
    Rating: 0.0/5 (0 votes cast)

Bir cevap yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

Arama

Popüler Yazılar

  • Güneşin Altında Her Şey Boş
  • Agile: Türk Kaşığıyla Amerikan Çikolatası

Beni takip etmek için

  • Email
  • LinkedIn
  • Medium
  • Twitter
  • YouTube

Son çıkanlar

  • Kitaplar, Kitaplar, Kitaplar… 16/07/2021
  • İnsana İnsan Emanet Etmek 23/04/2021
  • Planlama, Proje Yönetimi, Risk Yönetimi 23/04/2021
  • Kafamdaki Bölmeler 23/04/2021
  • “Zor” Merakı 17/04/2021

Son yorumlar

  • Yazılım yazılsa yazılsa kaç saat yazılır? için Emir
  • CV Nasıl Yazılır? Nasıl Yazılmaz? için Bahar
  • Kafamdaki Bölmeler için paslanmaz çelik evye bataryası
  • 8401 Makinayı Nasıl Kapattım? için Salih
  • 8401 Makinayı Nasıl Kapattım? için Tolga

En çok ziyaret edilenler

  • CV Nasıl Yazılır? Nasıl Yazılmaz? Ne belalı iştir şu CV yazmak. Özellikle ilkini. Yalnız, nedense… (73.217)
  • Neden Türkiye’de Hasan Beyin Takımı Olur Da,… Böyle yazı başlığı mı olur? Başlıkları vurucu yapınca daha çok… (12.515)
  • Maliyet Hesabı Maliyet hesabı... Düşündünüz mü hiç, ne kadar önemli bir meseledir… (9.168)
  • Yazılım Kariyerinin Başındakilere Ukalalıklar… İlk defa Google'da duyduğum bir laf var: "Haklıysan ukalalık değildir."… (8.841)
  • 8401 Makinayı Nasıl Kapattım? Artık on yılı geçti, ama Google hikayeleri askerlik hikayeleri gibi… (6.427)
  • Başlatmayın Erken Kalkmanızdan! Gördüğünüz yazı başlığı, ilk düşündüğüm başlığın, üç kademe efendileştirilmiş hali.… (6.150)
  • Agile: Türk Kaşığıyla Amerikan Çikolatası Doğru, onun aslı çikolata değil. Hem çikolata kaşıkla yenmez. Nutella… (6.093)
  • Meslek Seçimi Hikayesi "Meslek sahibi olana meslek seçmesi kolay" dememişler, ben olsam derdim.… (5.223)
  • Tartışmak Nedir, Nasıl Olur? "Tartışmak", kelime kökeni olarak açık şekilde "tart"maktan türemiş bir kelimedir.… (4.598)
  • Yöneticilik ve Liderlik Üzerine Bunların üzerine yazıp çizen çok... Bir ukalalık da ben edeyim,… (4.257)
  • Arşiv

Sosyal Medya’da

Follow Us on TwitterFollow Us on LinkedInFollow Us on YouTube

Bloga e-posta ile abone ol

Bu bloga abone olmak ve e-posta ile bildirimler almak için e-posta adresinizi girin.

MOST LIKED POSTS

  • Neden Türkiye'de Hasan Beyin Takımı Olur Da, Beyin Takımı Olmaz? (55)
  • Yazılım Kariyerinin Başındakilere Ukalalıklar... (40)
  • 8401 Makinayı Nasıl Kapattım? (34)
  • CV Nasıl Yazılır? Nasıl Yazılmaz? (30)
  • Öğrenemediklerim... (30)

Copyright © 2006-2017 · News Pro Theme On Genesis Framework · WordPress