PDA

Tam Sürümünü Görmek İçin : assembly dersinin c ye yararı


mr1yh1
01/05/2005, 01:07
assembly öğrenmeye başlayalı 2 gün oldu,
bugün bellek adreslemesi ile ilgili birşeyler öğrendim.
konuyu daha iyi bilenbir arkadaşın açıklaması
daha uygun olur ama,
hız üzerindeki etkisini görebilmek için
aşağıdaki testi yazdım,
test, tek farkları başlangıç adreslerinin 8 ile bölümünden kalanı farklı olan diziler
üzerinde aynı işlemleri yapıyor.(ardışık elemanları değiştir.)

( 8 i seçtim böylece 64 bit ve 32 bit işlemcilerle çalışabilecek )


#include <iostream>
#include <ctime>
#include <cstdlib>

#define mod %
#define element_count 1000000
#define block_size element_count*sizeof(int)
#define tampon 8
#define patience 200

using namespace std ;
long timer1 , timer2 , temp ;
int *fastArray, *slowArray ;
void test(int* , long&) ;
void endOfTest() ;

int main()
{
int* block = (int*) malloc(block_size + tampon) ;
int memMod = 8 - (long) block mod 8 ;
switch ( (long) block mod 8 )
{
case 0:
fastArray = block ;
slowArray = (int*)((char*) block + 7) ;
break;
default:
fastArray = (int*) ((char*)block + memMod) ;
slowArray =(int*) ((char*) block + memMod -1);
break;
}
cout<<"lutfen bekleyin, sure cok ise patience degiskenini ayarlayin"<<endl;
test(fastArray, timer1 );
test(slowArray, timer2 );
endOfTest();
free((void*) block);
return 0;
}

void test(int* array, long & timer)
{
temp = clock();
for( int i = 0 ; i< patience ; i++ )
{
int temp;
for ( int j = 0 ; j< element_count-1 ; j++ )
{
temp= array[j] ;
array[j] = array[j+1] ;
array[j+1] = temp ;
}
}
timer=clock() - temp;
}
void endOfTest()
{
cout<<"fast array icin gecen sure :"<<timer1<<endl;
cout<<"slow array icin gecen sure :"<<timer2<<endl;
if (timer2) cout<<"hiz oranlari :"<<(double)timer2/timer1;
system("pause");
}


Euclides
01/05/2005, 11:37
bellek adresi nedir ?
http://www.ceviz.net/index.php?case=article&id=310&catid=49

farkındaysan devamlı 1.2 gibi değerler çıkıyor.
test(fastArray, timer2 );
test(slowArray, timer1 );
gene o civarda ;)

multi tasking'e takılmışsın dostum.

Hizalanmış hafıza hızı yükseltir mi ? Evt. yükseltir. Fakat büyüklük açısından hizalanmış....
bu yüzden default şartlarda aşağıdaki struct 1 değil 4 byte (1 DWORD) kaplar(C bizi düşünüyor)
struct a
{
char b;
}

AMD'nin sistesinde amd için optimize edilmiş bir memcpy kodu var ona bakmanı öneririm.

Şimdi başka bir numara yapalım
1 loop içinde devamlı rdstc okuyalım,bir önceki ölçümden çıkatalım(loop uzunluğu buluruz değil mi ?)
şimdi 1 önceki bulduğumuz loop uzunluğundan şimdi bulduğumuz loop uzunluğu çıkartalım
WOW ! sıfıra yaklaşmadı bile....

(bu kadar geyik yaptım ama oturum run trace yapmadım. kasamam :) :) )

Euclides
01/05/2005, 15:20
Bu programla aynı prensipte çalışıyor 64MB test'i
Kaynak kodlar ve exe dosyanın içinde
Resimde kırmızı ile işaretlenmiş olanlara bakın
9 test'den sadece 3'ünde 2 ölçüm aynı sonucu vermemiş...

mr1yh1
01/05/2005, 21:30
verdiğin programın kaynak kodu assembly olduğu için pek anlamadım nasıl işlediğini :)
verdiğin likdeki yazın içinde ayrıca teşekkürler.

pratikde evet aslında struct ( ve belki class ) yapısı için önemli bu optimizasyon çünkü
yukarıdaki denemede block ile ayırdığım bellek
nedense zaten daima 8'e bölünür çıkıyor( amd64).
Ya C derleyicisi ya da malloc fonsiyonu yapıyor bu optimizasyonu bilemiyorum.
slowArray ı, 7 kalanını bırakan bir başlangıç adresine ben zorladım.

