İÇİNDEKİLER. İşletim Sistemleri



Benzer belgeler
İşletim Sistemleri. Dr. Binnur Kurt Omega Eğitim ve Danışmanlık İşletim Sistemleri

Multicore/Multithread Programlama

Bilgisayar İşletim Sistemleri BLG 312

Proses. Prosesler 2. İşletim Sistemleri

PROSESLER. Proses. Proses

Giriş. geleneksel işletim sistemlerinde her prosesin. aynı adres uzayında birden fazla akış kontrolü gerekebilir

BMS-302 İleri Web Programlama. İş Parçacığı (Thread) ve Soket (Socket) Programlama

C++ Giriş Ders 1 MSGSU Fizik Bölümü Ferhat ÖZOK Kullanılacak kaynak: Published by Juan Soulié

C++ Dersi: Nesne Tabanlı Programlama

BM-209 Nesne Yönelimli Programlama. Yrd. Doç. Dr. İbrahim Alper Doğru Gazi Üniversitesi Teknoloji Fakültesi Bilgisayar Mühendisliği Bölümü

İşletim Sistemlerine Giriş

Nesne İşaretçileri. Binnur Kurt Bilgisayar Mühendisliği Bölümü İstanbul Teknik Üniversitesi. Sınıf Yapısı. Kalıtım Çok Şekillilik

Giriş. İplik Modeli. geleneksel işletim sistemlerinde her prosesin özel adres uzayı ve tek akış kontrolü var.

Android e Giriş. Öğr.Gör. Utku SOBUTAY

Akıllı telefonlar, avuçiçi bilgisayarlar ile taşınabilir (cep) telefonların özelliklerini birleştiren cihazlardır. Akıllı telefonlar kullanıcıların

İŞLETİM SİSTEMLERİ. (Operating Systems)

Java da Soyutlama ( Abstraction ) ve Çok-biçimlilik ( Polymorphism )

1. Aşağıdaki program parçacığını çalıştırdığınızda result ve param değişkenlerinin aldığı en son değerleri ve programın çıktısını yazınız.

Sunum İçeriği. Programlamaya Giriş

İşletim Sistemleri. Dr. Binnur Kurt Omega Eğitim ve Danışmanlık İşletim Sistemleri

Bölüm 4: Threads (İş Parçaları)

1 PROGRAMLAMAYA GİRİŞ

PROCESS YARATIMI (TEKRAR):

MAT213 BİLGİSAYAR PROGRAMLAMA I DERSİ Ders 1: Programlamaya Giriş

KOCAELİ ÜNİVERSİTESİ MÜHENDİSLİK FAKÜLTESİ

İŞLETİM SİSTEMİ KATMANLARI (Çekirdek, kabuk ve diğer temel kavramlar) Bir işletim sisteminin yazılım tasarımında ele alınması gereken iki önemli konu

Bölüm 4: İş Parçacıkları. Operating System Concepts with Java 8 th Edition

4. Bölüm Programlamaya Giriş

Yrd. Doç. Dr. Caner ÖZCAN

C++ Dersi: Nesne Tabanlı Programlama

Diziler. Yrd.Doç.Dr.Bülent ÇOBANOĞLU

Bilgisayar İşletim Sistemleri BLG 312

NESNEYE YÖNELİK PROGRAMLAMA

İŞLETİM SİSTEMİ KATMANLARI (Çekirdek, Kabuk ve diğer temel kavramlar) Öğr.Gör. Dr. Dr. Şirin KARADENİZ

Yrd. Doç. Dr. Caner ÖZCAN

Sınav tarihi : Süre : 60 dak.

BĠLGĠSAYAR PROGRAMLAMA II C++ Programlamaya GiriĢ Published by Juan Soulié

YZM 2105 Nesneye Yönelik Programlama

Bölüm 9. Altprogramlar ISBN

Bölüm 11. Soyut veri tipleri ve kapsülleme kavramları ISBN

Bilgisayar İşletim Sistemleri BLG 312

// hataları işaret eden referans

/*Aşağıda ki kodları doğru şekilde anlar ve kullanırsanız java da sınıfları biraz da olsa anlamış olursunuz.*/

Süreç 1 Kavramı ve Oluşturma Yöntemleri

BİL132 Bilgisayar Programlama II

C++ ile Nesneye Dayalı Programlama

TEMPLATES. Binnur Kurt Bilgisayar Mühendisliği Bölümü İstanbul Teknik Üniversitesi. C++ ile Nesneye Dayalı Programlama 1

Temel Bilgisayar Bilimleri Ders Notu #4-2. kısım

İsimler ve Kapsam. 24 Şubat Programlama Dilleri - Pamukkale Üniversitesi 1

Java 2 Standart Edition SDK Kurulum ve Java ya Giriş

