Forum: D Programlama Dili RSS
Kesirli sayı bölme işlemi tamsayı bölmeden daha hızlı
acehreli (Moderatör) #1
Kullanıcı başlığı: Ali Çehreli
Üye Haz 2009 tarihinden beri · 4527 mesaj
Grup üyelikleri: Genel Moderatörler, Üyeler
Profili göster · Bu konuya bağlantı
Konu adı: Kesirli sayı bölme işlemi tamsayı bölmeden daha hızlı
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
Avatar
zekeriyadurmus #2
Kullanıcı başlığı: Talha Zekeriya Durmuş
Üye Eki 2012 tarihinden beri · 701 mesaj · Konum: Samsun/Türkiye
Grup üyelikleri: Üyeler
Profili göster · Bu konuya bağlantı
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
Bilgi meraktan gelir...
acehreli (Moderatör) #3
Kullanıcı başlığı: Ali Çehreli
Üye Haz 2009 tarihinden beri · 4527 mesaj
Grup üyelikleri: Genel Moderatörler, Üyeler
Profili göster · Bu konuya bağlantı
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
Avatar
zekeriyadurmus #4
Kullanıcı başlığı: Talha Zekeriya Durmuş
Üye Eki 2012 tarihinden beri · 701 mesaj · Konum: Samsun/Türkiye
Grup üyelikleri: Üyeler
Profili göster · Bu konuya bağlantı
Bu yaptığınız örnek konuyu anlamam için cidden çok faydalı oldu.

Teşekkür ederim.

Zekeriya
Bilgi meraktan gelir...
Doğrulama Kodu: VeriCode Lütfen resimde gördüğünüz doğrulama kodunu girin:
İfadeler: :-) ;-) :-D :-p :blush: :cool: :rolleyes: :huh: :-/ <_< :-( :'( :#: :scared: 8-( :nuts: :-O
Özel Karakterler:
Bağlı değilsiniz. · Şifremi unuttum · ÜYELİK
This board is powered by the Unclassified NewsBoard software, 20100516-dev, © 2003-10 by Yves Goergen
Şu an: 2017-11-21, 15:17:50 (UTC -08:00)