Bu Blogda Ara

visual studio etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster
visual studio etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster

20 Kasım 2010 Cumartesi

Paralel Programlama ile PI Sayısının Hesaplanması

PI sayısı küçük yaşımdan beri bana hep gizemli gelmiştir. İrrasyonel yapıda olduğu için tam değeri hesaplanamayışı, bir çok matematiksel ve geometrik hesabın temelini oluşturması, çok eski medeniyetler tarafından kullanılmış olması insanın ister istemez ilgisini çekiyor. İnternetteki bir tanıma göre (bkz: wikipedia) "çapı 1 olan dairenin çevre uzunluğu" dur PI sayısı.

(bkz: http://tr.wikipedia.org/wiki/Dosya:Pi-unrolled-720.gif)


PI sayısının önemi hakkında daha fazla yazıp çizmek isterdim fakat bu yazının konusu dışında kalmaktadır. Asıl konumuz bu sayının yaklaşık değerini Paralel Programlama yöntemlerini kullanarak nasıl hesaplarız. Yaklaşık değerini dedim çünkü tam değerini hesaplayamayacağımızı artık biliyoruz. :) Bu sayı için hesaplayacağımız basamak sayısı yapmak istediğimiz işin büyüklüğüyle doğru orantılı. Arka bahçemizdeki havuzunuz kaç m^3 su aldığını hesaplamamız için 3.1415 değeri yeterken, Ay'a bir araç göndermek isteyen NASA mühendisleri için 3.1415926535 8979323846 2643383279 5028841971 6939937510 5820974944 5923078164 0628620899 8628034825 3421170679 8214808651 3282306647 0938446095 5058223172 5359408128 4811174502 8410270193 8521105559 6446229489 5493038196 4428810975 6659334461 2847564823 3786783165 2712019091 4564856692 3460348610 4543266482 1339360726 0249141273 7245870066 0631558817 4881520920 9628292540 9171536436 7892590360 0113305305 4882046652 1384146951 9415116094 3305727036 5759591953 0921861173 8193261179 3105118548 0744623799 6274956735 1885752724 8912279381 8301194912 9833673362 4406566430 8602139494 6395224737 1907021798 6094370277 0539217176 2931767523 8467481846 7669405132 0005681271 4526356082 7785771342 7577896091 7363717872 1468440901 2249534301 4654958537 1050792279 6892589235 4201995611 2129021960 8640344181 5981362977 4771309960 5187072113 4999999837 2978049951 0597317328 1609631859 5024459455 3469083026 4252230825 3344685035 2619311881 7101000313 7838752886 5875332083 8142061717 7669147303 5982534904 2875546873 1159562863 8823537875 9375195778 1857780532 1712268066 1300192787 6611195909 2164201989 3809525720 1065485863 2788659361 5338182796 8230301952 0353018529 6899577362 2599413891 2497217752 8347913151 5574857242 4541506959 5082953311 6861727855 8890750983 8175463746 4939319255 0604009277 0167113900 9848824012 8583616035 6370766010 4710181942 9555961989 4676783744 9448255379 7747268471 0404753464 6208046684 2590694912 9331367702 8989152104 7521620569 6602405803 8150193511 2533824300 3558764024 7496473263 9141992726 0426992279 6782354781 6360093417 2164121992 4586315030 2861829745 5570674983 8505494588 5869269956 9092721079 7509302955 3211653449 8720275596 0236480665 4991198818 3479775356 6369807426 5425278625 5181841757 4672890977 7727938000 8164706001 6145249192 1732172147 7235014144 1973568548 1613611573 5255213347 5741849468 4385233239 0739414333 4547762416 8625189835 6948556209 9219222184 2725502542 5688767179 0494601653 4668049886 2723279178 6085784383 8279679766 8145410095 3883786360 9506800642 2512520511 7392984896 0841284886 2694560424 1965285022 2106611863 0674427862 2039194945 0471237137 8696095636 4371917287 4677646575 7396241389 0865832645 9958133904 7802759009 değeri yetersiz ya da yanıltıcı olabilir. Bu yüzden sıradışı ihtiyaçlar için (fezaya mekik göndermek gibi) sıradışı çözümler sunmamız gerekmektedir. Bir gün NASA mühendisleri için PI sayısı hesaplarmıyız bilemiyorum fakat gelecekte çok fazla paralel işlem bizleri bekliyor. Şimdiden kendimizi hazırlamalıyız.

