Thread overview
Kesirli sayı bölme işlemi tamsayı bölmeden daha hızlı
December 21, 2015

Andrei'nin eniyileştirme üzerine konuşmasını izliyorum:

https://www.youtube.com/watch?v=ph7FP0LnmcA

İki noktayı denemek istedim:

  • 32 bitlik veriyi yeğleyin çünkü 64 bitlik veriyi işlemekten daha hızlıdır (mantıklı)

  • Kesirli sayı bölme işlemi tamsayı bölme işleminden daha hızlıdır! (Bu çok şaşırtıcı ama nedenini duyunca o da mantıklı: kesirli sayının bellekteki gösteriminde değerin üssünü belirleyen bitlerde çıkartma işlemi yapmak yetiyor. (Tabii bunu mikro işlemci yapıyor.) Tamsayılarda ise mikro işlemci bütün bölme işlemini gerçekleştirmek zorunda.)

(Not: Sonuçlar sizin ortamınızda aynı çıkmayabilir.)

import std.stdio;
import std.algorithm;
import std.datetime;
import std.range;
import std.meta;

enum bölmeAdedi = 100_000;
enum testTekrarı = 100;

T dene(T)() {
   T toplam = 42;
   T bölen = 23;
   bölmeAdedi.iota.each!(_ => toplam += toplam / bölen);
   return toplam;
}

void main() {
   foreach (T; AliasSeq!(byte, short, int, long,
                         ubyte, ushort, uint, ulong,
                         float, double /*, real HATALI SONUÇ VERİYOR */)) {

       const ölçüm = benchmark!(() => cast(void)dene!T)(testTekrarı);

       writefln("%10s: %8s", T.stringof, ölçüm[0].msecs);
   }
}

Bu arada, real ile ilgili bir hata buldum ve bildirdim:

https://issues.dlang.org/show_bug.cgi?id=15466

Sonuç, hiç dmd seçeneği belirtmeden derleyince Andrei'nin dediği gibi çıkıyor (hepsi milisaniye):
'
byte: 204 <- int'ten yavaş çünkü işlemden önce int'e dönüştürülür
short: 203 <- aynı nedenden
int: 196 <- 32 bitlik veri daha hızlı
long: 297 <- 64 bitlik veri daha yavaş
ubyte: 202
ushort: 200
uint: 189
ulong: 253
float: 177 <- Evet, kesirli sayı daha hızlı
double: 176 <- Evet, kesirli sayı daha hızlı
'
dmd'ye bir de '-O -inline -noboundscheck -release' seçeneklerini vermeyi deniyorum. Çoğu derleyici gibi fazla akıllı davranıyor ve dene()'nin dönüş değerinin kullanılmadığını görerek o çağrıyı hiç yapmıyor ve sonuç her tür için 0 çıkıyor. O yüzden kodu biraz değiştiriyorum ve dene() artık sonuç döndürmüyor; parametresinde yan etki üretiyor.

Bu kod real kullanmadaki hatayı ortaya çıkartmadı onu da ekliyorum:

import std.stdio;
import std.algorithm;
import std.datetime;
import std.range;
import std.meta;

enum bölmeAdedi = 100_000;
enum testTekrarı = 100;

void dene(T)(ref T toplam) {    // <-- toplam şimdi parametre olarak
   T bölen = 23;
   bölmeAdedi.iota.each!(_ => toplam += toplam / bölen);
}

void main() {
   foreach (T; AliasSeq!(byte, short, int, long,
                         ubyte, ushort, uint, ulong,
                         float, double, real)) {

       T toplam = 42;    // <-- toplam şimdi parametre olarak
       const ölçüm = benchmark!(() => dene!T(toplam))(testTekrarı);

       writefln("%10s: %8s", T.stringof, ölçüm[0].msecs);
   }
}

Şimdi sonuçlar daha çarpıcı:
'
byte: 99
short: 97
int: 111
long: 207
ubyte: 97
ushort: 98
uint: 107
ulong: 169
float: 58 <-- int'ten daha da hızlı :)
double: 57 <--
real: 1811 <-- artık hiç real kullanmasam mı? ;)
'

Ali