testi yazmama neden olan metin burada,
databusun büyüklüğüne bağlı olarak
adresin, 16bit için 2 , 32bit için 4 ile bıraktığı kalan da önemli imiş.
( The Memory Subsystem kısmı )
http://maven.smith.edu/~thiebaut/ArtOfAssembly/CH03/CH03-1.html#HEADING1-15
özetle:
With a 32 bit memory interface, the 80x86 CPU can access any byte with one memory operation. If (address MOD 4) does not equal three, then a 32 bit CPU can access a word at that address using a single memory operation. However, if the remainder is three, then it will take two memory operations to access that word:

ben 64 bitlik makinaya uysun diye mod 8 kullandım
bu aynı zamanda 32 bit içinde benzer etki yapıyor.

bir de multitasking e takılmadan kastın nedir ?

Euclides
02/05/2005, 11:46
1...
Kod senin yaptığın şeyin aynısını yapıyor.(Fakat 1 farkla her test'den önce hafızayı ilk konumuna geri getiryor.)
2...
Evt derenin altında çok sular akmış durumda Intel IA-32 Manual de böyle bir tanım yok zaten screenshot'ada bakarsan %90 aynı zamanda işlediğini görürsün. İşte o 3 tane farklı ölçümün nedeni multitasking...(bakınız:google->Protected Mode And MultiTasking)

"nedense zaten daima 8'e bölünür çıkıyor( amd64)."

efendim o "windows->VirtualAlloc" fonksiyonun marifeti sebebi ise kolay hesaplama ve hizalama. Mesela VİrtualAlloc ile 1 bye istediğimizde bile windows bize 1 PageSize kadar veriyor(PageSize=Bizim için 0x1000)
Mesela biz yeni hafızayı VİrtualAlloc yerine LocalAlloc,HeapAlloc veya GlobalAlloc ile istemiş olsaydık yeni hafıza Heap'den gelecekti ve o zamanki durumua göre abuk bir sayı olacaktı.

mr1yh1
02/05/2005, 15:58
evet multi tasking sonuçları etkiliyor.
programı biraz değiştirdim,
rastlantısal etkiyi azaltmak için, 500 civarı test yaptım ort. aldım.
bazı testler çok tuhaf sonuçlar çıkabiliyor,
bir testte, hızlı çalışması gereken dizi 2 kat yavaş çalıştı.
ama bunlar genelde, patience değişkeni küçükken yada block un boyutu küçükken oluyor.
uzun vadede ortalamalar benzer bir orana doğru gidiyor.
500 testin sonucunda oran 1.18 idi.


#include<iostream>
#include<ctime>
using namespace std;
#define size 10000 // test bellek boyutu
#define patience 100 // test suresi
#define test_count 500 // test sayisi
long timer1,timer2,temp,time_med1,time_med2;
char* block=new char[(size+8+7)*4];
int *fastArray,*slowArray;

void EndOfTest();
void Test(int*,long&);

int main()
{
int modAdd= 8 - (long)block % 8;
fastArray=(int*)(block+modAdd);
slowArray=(int*)(block+modAdd+7);
//testin tekrari
for(int i = 0 ; i < test_count ; i++)
{
Test(fastArray,timer2);
Test(slowArray,timer1);
time_med1+=timer1;
time_med2+=timer2;
EndOfTest();
}
//sonuclarin ortalamasi
cout<<"slowArray ortalama :"<<time_med1/test_count<<endl;
cout<<"fastArray ortalama :"<<time_med2/test_count<<endl;
if ( timer1 != 0 ) cout<<" ort. oran :" << (double)time_med1/time_med2;
delete[] block;
return 0;
}
//test*************
void Test( int* array,long& timer )
{
temp=clock();
for ( int i =0 ; i < patience ; i++)
{
int t;
for ( int j = 0; j < size-1 ; j++ )
{
t = array[j];
array[j] = array[j+1];
array[j+1] = t;
}
}
timer=clock()-temp;
}
//ekran ciktisi************
void EndOfTest()
{
cout<<"slowArray time"<<timer1<<endl;
cout<<"fastArray time"<<timer2<<endl;
if ( timer1 != 0 ) cout<<" oran " << (double)timer1/timer2<<endl<<endl;
}

Euclides
02/05/2005, 17:26
her test'den önce hafızayı 1 önceki test'in konumuna getirmelisin...!

Ek:
Şimdi aklıma geldi.Biz zaten RAM'ler yazmıyoruz ki :) Virtual(Linear) Memory'e yazıyoruz
Virtual Memory !=Fiziksel Hafıza

mr1yh1
03/05/2005, 16:26
neden testlerden önce belleği eski duruma getiriyorsun,
galiba farklı şeyleri test ediyoruz.
çünki benim test de belleğin içeriğinden etkilenecek bir algoritma yok .:)
verdiğin kodda bir açıklaman var, diyorsun ki
WinXP,NT Biz hafızaya yazmaya başlayana kadar onu atamaz
bu durumda belleğe ilk kez yazımın gerçekleştiği 1. test gözardı edilmeli, diğer 499 un ortalaması alınmalı bence.

