MPI İLE PARALEL PROGRAMLAMA
MPI ile Dinamik Yük Dağılımı Uygulaması
MPI Nedir?
Message Passage Interface: Dağıtık bellekli, mesaj geçmeli paralel hesaplamalarda standart oluşturabilecek mesaj geçme kütüphaneleri için bir tanımlamadır.
MPI’ın hedefi mesaj geçmeli programlar yazmak için geniş kullanıma sahip bir standart sağlamaktır. Arayüz; mesaj geçme için pratik, taşınabilir, etkin ve esnek bir standart kurmaya çalışır.
MPI Kullanmanın Nedenleri
- Standartlaştırma: MPI, standart sayılabilecek tek mesaj geçme kütüphanesidir. Bütün HPC platformlarında desteklenmiştir.
- Taşınabilirlik: MPI destekleyen başka bir platforma uygulamanızı taşıdığınız zaman kodunuzu değiştirmeye gerek yoktur.
- Performans: Satıcı implementasyonları performansı optimum edebilmek için donanım özelliklerini en üst düzeyde kullanabilecek şekildedir.
- Fonksiyonellik: 115’in üzerinde rutin ile.
- Kullanırlık: Bir çok implementasyonu vardır.
Mesaj Geçme Modeli
Dağıtık Bellek: Her işlemci sadece kendi CPU’su ile direk olarak erişilebileceği yerel belleğe sahiptir. Bir işlemciden diğerine veri iletimi ağ üzerinden gerçekleştirilir.
Mesaj Geçme: Verinin bir işlemcinin belleğinden diğer bir işlemcinin belleğine kopyalandığı yöntemdir. Dağıtık bellekli sistemlerde veri genellikle bir işlemciden diğerine ağ üzerinden bilgi paketleri olarak yollanır. Bir mesaj bir veya daha çok bellekten oluşabilir ve çoğunlukla yönlendirme ya da başka kontrol bilgileri içerir.
Mesaj Geçme Kütüphanesi: Uygulama kodunun içine gömülen bir rutin kümesini belirtir.
Send/Receive: Mesaj geçme verinin bir süreçten diğer bir sürece transfer olmasını icap ettirir. Yollayan ve alan süreçlerin birlikte çalışmasını gerektirir. Send işlemleri genellikle yollayan sürecin verinin yeri, boyu, tipi ve hedefi bilgilerini belirtmesini gerektirir. Receive işlemleri ilişkili Send işlemi ile eşleşmelidir.

Senkron / Asenkron: Senkron send işlemi alıcı process’in mesajı güvenli bir şekilde aldığını belirten onayından sonra sonlanacaktır. Asenkron send işlemleri alıcı süreç mesajı almadan tamamlanabilir.
Uygulama Tamponu: Yollanacak veya alınacak veriyi tutan adres uzayıdır.
Sistem Tamponu: Mesajları tutmak için kullanılan sistem uzayıdır. Sen/Receive işlemine bağlı olarak uygulama tamponundaki veri, sistem tampon uzayından kopyalanmak zorunda kalabilir. Haberleşmenin asenkron olmasına izin verir.

Blocking Communication
Eğer çağrının tamamlanması belirli bir olaya bağlı ise haberleşme rutini bloklanır. Göndermeler için, veri başarı ile yollanmalı veya sistem tampon uzayına güvenli bir şekilde kopyalanmalıdır. Alıcı için veri, alıcı tamponunda depolanmalıdır. Böylece veri kullanmak için hazır olacaktır.
Non-Blocking Communication
Eğer çağırma herhangi bir haberleşme olayının tamamlanmasını beklemeden dönüyorsa haberleşme rutini bloklanmayandır.
Bloklanmayan haberleşmeler performansı arttırmak amacıyla hesaplamayı haberleşme ile üst üste getirmek için kullanılırlar.
İleticiler ve Gruplar
MPI hangi süreç kümelerinin hangileri ile haberleşeceğini belirtmek için, communicator ve group adında nesneler kullanırlar. Çoğu MPI rutinleri communicator’ı argüman olarak belirtmenizi gerektirir.
MPI_COMM_WORLD, bütün MPI süreçlerini içeren önceden tanımlı ileticidir.