Lafı fazla dolandırmadan işe koyulalım. İlk PI hesabımızı Monte Carlo yöntemiyle yapacağız. Bu yöntem oldukça basit bir olasılık hesabına dayanmaktadır.



Bu olasılık hesabını yapmak için C++ programlama dili üzerinden openMP kütüphanesini kullandım. Paylaşımlı bellek üzerinden paralel programlama yapmak için geliştirilmiş olan bu kütüphane kullanması oldukça basittir. Mevcut kodunuza hiç dokunmadan (elbetteki paylaşımlı bellek paralel program mantığı güdülerek yazılmış kod olamalı) doğrudan programınızı paralel çalıştırabilirsiniz. Bu yazacağımız program dağıtık mimariler için değil tek bir süper bilgisayar için programlanmıştır. Makine üzerindeki çekirdek sayısıyla doğru orantılı olarak performans artışı sağlanacaktır.

Programımızı yazmadan önce Visual Studio 2010 üzerinden projemizin openMP desteğini açalım. Project -> Properties ...


(DÜZELTME)








Şimdi programımızı yazalım. Programda açıklamak istediğim tüm noktaları yorum satırı olarak ekledim. Bir göz atalım sonra sonuçları değerlendirelim.


/*
Monte Carlo yöntemiyle PI sayısının yaklaşık değerinin hesaplanması.

C++ / openMP

Ramazan Bellek - Kasım 2010
*/

#include <iostream>
#include <ctime>
#include <math.h>

#define MAX_ITERATION 1000000 // tekrarlama sayısı
#define r 500 // yarıçap
#define R r*2+1 // çap + 1

int main(int argc, char **argv)
{
srand(time(0));
clock_t start = clock();

bool iceride[MAX_ITERATION]; // doğruluk tablomuz
double x,y;

/*
openMP ile birbirinden bağımsız olan hesaplama kısmını işlemciye paralel olarak işletiyoruz.
x ve y değişkenleri her bir iş parçacığı için kişisel olarak tanımlandı, böylelikle bir iş parçacığı
diğerinin konum bilgisini görememektedir. Doğruluk tablosunu belirten değişkeni ise ortak olarak
kullanacaklardır.
*/
#pragma omp parallel for private(x,y) shared(iceride)
for(int i=0;i<MAX_ITERATION; i++)
{
// uzayda bir noktayı (sözde) keyfi seçiyoruz.
x = rand()%R;
y = rand()%R;

double fark = sqrt(pow(x-r,2)+pow(y-r,2)); // seçtiğimiz noktanın dairenin merkezine olan uzaklığını alıyoruz.

if (fark<=r) // eğer bu uzaklık dairenin yarıçapından küçük ya da eşit ise nokta dairenin içerindedir.
iceride[i]=true;
else
iceride[i]=false;
}

long toplamIceride = 0;
// daire içerisine düşen toplam sayı
for(int i=0;i<MAX_ITERATION; i++){
if (iceride[i]==true)
toplamIceride++;
}

/*
daire_icine_dusen/toplam = (pi*r^2)/(4*r^2)
=> pi = 4 * daire_icine_dusen/toplam
*/
std::cout<<"PI ~= "<<4*(double)toplamIceride/MAX_ITERATION<<std::endl;

printf("Gecen Sure: %f\n", ((double)clock() - start) / CLOCKS_PER_SEC);
system("pause");
return 0;
}


Programımızı çalıştırıyoruz.



Sonuç aslında pek istediğim gibi çıkmadı. Beklentim en azından ilk 4 basamağı (3.1415) bulmak üzereydi. İterasyonları arttırarak tekrar denenebilir, ayrıca burada ürettiğimiz rastgele sayılar sözde olduğu için bize tam bir sonuç vermesi beklenemez. İterasyonu arttırarak işlemcinin çekirdeklerindeki değişimi göstermek istiyorum.