virtual memory ile ilgili söylediklerin kafamı karıştırdı.
biz belleğe yazıyoruz derken harddiske yazabiliyoruz ,
bu virtual mem de farklı birşeyler var.. ok..
ama "c ile doğrudan bellek erişimi mümkündür" ne olacak o zaman ? :confused:

Euclides
03/05/2005, 17:22
1...
Yanlış anlamışsın
biz zaten test etmeden önce belleği 0000 FFFF 0000 FFFF ile dolduruyoruz (her test'den önce)
zaten bellek ayrılmış oluyor yani.(ben doldurulmadan önceki halini kasteddim)
2....
O real mode için doğru(yani salt windows olmadan DOS için) FAKAT FAKAT FAKAT !

Windows,Linux ve "Ali'nin İşletim Sistemi" için şöyle Virtual Memory(memory=bellek)'e doğrudan erişim mümkündür.

Hemen bunu deneyelim programın 10 tan kopya çalıştır 10'unda aynı hafıza noktasında çalıştığı 10'ununda aynı offset ve segment'i paylaştığını görürsün. FAKAT aslında RAM'den tamamen farklı noktalardadırlar....(buradan daha detaya girmek lazım aslında paging(windows tabiri ile sanal bellek) nedir o zaman değil mi ? bu soruların cevabı için 32-bit OS Development'la ilgili bir kitap edinmeni tavsiye ederim)

Kısaca CPU Protected Mode'da senini 1 hafıza işlemin için bu kadar hesaplama yapmak zorundadır.

acehreli
03/05/2005, 22:49
mr1yh1, benim bildigim butun isletim sistemleri belirli bir anda calismakta olan programi her zaman icin fiziksel bellekte calistirirlar. O programlarin eristikleri bellek sayfalari da hep fiziksel bellektedirler.

Diske kopyalanan bellek sayfalari, o anda calistirilmayan programlara aittir.

Fiziksel bellekteki bazi sayfalar, bir programa yer acmak icin parca parca diske kopyalanirlar; kendilerine sahip olan program onlari kullanana kadar da orada dururlar. Bazi sayfalar belki de hic kullanilmadiklari icin suresiz olarak (aylarca?) diskte durabilirler.

Ali

mr1yh1
03/05/2005, 23:37
@acehreli
bellek yetersiz olduğunda diskeden yaralanılıyor olmalı,
256MB bellek(RAM) ile 300MB civarı bellek kullanımım olduğunu hatırlıyorum.
leap den ayrılan belleğin RAM dışında olamayacağını,
ama heap den ayrılanın olabileceğini okumuştum.
int i için --------- i daima ram de
int *i = new int[..] için -- i nin işaret ettiği yer diskde olabilir.

@Euclides
epey denedim , 1500 civarı test sonunda bile,
yukarıdaki program 1.2 ye yakınsıyan bir oran veriyor.
ortalama dışı durumlar senin dediğin gibi multitasking olmalı, çünkü onların oranları çok rastlantısal.:)

acehreli
04/05/2005, 00:37
Tabii ki diskten yararlaniliyor; yoksa bellegin boyutu fiziksel bellekle sInIrli kalirdi.

'int *i'nin gosterdigi bellegin diskte olabilecegini soylerken butun yasami boyunca diskte kalacagini soylemiyorsun degil mi? Cunku ben de bunun boyle olmadigini soylemeye calismistim. Yoksa ayirdigi bellek sansina disk uzerinde cikan program hic sonlanamayabilirdi bile; disk erisim hiziyla bellek erisim hizini karsilastiramazsin bile. Kaldi ki disk erisimi de rastgele (random access) degildir; tek bir yerine yazilamaz.

Ayrilan bellek diskten ayrilmiyor; sanal bellekten ayriliyor. Program o bellegi kullandikca, isletim sistemi o bellegin her zaman icin fiziksel bellekte bulundugunu garanti ediyor; yoksa program kabul edilemeyecek kadar yavas kalirdi. Diskte bellek sayfasi bulunan programin o bellek sayfasi once fiziksel bellege kopyalaniyor, program fiziksel bellek uzerinde calisiyor.

Bir program bir bellege erismek istediginde isletim sisteminin sanal bellek algoritmasi suna benzer bir sekilde calisir:


O programin o bellek bolgesine erisim hakki var mi?

Hayir: Programi sonlandir

O bellek bolgesinin hangi bellek sayfasinda oldugunu bul.
(Her ne tur bir veri yapisi kullaniyorsa; agac, liste, vs.)

O bellek bolgesinin uzerinde yasadigi bellek sayfasi su anda
fiziksel bellekte mi?

Hayir: Herhangi bir fiziksel bellek sayfasini diske yazarak
fiziksel bellekte yer ac; bizim programin erismeye calistigi
bellek sayfasini fiziksel bellekte acilan yere kopyala

Programi hicbir sey olmamis gibi ilerlet.


Ali

mr1yh1
04/05/2005, 01:52
@acehreli

anlattığın modelin etkisini programları kulanırken yaşıyorum,
arka planda kalan bir pencere focus aldığında ilk işlemler yavaş oluyor.
ama veri diskden okunduktan sonra, belleğe yazılmadan, doğrudan cpu tarafından da işlenebilirdi.
sanırım burada hız için yapılmış bir kabul var,
benim okuduğum verinin programım tarafından yakınzamanda tekrar kullanılacağı kabulu..
( ve hatta civarında belleğin yakın zamanda okunacağı).
işlemcinin chache belleği nasıl kullandığını okuyorum.
bu mantıkla kullanıyorlar chache de.

virtual memory kullanımı ile ilgili bir döküman aradım,
linux ile ilgili birşey buldum.
ama 3.5MB lık html dökümanı olduğunu görünce vazgeçtim.:)