--
[ Bu gönderi, http://ddili.org/forum'dan dönüştürülmüştür. ]

December 25, 2015

Yazı için gerçekten çok teşekkür ederim. Bu tarz testleri görmek oldukça hoşuma gidiyor.

Merak ettiğim şey ise integer float a çevirilip mi işlem yapılıyor yoksa direk integer üzerinde mi işlem yapılıyor?

Yani two's complement olan tam sayı floating point notation'a çeviriliyor ve işlem sonrasında da tekrar two's complement haline geri mi dönüyor?

Zekeriya

--
[ Bu gönderi, http://ddili.org/forum'dan dönüştürülmüştür. ]

December 25, 2015

Alıntı (zekeriyadurmus):

>

integer float a çevirilip mi işlem yapılıyor

İşlemcinin 32 bit (float) ve 64 bit (double) kesirli sayı işlem birimlerinin o türlerden daha fazla bit kullanabildiklerini düşünmüyorum.

Eğer öyleyse, söylediğin gibi olduğunu sanmıyorum çünkü her kesirli tür her tamsayı değeri ifade edemez:

import std.stdio;

void main() {
   writeln(long.max);
   writefln("%f", cast(double)(long.max));
}

'
9223372036854775807
9223372036854775808.000000 // <-- 1 fazla
'
O kadarı yeterli olduğu halde gereksiz bir deneme daha:

import std.stdio;
import std.meta;

enum Nasıl {
   sessizce, açıklayarak
}

T tamsayıBölerek(T)(T i) {
   const sonuç = i / 3;
   return sonuç;
}

T kesirliBölerek(T, K)(T i, Nasıl nasıl = Nasıl.sessizce) {
   const kesirli = cast(K)i;
   const araSonuç = kesirli / 3;
   const sonuç = cast(T)araSonuç;
   if (nasıl == Nasıl.açıklayarak) {
       writefln("  kesirliye dönüşen değer: %f", kesirli);
       writefln("  kesirli bölmenin sonucu: %f", araSonuç);
       writefln("  tekrar tamsayı dönüşümü: %s", sonuç);
   }
   return sonuç;
}

void karşılaştır(T, K)(T i) {
   const a = tamsayıBölerek!(T)(i);
   const b = kesirliBölerek!(T, K)(i);

   if (a != b) {
       writefln("  %s için HATALI: %s != %s", i, a, b);
       kesirliBölerek!(T, K)(i, Nasıl.açıklayarak);
   }
}

void dene(T, K)() {
}

void main() {
   foreach (T; AliasSeq!(byte, short, int, long, ubyte, ushort, uint, ulong)) {
       foreach (K; AliasSeq!(float, double, real)) {
           writefln("%s ile %s (%s hane)", T.stringof, K.stringof, K.dig);
           karşılaştır!(T, K)(T.max);
       }
   }
}

'
byte ile float (6 hane)
byte ile double (15 hane)
byte ile real (18 hane)
short ile float (6 hane)
short ile double (15 hane)
short ile real (18 hane)
int ile float (6 hane)
2147483647 için HATALI: 715827882 != 715827904
kesirliye dönüşen değer: 2147483648.000000
kesirli bölmenin sonucu: 715827904.000000
tekrar tamsayı dönüşümü: 715827904
int ile double (15 hane)
int ile real (18 hane)
long ile float (6 hane)
9223372036854775807 için HATALI: 3074457345618258602 != 3074457437244227584
kesirliye dönüşen değer: 9223372036854775808.000000
kesirli bölmenin sonucu: 3074457437244227584.000000
tekrar tamsayı dönüşümü: 3074457437244227584
long ile double (15 hane)
9223372036854775807 için HATALI: 3074457345618258602 != 3074457345618258432
kesirliye dönüşen değer: 9223372036854775808.000000
kesirli bölmenin sonucu: 3074457345618258432.000000
tekrar tamsayı dönüşümü: 3074457345618258432
long ile real (18 hane)
ubyte ile float (6 hane)
ubyte ile double (15 hane)
ubyte ile real (18 hane)
ushort ile float (6 hane)
ushort ile double (15 hane)
ushort ile real (18 hane)
uint ile float (6 hane)
4294967295 için HATALI: 1431655765 != 1431655808
kesirliye dönüşen değer: 4294967296.000000
kesirli bölmenin sonucu: 1431655808.000000
tekrar tamsayı dönüşümü: 1431655808
uint ile double (15 hane)
uint ile real (18 hane)
ulong ile float (6 hane)
18446744073709551615 için HATALI: 6148914691236517205 != 6148914874488455168
kesirliye dönüşen değer: 18446744073709551616.000000
kesirli bölmenin sonucu: 6148914874488455168.000000
tekrar tamsayı dönüşümü: 6148914874488455168
ulong ile double (15 hane)
18446744073709551615 için HATALI: 6148914691236517205 != 6148914691236516864
kesirliye dönüşen değer: 18446744073709551616.000000
kesirli bölmenin sonucu: 6148914691236516864.000000
tekrar tamsayı dönüşümü: 6148914691236516864
ulong ile real (18 hane)
'

Ali

--
[ Bu gönderi, http://ddili.org/forum'dan dönüştürülmüştür. ]

December 29, 2015

Bu yaptığınız örnek konuyu anlamam için cidden çok faydalı oldu.

Teşekkür ederim.

Zekeriya

--
[ Bu gönderi, http://ddili.org/forum'dan dönüştürülmüştür. ]