Gördüğünüz gibi PI sayısını bir süper bilgisayarda nasıl hesaplayacağımızı basit olarak da olsa görmüş olduk. Daha detaylı görsel uygulama için http://www.eveandersson.com/pi/monte-carlo-demo adresindenki java applete göz atabilirler.

Bir sonraki paralel programlama yazımda paylaşımsız bellekli dağıtık mimari kullaranak PI sayısını hespalamaya çalışacağım. Küçük bir bilgisayar kümesi kurarak dev işlemleri basitleştirmeye çalışacağız. Gelecekteki bir bilgisayar ordusunun komutanı olmanız ümidiyle. Hoşçakalın...

4 Nisan 2010 Pazar

Winapi: WinExec

Merhaba bu yazımda WinExec fonksiyonunu inceliyeceğiz.

Teknik detaylara geçmeden önce özetle bu fonksiyonun görevinden bahsedeyim. Bu fonksiyon ile yeni bir uygulamayı başlatabiliyoruz. Başlattığımız uygulama fonksiyonu çağıran uygulama ile aynı yetkilere sahip oluyor.

Fonksiyonun yapısı:

UINT WINAPI WinExec(
__in LPCSTR lpCmdLine,
__in UINT uCmdShow
);


2 adet parametre alıyor. Bunlardan birincisi çalıştıracağımız uygulamanın sabit disk üzerindeki tam adresini ifade ediyor.

typedef __nullterminated CONST CHAR *LPCSTR, *PCSTR;

İkinci parametre ise açacağımızın uygulamanın başlama durumunu belirtmektedir. Örnek olarak simge durumunda, tam ekran ya da görünmez olarak başlatabiliriz.


#define SW_HIDE 0 // gizli
#define SW_SHOWNORMAL 1 // normal
#define SW_NORMAL 1
#define SW_SHOWMINIMIZED 2 // simge durumunda
#define SW_SHOWMAXIMIZED 3 // tam ekran
#define SW_MAXIMIZE 3
#define SW_SHOWNOACTIVATE 4 // normal gösterim ile aynı, tek farkı aktif olmaması
#define SW_SHOW 5 // gösterir (pencere gizli bile olsa)
#define SW_MINIMIZE 6 // simge durumuna getirir
#define SW_SHOWMINNOACTIVE 7 // simge durumuyla aynı, tek farkı aktif değil
#define SW_SHOWNA 8 // gösterir, aktif değil
#define SW_RESTORE 9 // simge durumundan kurtarır
#define SW_SHOWDEFAULT 10 // uygulamanın orijinal açılış değeri (CreateProcess'te kullanılan...)
#define SW_FORCEMINIMIZE 11 // simge durumuna zorlar (uygulama yanıt vermese bile)
#define SW_MAX 11 // tam ekrana zorlar (uygulama yanıt vermese bile)


Bu fonksiyonu kullanabilmek için C/C++ uygulamanıza WinBase.h (Windows.h içerisinde mevcut) başlık dosyasını eklemeniz gereklidir. WinApi kernel32.dll içerisindedir.

Windows 2000 Professional ve üzeri bir işletim sisteminde çalıştırılabilir.

Olası hata kodları ve açıklamaları:

0 Sistem kaynakları ya da hafızası yetersiz.

ERROR_BAD_FORMAT .exe dosyası geçersiz.

ERROR_FILE_NOT_FOUND Belirtilen dosya bulunamadı.

ERROR_PATH_NOT_FOUND Belirtilen yol bulunmadı.


Hemen basit bir uygulama yapalım.

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>


int main(int argc, char **argv)
{
long hata = WinExec("notepad.exe",SW_NORMAL);

switch(hata)
{
case 0: printf("Sistem kaynakları ya da hafızası yetersiz.\n"); break;
case ERROR_BAD_FORMAT: printf(".exe dosyası geçersiz.\n"); break;
case ERROR_FILE_NOT_FOUND: printf("Belirtilen dosya bulunamadı.\n"); break;
case ERROR_PATH_NOT_FOUND: printf("Belirtilen yol bulunmadı.\n"); break;
default: printf("Uygulama başarıyla çalıştırıldı.\n");
}

system("pause");
return 0;
}


Not: Bu uygulama Microsoft Visual Studio 2008 Express Edition üzerinden test edilmiştir.