Eclipse, Nesneler ve Java 2 Java Nereden Çıktı? 2

C++0x - Sağ Taraf Değerine Bağlanan Referanslar (Rvalue References)

İşletim Sistemleri. Dr. Binnur Kurt Omega Eğitim ve Danışmanlık İşletim Sistemleri

İşletim Sistemleri. Dr. Binnur Kurt Omega Eğitim ve Danışmanlık İşletim Sistemleri

Lecture 11: Generics

Operatörlere Yeni İşlevler Yüklenmesi (Operator Overloading)

PORT HABERLEŞME SERİ PORT FARUK BOZAN

Qt ile Bir Ağ Uygulaması

BTEP243 Ders 3. class Yazım Kuralı:

C++ Dersi: Nesne Tabanlı Programlama

Fonksiyonlar. C++ ve NESNEYE DAYALI PROGRAMLAMA 51. /* Fonksiyon: kup Bir tamsayının küpünü hesaplar */ long int kup(int x) {

10/17/2007 Nesneye Yonelik Programlama 3.1

NESNEYE YÖNELİK PROGRAMLAMA SINIFLAR

NESNEYE YÖNELİK PROGRAMLAMA C++ a Giriş

String ve Karakter Dizileri. Yrd. Doç. Dr. Fehim KÖYLÜ Erciyes Üniversitesi Bilgisayar Mühendisliği Bölümü

C de Detaylı Üs Alma Programı. C# Dilinde Metot Tanımlama ve Yazdırma

Bil101 Bilgisayar Yazılımı I. M. Erdem ÇORAPÇIOĞLU Bilgisayar Yüksek Mühendisi

BBS515 Nesneye Yönelik Programlama. Ders 1 Zümra Kavafoğlu

Görsel Programlama DERS 11. Görsel Programlama - Ders11/ 1

BLM 112- Programlama Dilleri II. Hafta 5 İşaretçiler (Pointers)

BİLG Dr. Mustafa T. Babagil 1

PROGRAMLAMAYA GİRİŞ. Öğr. Gör. Ayhan KOÇ. Kaynak: Algoritma Geliştirme ve Programlamaya Giriş, Dr. Fahri VATANSEVER, Seçkin Yay.

Arş.Gör.Muhammet Çağrı Gencer Bilgisayar Mühendisliği KTO Karatay Üniversitesi 2015

İş Parçacıkları Thread(s)

BMH-303 Nesneye Yönelik Programlama

işlemler bittikten sonra dosyaların kapatılması uygun olacaktır. Bunun için, fclose(fin);

public class SalesLineItem // Java { private int quantity; private ProductSpecification description; public Money getsubtotal() {...

İşletim Sistemleri. İşletim Sistemleri. Dr. Binnur Kurt Omega Eğitim ve Danışmanlık

C++ Dersi: Nesne Tabanlı Programlama

Pointers (İşaretçiler)

Binnur Kurt İstanbul Teknik Üniversitesi Bilgisayar MühendisliM

Operator Aşırı Yükleme (Operator OverLoading)

PROGRAMLAMAYA GİRİŞ FONKSİYONLAR

İsimler ve Kapsam. Hafta 4 Ders 2 BLG339 PROGRAMLAMA DİLLERİ KAVRAMI

Nesneye Dayalı Programlama Laboratuvarı

İşletim Sistemleri. Hazırlayan: M. Ali Akcayol Gazi Üniversitesi Bilgisayar Mühendisliği Bölümü

Veri Yapıları Lab Notları 1

Örnek1: #include <iostream> #include <string> using namespace std;

Üst Düzey Programlama

Kurucu Fonksiyonlar (Constructors)

public static int Toplam int x, int y

Statik veri üyeleri sınıf dosyası içerisinde, ancak sınıf bildirimi dışında başlatılmalıdır. Statik üye fonksiyonları

OpenGL Uygulamaları. 1. Giriş. 2. OpenGL. Deney 2

İşletim Sistemleri-II

Uzaktan Eğitim Uygulama ve Araştırma Merkezi

Programlama Dili Prensipleri. Lab Notları 1

Windows'da çalışırken pek çok durumda bir işe başlamadan önce işletim sisteminin o işe ilişkin bilgileri depolayacağı bir alan yaratması gerekir.

BİL-141 Bilgisayar Programlama I (Java)

NESNEYE YÖNELİK PROGRAMLAMA

JAVA API v2.0 Belge sürümü: 2.0.2

Transkript:

İşletim Sistemleri Dr. Binnur Kurt binnur.kurt@gmail.com Omega Eğitim ve Danışmanlık http://www.omegaegitim.com 1 S a y f a

İÇİNDEKİLER 1. İşletim Sistemi 2. Kabuk 3. Prosesler 4. 5. Prosesler Arası İletişim 6. İş Sıralama 7. Ölümcül Kilitlenme 8. Çok İplikli Programlama 9. Bellek Yönetimi 10. Dosya Sistemi 11. Soket Haberleşme 2 S a y f a

BÖLÜM 4 Bölümün Amacı Bölüm sonunda aşağıdaki konular öğrenilmiş olacaktır: İplik Modeli Linux da PTHREAD Kütüphanesi kullanarak çok iplikli uygulama geliştirmek C++11 de çok iplikli uygulama geliştirmek Java da çok iplikli uygulama geliştirmek 3 S a y f a

4.1 Giriş de prosesler gibi birden fazla görevi yerine getirmek için kullanılır. de prosesler gibi işlemciyi zamanda paylaşarak kullanılırlar. Eğer birden fazla işlemci ya da çekirdek varsa çekirdek sayısı kadar iplik ya da proses gerçekten paralel olarak çalışabilir. proseslere göre daha hafif sıklet bir çözüm sunar. Yeni bir iplik yaratıldığında sadece yığın için yer ayrılır. Ait olduğu prosesin heap ve text alanlarını diğer ipliklerle beraber paylaşır. ile veri ve görev paralelliği daha ince ölçekte gerçeklenebilir. Proses fork sistem çağrısı kullanılarak yaratılır: #include <unistd.h> pid_t fork(void); Çağrıyı yapan prosesin bellek görüntüsü birebir kopyalanarak çocuk proses yaratılır. Bu yüzden fork ile proses yaratmak maliyetlidir. Eğer çocuk proses ebeveyn prosesin bellek alanında bir değişiklik yapmayacaksa, Linux işletim sisteminde, fork sistem çağrısına göre daha hızlı çalışan vfork sistem çağrısını kullanmak gerekir: #include <unistd.h> pid_t vfork(void); vfork paylaşılan bellek alanına yazma yapıldığında kopyalama yapar. Bu en iyileme literatürde copy-on-write olarak adlandırılmaktadır. Özellikle çocuk proses exec sistem çağrılarından birini kullanacaksa, fork yerine vfork kullanılmalıdır. İplik yaratmak için ise clone sistem çağrısı kullanılır: #include <sched.h> int clone(int (*fn)(void *), void *child_stack,int flags, void *arg,...); Aşağıda clone sistem çağrısı kullanılarak yaratılmış bir ipliğin yer aldığı örnek bir uygulama bulunmaktadır: Kod 4.1: #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <sched.h> #define CHILD_STACK_SIZE 16384 int var; int do_something(void *) { printf("i am in thread. Assigning 42 to the variable.\n"); var = 42; int main(int argc, char *argv[]) { void **child_stack; var = 9; child_stack = (void **) malloc(child_stack_size); printf("i am in the process! The variable was %d\n", var); clone(do_something, child_stack+child_stack_size/sizeof(void**), 4 S a y f a

CLONE_SIGHAND CLONE_FS CLONE_VM CLONE_FILES, NULL); sleep(1); printf("i am in the process. The variable is %d\n", var); return 0; CLONE_VM sabiti ipliğin prosesin bellek uzayını paylaşmasına neden olur. var değişkenine P prosesi tarafından CLONE_VM seçeneği ile yaratılan tüm iplikler erişebilir (Şekil-4.1). P var t 1 t 2 t 3 Şekil-4.1 Proses ve ipliklerin bellek erişimleri 3.2 PTHREAD Kütüphanesini Kullanarak İplik Yaratmak clone gibi sistem çağrılarını kullanarak iplik programlamak güçtür. İplik programlamayı bu nedenle POSIX Thread Kütüphanesi gibi bir üst düzey API kullanarak gerçekleştiriyoruz. Yaratılan her ipliğe o ipliğin çalıştırmasını istediğimiz fonksiyonun adresini ve parametresini veriyoruz. PThread Kütüphanesi kullanarak iplik yaratmak için pthread_create fonksiyonunu kullanıyoruz: #include <pthread.h> int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void*(*start_routine)(void*), void *arg) Burada birinci parametre ipliğin kimliğini saklamak istediğimiz bellek gözünün adresini ve ikinci parametre ise yaratmak istediğimiz ipliğin özelliklerini almaktadır. Üçüncü parametre ile ipliğin çalıştırmasını istediğimiz fonksiyonun adresini ve dördüncü parametre ile bu fonksiyona geçirmek istediğimiz parametrenin değerini veriyoruz. PTHREAD kütüphanesindeki tüm fonksiyonlar ve veri yapıları pthread_ ön eki ile başlar. Aşağıdaki uygulamada (Kod-4.2) 5 adet iplik yaratılmaktadır. çalışmaya başladıktan sonra ekrana bir ileti gönderip uyutulmaktadır. Ebeveyn proses ise bu beş ipliğin işini bitirmesini beklemek üzere pthread_join çağrısı yapmaktadır. Yaratılan her ipliğe ulaşmak üzere pthread_t tipinde bir veri yapısı iliştirilir. Tüm PTHREAD kütüphane fonksiyonlarda iplik üzerinde işlem yapmak amacı ile referans olarak bu değer kullanılır. 5 S a y f a