Rank
İleticinin içinde, her süreç başlatımlandığında sistem tarafından atanan kendi eşsiz integer belirtecine sahiptir. Rank bazen “process ID” olarak da adlandırılabilirler. Ranklar sıralıdır ve 0’dan başlarlar.
Programcı tarafından mesajın kaynağını ve hedefini belirtmek için kullanılırlar. Bir çok zaman program icrasını kontrol için uygulama tarafından şartlı olarak kullanılırlar. (if rank==0 do this / if rank==1 do that)
MPI’a Giriş
MPI kütüphane fonksiyonlarını kullanmak için header dosyasını include etmeliyiz.
#include "mpi.h"
MPI Çağrılarının formatı şu şekildedir:
rc = MPI_Xxxxx(parameter, ... )
Genel MPI Programı Yapısı şu şekilde olmaktadır:
| MPI include file |
|
|
| Initialize MPI environment |
|
|
| Do work and make message passing calls |
|
|
| Terminate MPI Environment |
Yaygın Kullanılan MPI Fonksiyonları:
MPI_Init(&argc,&argv)
MPI_Init fonksiyonunu bütün kodlarınıza eklemeniz gerekmektedir. MPI_Init fonksiyonuna komut satırından da parametre verebilirsiniz ama mecbur değilsiniz. MPI_Init size bir değer dönmektedir bu “MPI_SUCCESS” olursa kod geri kalan MPI_X fonksiyonlarını kullanabilecektir, eğer bu değer dönmezse MPI_X fonksiyonlarını kullanamazsınız.
MPI_Comm_size(comm,&size)
Bu fonksiyonumuz proses sayısını bize vermektedir. Genellikle “comm” kısmına MPI_COMM_WORLD üst fonksiyonu yazılmaktadır.
MPI_Comm_rank(comm,&rank)
Bu MPI fonksiyonu çağıran prosesin sırasını vermektedir. Başlangıçta bütün prosesler 0 veya -1 değerlerini almaktadır. Daha sonra MPI tarafından sıraya sokulunca bütün prosesler numaralandırılmaktadır. Aşağıda ki örneğimizde göreceğimiz gibi makineler 0 dan başlayarak rank(sıra) alacaklardır.
MPI_Abort (comm,errorcode)
Adından da anlaşıldığı gibi bütün prosesleri durdurmaktadır. Istenmeyen durumlarda başvurulabilecek bir fonksiyondur.
MPI_Get_processor_name(&name,&resultlength)
İşlemcinin adını ve adın uzunluğunu dönmektedir.
MPI_Initialized(&flag)
MPI_Init fonksiyonun çağırılıp çağırılmadığını kontrol etmektedir ve çağırıldıysa “1” dönmektedir. MPI_Init her proses tarafından sadece bir kere çağırılması gerektiği için MPI_Initialized fonksiyonu olası çakışmaları önlemektedir.
MPI_Wtime( )
Double deger olarak paralel kodun çalışmaya başlamasından sonra geçen zamanı vermektedir.
MPI_Finalize( )
Bütün işlemlerimizden sonra MPI_Finalize diyerek işlemlerimizi sonlandırıyoruz.
MPI_Send(&buffer,count,datatype,dest,tag,comm)
MPI_Recv(&buffer,count,datatype,source,tag,comm,status)
Fonksiyonlarımız burada değişik parametreler almaktadır bunlardan buffer yollanacak veya alınacak verinin adresini göstermektedir. Count parametresi yollanacak verilerin sayısını belirtmektedir. Datatype parametresi gidecek verinin türünü belirtmektedir (int,float,char,...). Dest parametresi verinin hangi makineye gideceğini belirtmektedir. Source parametresi verinin hangi makineden alacağını belirtmektedir.Tag parametresi ise 0-32767 arası sayısal bir değer alır ve kullanıcı tarafından yanlış makinelerden mesajlar gelmesini önlemek için verilebilecek bir tür güvenlik kodudur. SEND ve RECV fonksiyonlarında tag’ lerin eşit olması gerekmektedir.Comm değişkeni olarak genelde MPI_COMM_WORLD kullanılmaktadır. Grup haberleşme fonksiyonlarında değişik “comm” değerleri kullanılmaktadır. Şu anlık bu değer yeterlidir. Grup haberleşme fonksiyonlarında ise comm parametresi birden fazla değer alabilir. En son parametre değerimiz ise RECV fonksiyonundaki status değeridir. Status mesajın geldiği kaynağı bize bildirmektedir.
SEND/RECV fonksiyonlarımızın dışında işimize yarayabilecek başka fonksiyonlarda vardır bunları kısaca inceleyecek olursak;
MPI_Isend(&buffer,count,datatype,dest,tag,comm)
MPI_Irecv(&buffer,count,datatype,source,tag,comm,status)
Yukarıda gördüğünüz komutların Send ve Recv fonksiyonlarından tek farkı “I” (Immediate) yani beklemeksizin veri alışverişlerin olmasıdır. Sizin de tahmin edebileceğiniz gibi, Send/Recv fonksiyonlarında var olan otomatik olarak bekleme işlemi burada kullanıcı inisiyatifine bırakılmaktadır. Bu fonksiyonlar tek başlarına kullanılmazlar. Muhakkak MPI_WAIT veya MPI_TEST gibi yardımcı fonksiyonları kullanmaları gerekmektedir.
MPI_Wait(&request,&status)
ISEND ve IRECV fonksiyonları verileri yollayana veya alana kadar beklemelerini sağlayan fonksiyondur. Status istenilen mesajın adresini ve tag’ ini dönmektedir.
MPI_Test (&request,&flag,&status)
Bu fonksiyonumuz da istenilen verinin ulaşıp ulaşmadığını kontrol etmek için kullanılır. Eğer verimiz istenilen yere ulaşmışsa flag olarak true değeri dönmektedir. Status istenilen mesajın adresini ve tag’ ini dönmektedir.
MPI_Reduce(data, result, count, type, op, root,comm)
Bu fonksiyonumuz N tane makinede oluşmuş sonuçları derleyerek ana proses’ te result dizisinde saklamaktadır. Geri kalan değişkenlerden count “reduce”(alınmış veri) sayısını, type alınan verilerin türlerini, op yapılan alınma işlemlerinin sonuçlarını, root verilerin alındığı prosesleri ve son olarak comm haberleşme ortamının adını (MPI_COMM_WORLD) almaktadır.
Yukarıda da gördüğümüz gibi 5 tane işlemci ayrı data yani veriler üretmektedir. İstersek bunları ayrı ayrı yollayabiliriz. Bunun için her makine send/recv fonksiyonlarını kullanabiliriz. Biz bunun yerine MPI_REDUCE fonksiyonunu kullanıyoruz. Elimizde ki bütün “data” verilerini MPI_REDUCE aracılığı ile ana işlemcide “result” dizisine gönderiyoruz.
MPI_AllReduce(data, result, count, type, op, root,comm)
ALLREDUCE fonksiyonu REDUCE ile işlevsel olarak aynıdır. Tek farkı bütün proseslere result değerini göndermektedir.
MPI_Barrier(comm)
Global haberleşme fonksiyonlarının en önemlilerindendir. Bütün proseslerin işlemleri tamamlanana kadar devam etmelerini engelleyen senkronizasyon fonksiyonudur.
MPI_Bcast(&buffer,count,datatype,root,comm)
BCAST, Broadcasting kelimesinin kısaltılmış halidir. Root olarak sayılan prosesten arabellekteki (buffer) değerleri “comm” ortamında ki bütün proseslere yollamaktadır. İlk yazımda yazdığım gibi MPI paralel makineler arası haberleşme kütüphanesidir ve 100 den fazla fonksiyonu vardır.
MPI_Scatter (*sendbuf, sendcnt, sendtype, *recvbuf, recvcnt, recvtype, root, comm)
Tek bir kaynak task’tan gruptaki her bir task’a ayrı mesajların dağıtılmasını sağlar.
MPI_Gather (*sendbuf, sendcnt, sendtype, *recvbuf, recvcount, recvtype, root, comm)
Gruptaki her bir tasktan ayrı mesajları alır ve tek hedefe toplar. Bu rutin MPI_Scatter’ın ters işlemidir.
Önceden tanımlı MPI indirgeme işlemleri şunlardır:
| MPI Reduction Operation | C data types | Fortran data types | |
| MPI_MAX | maximum | integer, float | integer, real, complex |
| MPI_MIN | minimum | integer, float | integer, real, complex |
| MPI_SUM | sum | integer, float | integer, real, complex |
| MPI_PROD | product | integer, float | integer, real, complex |
| MPI_LAND | logical AND | integer | logical |
| MPI_BAND | bit-wise AND | integer, MPI_BYTE | integer, MPI_BYTE |
| MPI_LOR | logical OR | integer | logical |
| MPI_BOR | bit-wise OR | integer, MPI_BYTE | integer, MPI_BYTE |
| MPI_LXOR | logical XOR | integer | logical |
| MPI_BXOR | bit-wise XOR | integer, MPI_BYTE | integer, MPI_BYTE |
| MPI_MAXLOC | max value and location | float, double and long double | real, complex,double precision |
| MPI_MINLOC | min value and location | float, double and long double | real, complex, double precision |
MPI Argümanları
Buffer: Yollanacak veya alınacak veriyi referanslayan program adres uzayıdır.
Data Count: Yollanacak olan belirli bir tipteki veri elemanı sayısıdır.
Data Type: Taşınabilirlik için MPI kendi veri tiplerini tanımlamıştır:
| MPI-C data types | |
| MPI_CHAR | signed char |
| MPI_SHORT | signed short int |
| MPI_INT | signed int |
| MPI_LONG | signed long int |
| MPI_UNSIGNED_CHAR | unsigned char |
| MPI_UNSIGNED_SHORT | unsigned short int |
| MPI_UNSIGNED | unsigned int |
| MPI_UNSIGNED_LONG | unsigned long int |
| MPI_FLOAT | float |
| MPI_DOUBLE | double |
| MPI_LONG_DOUBLE | long double |
| MPI_BYTE | 8 binary digits |
| MPI_PACKED | data packed or unpacked with MPI_Pack()/ MPI_Unpack |
Destination: Send rutinlerinde kullanılan bir argüman olup, mesajın nereye ulaştırılacağını belirtir. Alıcı process’in rank’ı ile tanımlanır.
Source: Mesajın nereden geldiğini belirtir. Bu argüman herhangi bir süreçten mesaj almak için MPI_ANY_SOURCE wild card’ına ayarlanabilir.
Tag: Mesajı unique olarak tanımlamak için programcı tarafından gelişigüzel atanan, negatif olmayan bir sayıdır. Send ve receive işlemleri mesaj tag’larında eşleşmelidir. Receive işlemi için MPI_ANY_TAG wild card’ı kullanılabilir. MPI standart 0-32767 arasındaki sayıların kullanılabilmesini garanti eder.
Communicator: Haberleşme içeriğini veya kaynak ve hedef alanları geçerli bir grup süreci işaret eder. Programcı açıkça yeni bir communicator oluşturmadıkça önceden tanımlanmış MPI_COMM_WORLD kullanılır.
Status: Receive işlemi için mesajın kaynağını ve mesajın tag’ını belirtir. C’de bu argüman önceden tanımlı MPI_Status yapısına (MPI_SOURCE ve MPI_TAG elemanlarını içerir) bir pointer’dır.
| Yorumlar |
|