2 Nisan 2010 Cuma

C/C++ uygulamalarında Assembly komutları

Herkese merhaba,

C/C++ uygulamaları, genellikle hız ve performans gerektiren uygulamaların geliştirilmesinde kullanılan temel dillerdir. Donanıma yakın olmak isteyen bir programcı ya makine diline en yakın olan assembly ile günlerini harcayarak en hızlı ama güvenilir olmayan uygulamalar ortaya koyacaktır ya da C/C++ gibi bir programlama diliyle assembly diline yakın bir performansı daha hızlı ve bir nebze olsun daha güvenli uygulamalar geliştirecektir.

Bu yazımda sizlere Visual Studio geliştirme ortamında C/C++ uygulamaları içerinde Assembly komutlaranı nasıl kullanacağımızı göstermeye çalışacağım. Amacım Assembly dilini anlatmak değildir. Yalnızca bu komutları C/C++ uygulamalarında kullanmayı göstermektir.

Peki bir programcı buna neden gerek duyar? Hızdan ve performanstan bahsettik. Eğer çok fazla işlemi çok kısa bir sürede yapmak istiyorsak, programlarımızdan elimizden geldiğince gereksiz kodları çıkartmamız gerekcektir. Ne kadar gereksiz kodları programımızdan çıkartsakta bazı kodlar bizim isteğimizin dışında programa derleme zamanında yerleşmektedir. Bunlar derleyiciler tarafından optimize edilmiş genel amaçlı komutlardır. Eğer bir sistem ya da grafik programcısı gibi hız gerektiren bir iş yapmak istiyorsanız bu komutlarıda optimize etmelisiniz. Lafı daha fazla dolaştırmadan örneğimize geçelim.

Öncelikle herhangi bir C/C++ projesi oluşturalım. Bu konsol ya da görsel bir proje olabilir. Daha önce bahsettiğim derleyicilerin bizden habersiz programımıza eklediği kodları görmeliyiz. Bunun için Visual Studio geliştirme ortamının Disassembly özelliğini kullanacağız. Bu pencereyi programınızı debug ederken (herhangi bir satıra breakpoint koyarak) ister Alt + 8 tuş kombinasyonları ile ister Debug -> Windows -> Disassembly menüsünden açabilirsiniz. Bu pencere ile yazmış olduğunuz C/C++ uygulamasının kaynak kodlarına karşılık üretilen Assembly kodları görünmektedir.

Not: Bu proje Microsoft Visual Studio 2008 Express Edition ile oluşturulmuştur.








Şimdi kendi assembly komutlarımızı girelim. Bir C/C++ uygulamasında assembly komutları kullanmak için
__asm
{
... komutlar ...
}

şeklinde bir yapı kullanarak derleyiciyi uyarıyoruz.

Şimdi basit bir örneği inceleyelim. 32-bit Integer bir değişkene assembly ile değer atayalım.




MOV komutu ile akümülatöre 7 değerini atadık. Burada akümülatörü EAX ile ifade ederek 32-bit kelime işleyeceğimiz belirttik. Böylelikle 7 değeri 32-bit integer olarak akümülatöre atandı. Daha sonra bu değeri a değişkenine yine MOV komutu ile aktardık. Hepsi bu.

Şimdi biraz daha derine inip bir pointer (işaretçi) kullanarak bu işi nasıl yapacağımıza bakalım.




Bu örnekte akümülatöre b işaretçisinin adresini atadık. Bu atamayı yaparken DWORD PTR anahtar kelimelerini kullandık. Burada DWORD (double word - çift kelime) 32-bit uzunluğunda , PTR ise bir pointer (işaretçi) gösterdiğini belirtiyor. Derleyicimiz b işaretçisinin gösterdiği alana gidiyor ve oradaki 4 byte (32-bit) veri ile işlem yapıyor. Daha sonra bu adresin gösterdiği alana 9 değerini girerek a değişkenini dolaylı olarak değiştirmiş oluyoruz.

Böylelikle bir giriş yapmış olduk. Bir sonraki yazılarda daha derin işlemleri yaparak ışık hızında uygulamaları göstermek istiyorum.

Görüşmek üzere...