Yaratılan ipliğin yürüteceği fonksiyon herhangi bir tipten değer alabilir ve benzer şekilde herhangi bir tipten değer döndürebilir. Parametre aktarımı pthread_create çağrısı ile iplik yaratılırken gerçekleşir: pthread_create( &tid[i], NULL, sleeping, (void *)(SLEEP_TIME+2*i) ); İpliğin döndürdüğü değere ise pthread_join çağrısı üzerinden erişilir: pthread_join( tid[i], (void **)&return_value); Kod 4.2: #include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <iostream> using namespace std; #define NUM_THREADS 5 #define SLEEP_TIME 10 pthread_t tid[num_threads]; void* sleeping(void *); void thread_start() { for (int i = 0; i < NUM_THREADS; i++) { pthread_create( &tid[i], NULL, sleeping, (void *)(SLEEP_TIME+2*i)); void thread_wait() { int return_value; for (int i = 0; i < NUM_THREADS; i++) { pthread_join( tid[i], (void **)&return_value); cout << "Thread " << i << " joins...!" << endl ; void* sleeping(void * arg) { int sleep_time= *((int*)(&arg)); cout << endl << "Sleeping for " << sleep_time << " seconds" << endl ; sleep(sleep_time); 6 S a y f a

return ((void *)sleep_time); int main() { thread_start(); thread_wait(); return 0; Bazen iplik fonksiyonu birden fazla parametre alması ya da birden fazla değer döndürmesi gerekebilir. Bu durumda parametre ya da dönüş değeri için struct ile bir tip tanımlanır. Birden fazla değer struct alanları olarak tanımlanır: struct problem { long *begin; long *end; problem(long *_begin,long *_end): begin(_begin), end(_end){ ; Aşağıdaki örnekte yüz milyon elemanlı bir dizinin elemanları toplamı, çekirdek sayısı kadar iplik yaratılarak, paralel olarak çözülmeye çalışılmaktadır: Kod 4.3: #include <iostream> #include <pthread.h> #include <time.h> using namespace std; const long PROBLEM_SIZE=100000000; const int NUMBER_OF_CORE= pthread_num_processors_np(); struct problem { long *begin; long *end; problem(long *_begin,long *_end): begin(_begin), end(_end){ ; long numbers[problem_size]; void init_array(long *array,long size){ for (long i=0,*p=array;i<size;++i,++p) *p=i; void * parallel_sum(void* _problem){ problem* prob= (problem*) _problem; long sum=0; for (long *q=prob->begin;q!=prob->end;++q) { 7 S a y f a

sum += *q; return new long(sum); void serial_sum(long *array,int size){ long sum=0l; long* end=array+size; for (long *p=array;p!=end;++p) sum += *p; cout << "Sum (serial): " << sum << endl; int main(){ init_array(numbers,problem_size); pthread_t threads[number_of_core]; problem **probs= new problem*[number_of_core]; long per_thread= PROBLEM_SIZE / NUMBER_OF_CORE; long* beg= numbers; long* end= beg + per_thread; for (int i=0;i<number_of_core;++i){ probs[i]= new problem(beg,end); beg = beg + per_thread; end= end + per_thread; for (int i=0;i<number_of_core;++i){ pthread_create( threads+i, 0L, parallel_sum, (void*)probs[i] ); long sum=0l; for (int i=0;i<number_of_core;++i){ int *result; pthread_join(threads[i], (void**)&result); sum += *result; delete result; cout << "Sum (parallel): " << sum << endl; for (int i=0;i<number_of_core;++i){ delete probs[i]; delete[] probs; serial_sum(numbers,problem_size); 8 S a y f a

3.3 C++11 de İplik Yaratmak C++, 2011 yılında çıkan yeni sürümü ile günümüz modern programlama dillerinde bulunan birçok ileri düzey özelliğe kavuşmuştur. Nihayet paralel programlama için kullandığımız iplikler işletim sistemine bağımlı olmaktan kurtuldu ve dilin bir parçası haline geldi. C++11 de thread kütüphanesi ile gelen thread adında yeni bir sınıf yer alır. C++11 de iplik yaratmanın 3 farklı yolu vardır. 1. Fonksiyon kullanımı thread sınıfının kurucu fonksiyonu ilk parametre olarak çalıştırılacak fonksiyonu alır (Kod 4.4). Aşağıdaki örnekte t1 ve t2 adında iki iplik yaratılır. Bu iplikler hello fonksiyonunu çalıştırırlar. Kod 4.4: #include <iostream> #include <thread> using namespace std; void hello(){ cout << "Hello Mars!" << endl ; int main(){ thread t1(hello); thread t2(hello); t1.join(); t2.join(); cout << "Done." << endl; 2. struct Kullanımı Burada ilk önce, () operatörüne işlev yüklenen bir struct tasarlanır. thread sınıfının kurucu fonksiyonu ilk parametre olarak tasarlanan bu struct bir nesne alır (Kod 4.5). Aşağıdaki örnekte t1 ve t2 adında iki iplik yaratılır. Bu iplikler struct içinde tanımlı void operator()() fonksiyonunu çalıştırırlar. Kod 4.5: #include <iostream> #include <thread> using namespace std; struct fun { void operator()(){ cout << "Hello Mars!" << endl ; ; 9 S a y f a

int main(){ thread t1((fun())); thread t2((fun())); t1.join(); t2.join(); cout << "Done." << endl; 3. İfadesi Kullanımı C++11 ile birçok yenilik geldi. Bu yeniliklerden bir diğeri, C++11 de artık fonksiyonel programlama yapılabiliyor olmamızdır. thread sınıfının kurucu fonksiyonu ilk parametre olarak artık ifadesi alabiliyor (Kod 4.6). Aşağıdaki örnekte t1 ve t2 adında iki iplik yaratılır. Bu iplikler run ve gun isimli ifadelerini çalıştırırlar. Kod 4.6: #include <iostream> #include <thread> using namespace std; int main(){ function<void()> run =[](){ cout << "Run forrest run!" << endl; ; auto gun = [](){ cout << "Hello Mars!" << endl; ; thread t1(gun); thread t2(run); t1.join(); t2.join(); cout << "Done." << endl; İplik fonksiyonuna parametre aktarmak mümkündür. Bunun için basitçe thread sınıfının yapıcısına çalıştırılacak fonksiyondan sonra sırayla parametre değerleri geçilir (Kod 4.7): thread t1(f,4) ve thread t2(f,8). Kod 4.7: #include <thread> #include <iostream> using namespace std; struct fun { void operator()(int value){ cout << "value= " << value << endl; 10 S a y f a

; int main(){ fun f; thread t1(f,4); thread t2(f,8); t1.join(); t2.join(); Aşağıdaki örnekte yüz milyon elemanlı bir dizinin elemanları toplamı, çekirdek sayısı kadar iplik yaratılarak, paralel olarak çözülmeye çalışılmaktadır (Kod 4.8). PTHREAD kullanılarak verilen çözüm ile karşılaştırıldığında C++11 de daha az kod yazılarak çözüme ulaşıldığı görülebilir. Kodda karşılaştığınız auto, async ve future C++11 ile gelen yenilikler. async ile yarattığımız ipliğin ürettiği sonuca future üzerinden asenkron bir şekilde erişilebilir. future sınıfının get çağrısını yapan proses, eğer iplik sonlanmamış ise bloke olmasına neden olur. Bloke olan proses iplik tamamlandığında kaldığı yerden devam eder. Eğer get çağrısı yapıldığında iplik sonlanmış ise çağrıyı yapan proses bloke olmadan sonucu ulaşır. Kodda üretken programlamanın (=Generic Programming) gücü hissediliyor. Aynı testi std::vector ile kodda neredeyse hiçbir değişiklik yapmadan gerçekleştirebiliriz. Kod 4.8: #include <iostream> #include <thread> #include <future> #include <algorithm> using namespace std; const int SIZE=80000000; int numbers[size]; template <typename iter> void init_problem(iter beg,iter end){ int i=1; for (iter p=beg;p!=end;p++,++i) *p= i; template <typename iter> int parallel_sum(iter begin, iter end){ long len= distance(begin,end); if (len <= 10000000){ return accumulate(begin,end,int()); iter mid= begin + len /2; auto handle_left= async(launch::async,parallel_sum<iter>,begin,mid); auto handle_right= async(launch::async,parallel_sum<iter>,mid,end); 11 S a y f a

return handle_left.get()+ handle_right.get(); int main(){ init_problem(numbers,numbers+size); int sum= parallel_sum(numbers,numbers+size); cout << "Sum (parallel): " << sum << endl ; 3.4 Java 8 de İplik Yaratmak Java 8 platformu üzerinde veri paralelliğini üç yöntemle gerçeklemek mümkündür. 1. Callable, Future ve Executor Kullanımı Java programlama dili ilk çıktığından itibaren ipliklerle programlama için bir çözüm sunuyor. Runnable arayüzünü gerçekleyen bir sınıf tasarlamakla işe başlıyoruz: public class HelloRunner implements Runnable { @Override public void run(){ System.out.println("Hello Mars!"); Ardından HelloRunner sınıfından bir nesne yaratıyoruz: HelloRunner runner= new HelloRunner(); Son olarak Thread sınıfından bir nesne yaratıyoruz ve Thread kurucusuna runner nesnesini parametre olarak veriyoruz: Thread t= new Thread(runner); C++11 deki thread sınıfından farklı olarak, Java da Thread sınıfından nesne yaratmak, ipliğin çalışması için yeterli değildir. Son olarak start metodunun çalıştırılması gerekir: t.start(); Runnable ipliklerin en kötü yönü run() metodunun herhangi bir parametre alamaması ve ipliğin bir değer döndürememesidir. Bu yüzden görevi yaratan iplik ile sonucu üretecek iplik arasında mutlaka bir eş güdüm oluşturmak gerekir. Java SE 5 ile gelen Callable arayüzü ile artık sonucu call() metodundan dönmek ve Future arayüzünün get() çağrısı ile bu sonuca asenkron bir şekilde ulaşmak mümkündür. C++11 in future sınıfı ile Java nın Future sınıfı aynı işlevi sunmaktadır. Java SE 5 ile gelen yeniliklerden biri de İplik havuzudur (=Thread Pool). Farklı havuz türleri ihtiyaca göre seçilebilir: Her zaman sabit sayıda ipliğin yer aldığı Fixed Thread Pool, Parlamalı çalışma modu için Cached Thread Pool, Yığın işler için Single Thread Pool, Periyodik işler için Scheduled Thread Pool. Ancak buradaki tüm yapılar alt düzeydedir. Bu yüzden bu yapı üzerinde uygulama geliştirmek vakit alır, test etmesi zordur, yarış durumları (=race conditions) ve ölümcül kilitlenme (=deadlock) gibi durumların iyi düşünülmüş olması gerekir. Örnek uygulama için kullanılan alan sınıfı Kod 4.9 da verilmiştir. Burada amacımız 60 milyon Programmer nesnesi arasından, 40 yaşın üstünde "en bilgili" Java programcısını bulmaktır. 12 S a y f a

Kod 4.9: public class Programmer implements Serializable { private int id; private String name; private String surname; private int age; private ProgrammingLanguage programminglanguage;... public class ProgrammingLanguage implements Serializable { private String name; private int level;... Önce seri çözüme bir bakalım. Seri çözümde liste içindeki Programmer sınıfı nesnelerini teker teker ziyaret edip en deneyimli Java programcısını tek bir iplik kullanarak bulmaya çalışıyoruz (Kod 4.10). Kod 4.10: private static Programmer serialsolve(list<programmer> list) { Programmer oldest = null; for (Programmer programmer : list) { if (programmer.getage() > 40 && programmer.getprogramminglanguage().getname().equalsignorecase("java")) { if (oldest == null) { oldest = programmer; else if (programmer.getprogramminglanguage().getlevel() > oldest.getprogramminglanguage().getlevel()) { oldest = programmer; return oldest; Callable iplik kullanılan çözüm ise Kod 4.11 de verilmiştir. Kod 4.11: private static long callablethread(list<programmer> list) throws InterruptedException { Programmer oldest = null; int numberofsegments = DATA_SIZE / SERIAL_THRESHOLD; ExecutorService es = Executors.newFixedThreadPool( Runtime.getRuntime().availableProcessors()); List<Callable<Programmer>> callables = new ArrayList<>(numberOfSegments); for (int i = 0, j = 0; 13 S a y f a

i < numberofsegments; ++i, j += SERIAL_THRESHOLD) { callables.add( new ProcessingThread(j, SERIAL_THRESHOLD, list) ); List<Future<Programmer>> partialsolutions = es.invokeall(callables); try { oldest = partialsolutions.get(0).get(); for (int i = 1; i < partialsolutions.size(); ++i) { Programmer programmer = partialsolutions.get(i).get(); if (programmer.getprogramminglanguage().getlevel() > oldest.getprogramminglanguage().getlevel()) { oldest = programmer; catch (InterruptedException ExecutionException ex) { System.err.println(ex.getMessage()); es.shutdown(); return oldest; public class ProcessingThread implements Callable<Programmer> { private final int start; private final int length; private final List<Programmer> list; public ProcessingThread(int start, int length, List<Programmer> list) { this.start = start; this.length = length; this.list = list; @Override public Programmer call() throws Exception { Programmer oldest = list.get(start); Programmer programmer; for (int i = start + 1, j = 1; j < length; ++j, ++i) { programmer = list.get(i); if (programmer.getage() > 40 && programmer.getprogramminglanguage().getname().equalsignorecase("java")) { if (programmer.getprogramminglanguage().getlevel() > oldest.getprogramminglanguage().getlevel()) { oldest = programmer; return oldest; 14 S a y f a

2. Fork/Join Çatısının (=Framework) Kullanımı Geliştirdiğimiz uygulamaların yüksek başarımla çalışması hepimizin arzu ettiği bir durum. Ancak bu her zaman ulaşılması kolay bir hedef olmayabilir. Özellikle günümüzde yazılımdan beklentilerin sürekli arttığı göz önüne alındığında, bu hedefe ulaşmak daha da zorlaşmaktadır. Başarımı iyileştirmeyi mutlaka yazılım geliştirme sürecinin (müşteriden isteklerin alınması, sistem çözümleme, detaylı tasarım, mimari tasarım, gerçekleme, sınama, bakım) bir parçası haline getirmeliyiz. Başarımı en iyilemek yazılım ortaya çıktıktan sonra yapılan bir etkinlik olmamalı. Java uygulamalarının başarımının en iyilenmesi ise farklı katmanlarda çalışmayı gerektirir. Donanım katmanı, İşletim sistemi katmanı, Java Sanal Makinası (JSM) katmanı ve elbette uygulama katmanı. Burada yer alan her bir katmandaki başarımı artırmak için ne yazık ki elimizde sihirli bir değnek bulunmuyor. Donanım katmanındaki gelişmeler elimizi güçlendiriyor: Çok çekirdekli mimariler, büyük bellekli bilgisayar sistemleri, daha yüksek kapasiteli cep bellekler (L0, L1, L2, L3), katı hal diskler, daha hızlı ara bağlaşım birimleri, çok çekirdekli yüksek başarımlı ekran kartı işlemcileri. İşletim sistemleri bu donanım kaynaklarını uygulamalar ve kullanıcılar arasında verimli bir şekilde dağıtmaktan ve yönetmekten sorumlu çok sayıda servisten oluşuyor: proses sıralayıcı, sanal bellek yönetimi, giriş/çıkış yönetimi, kullanıcı yönetimi, dosya sistemi, pencereleme sistemi. Linux (Red Hat, Suse), Unix (Oracle Solaris, IBM AIX, HP-UX), Microsoft Windows gibi işletim sistemleri bu güncel sistem kaynaklarını iyi yönettikleri söylenebilir. Yine de genel amaçlı işletim sistemi olmalarından kaynaklanan bazı darboğazlar yaşıyorlar. Java uygulamaları doğrudan işletim sistemi üzerinde çalışmazlar. Bir sanal makinaya ihtiyaç duyarlar: Java Sanal Makinası (JSM). JSM'nin görevi, kabaca, Java uygulamalarını oluşturan bytecode olarak isimlendirilen makina komutlarını, üzerinde çalıştığı işletim sisteminin ve işlemcinin anlayacağı ve çalıştırabileceği forma çevirmektir. Java platformunun en güçlü tarafının JSM olduğu söylenebilir. JSM bu dönüşümü yaparken devingen en iyileme yapabilmektedir. C/C++'da kod yazdığınızda ise derleyici ancak durağan en iyileme yapabilir. Durağan en iyileme ise başarımda kısıtlı bir iyileşme sağlayabilir. JSM içinde başarımı belirlemede önemli işlevi olan iki bileşen yer alır: Tam Anında Derleme ve Çöp Toplayıcı. Java uygulamaları çalışmaya başladıklarında yorumlamalı olarak çalışırlar. JSM uygulamayı çalıştırırken kesitini de alır. Eğer bir iyileştirme görüyorsa sınıf metotlarını bytecode'dan işlemcinin doğrudan çalıştırabileceği doğal komutlara dönüştürür. JSM satın alabileceğiniz, üretebileceğiniz bir işlemci tanımlar. Bu işlemcinin bir komut kümesi, yığın temelli adresleme kipleri, saklayıcı kümesi, yığın göstergesi, program sayacı, bellek modeli vardır. Çoğu zaman JSM'yi yazılımsal olarak ediniriz. Oracle, hemen hemen tüm işletim sistemleri için bir JSM yayınlıyor. Bu adresten elinizdeki işletim sistemi için bir JRE ya da JDK indirebilirsiniz. Eğer amacınız sadece Java uygulamalarını çalıştırmak ise Java Runtime Environment (JRE) yeterli olacaktır. Eğer uygulama geliştirmek isterseniz Java Geliştirme Çantasını (Java Development Kit, JDK) indirmeniz gerekir. Bu yazının yazıldığı tarihte, Java nın 8 sürümü vardı. Her yeni sürümde dilde bazı eklentiler, mevcut API'lerde değişiklikler ve yeni API'ler ile tanışıyoruz. Bu arada yeni sürümlerde bazen JSM'de de değişiklikler olabiliyor. Şu ana kadar ki yeni sürümlerde Java 1.2 ve Java SE 5'de dilde ciddi sayılabilecek yenilik geldi. Java 7 ile dilde gelen yenilikler daha çok geliştiricinin kodlama başarımını iyileştiren 15 S a y f a

türden. Java 7 de, Böl/Katıl çatısı olarak adlandırılan çok çekirdekli sistemlerde uygulama geliştirmek için yeni bir çözüm bulunuyor. Bu çözüm, Concurrency API ile gelen yeni bir iş parçası havuzu olan ForkJoinPool ve bu havuza atayacağımız iş parçacıklarını tanımladığımız RecursiveAction ve RecursiveTask sınıflarını kullanmaktadır. Fork/Join çatısı 1'e göre kodlaması daha kolay olsa da hala alt düzey bir API sunmaktadır. Üstelik problemi iki alt parçaya ayırmak ile seri olarak çözümü arasında genellikle veri boyutu üzerinden verilmesi gereken bir karar vardır. Fork/Join çatısı kullanılarak elde edilen çözüm ise Kod 4.12 de verilmiştir. Kod 4.12: private static long forkjoin(list<programmer> list) { long start = System.nanoTime(); ForkJoinPool pool = new ForkJoinPool(); ForkListProcessing flp = new ForkListProcessing(0, list.size(), list); Programmer oldest = pool.invoke(flp); System.err.println("Oldest [FJ]: " + oldest); long stop = System.nanoTime(); return (stop - start); public class ForkListProcessing extends RecursiveTask<Programmer> { private final int start; private final int length; private final List<Programmer> list; private static final int SERIAL_THRESHOLD = 5_000_000; public ForkListProcessing(int start, int length, List<Programmer> list) { this.start = start; this.length = length; this.list = list; @Override public Programmer compute() { if (length <= SERIAL_THRESHOLD) { return serialsolver(); else { int startleft = start; int lengthleft = length / 2; int startright = start + lengthleft; int lengthright = length - lengthleft; RecursiveTask<Programmer> left = new ForkListProcessing(startLeft, lengthleft, list); RecursiveTask<Programmer> right = new ForkListProcessing(startRight, lengthright, list); left.fork(); right.fork(); Programmer leftsolution = left.join(); Programmer rightsolution = right.join(); if (leftsolution.getprogramminglanguage().getlevel() >= 16 S a y f a

rightsolution.getprogramminglanguage().getlevel()) { return leftsolution; else { return rightsolution; private Programmer serialsolver() { Programmer oldest = list.get(start); Programmer programmer; for (int i = start + 1, j = 1; j < length; ++j, ++i) { programmer = list.get(i); if (programmer.getage() > 40 && programmer.getprogramminglanguage().getname().equalsignorecase("java")) { if (programmer.getprogramminglanguage().getlevel() > oldest.getprogramminglanguage().getlevel()) { oldest = programmer; return oldest; 3. Dönüştür-İndirge (=Map-Reduce) Çatısının Kullanımı Dönüştür-İndirge çatısı Java SE 8 ile gelmiştir ve alt tarafta Fork/Join çatısını kullanır. Collection API ile hazır olarak gelen paralel kaplar ve Dönüştür-İndirge çerçevesi kullanılarak, çekirdek sayısına göre ölçeklenebilir çözümlere hızlıca ulaşabilir. Dönüştür-İndirge çerçevesi kullanılarak elde edilen çözüm ise Kod 4.13 de verilmiştir. Koddan görüldüğü gibi Java 8 soyutlama düzeyini en üst düzeye çıkarıyor. Hiç iplik kullanmadan aynı problemi paralel olarak çözmeyi başardık. Bunu sağlayan Java 8 ile birlikte gelen Stream API sidir. Stream bir iş hattı gibi düşünülebilir. İş hattından sadece Programmer sınıfından nesneler akıyor. Bu nesneler akarken yapılacak işleri bir metot zinciri olarak verebiliyoruz: filter() ve reduce(). filter() ile akan nesneler arasından seçim yapabiliyoruz. İş hattından, sadece verdiğimiz koşula uyan programcıların akmasını sağlıyoruz. 17 S a y f a

Kod 4.13: private static Programmer mapreduce(list<programmer> list) { long start = System.nanoTime(); Programmer oldest = list.parallelstream().filter( (Programmer prg) -> prg.getage() > 40 && prg.getprogramminglanguage().getname().equalsignorecase("java") ).reduce( (left, right) -> left.getprogramminglanguage().getlevel() >= right.getprogramminglanguage().getlevel()? left : right ).get(); return oldest; 18 S a y f a