bu arada yukarıdaki programın değişik hızlardaki cpu lar için nasıl oranlar verdiğini merak ediyorum, deneyenler yazarsa sevinirim :hey:

Euclides
04/05/2005, 11:43
1...
BU sorunu görmemişim.
belleği eski haline getirmemin nedeni add,sub,shr,bswap gibi pek çok komut'un hızı girdiği bağlıdır(hatta bunun için div,mul gibi komutların tek bir clock'a sahip değillerdir. x-y arasında dır şeklinde tanımlanır)

2...
Virtual Memory ile ilgili Intel'in resmi sitesinde IA-32 Manuals Part3: System Programming isimli bir kitap var onda çok güzel bir anlatım var.

3....
mr1yh1:
onun sebebi windows'sun şeçilen pencerenin önceliğini yükseltmesi(sanırım win9x'de yok bu olay sadece xp) bir nevi onun cpu'yu kullandığı zaman aralığı artıyor...

4...
en sonunda dayanamadım ve kodu aldım derledim denedim.
Visual C++ 2003 toolkit
şöyle derliyor

...
mov reg,[reg+reg]
...
reg+reg'e dikkat çekmek istiyorum bir MD'de toplama var

programı patch'leyelim

...
lea reg,[reg+reg]
...

şimdi artık yazma işlemi yapmıyoruz.

gördüğünüz gibi toplamanın etkisi var...(öfff bunu nasıl daha önce göremedim :( :( :( )

benim kodum bunu yapmıyor çünkü orada toplama
add... ile yapılıyor...

4...
acehreli'a aynen katılıyorum.
mesela win ne zaman bu değişimi yapması gerektiğine PageFault(14) Interrupt'ı ile karar veriyor onun için sanal bellek kullanımı artığında bu int'nin kullanımıda artıyor....