Şimdi Ara

C#, diziyi bellekte hizalamak

Daha Fazla
Bu Konudaki Kullanıcılar: Daha Az
2 Misafir - 2 Masaüstü
5 sn
14
Cevap
0
Favori
219
Tıklama
Daha Fazla
İstatistik
  • Konu İstatistikleri Yükleniyor
0 oy
Öne Çıkar
Sayfa: 1
Giriş
Mesaj
  • Mesela aşağıdaki gibi, başlangıç adresi 4096'nın katı olana kadar deniyor ve bulduğunda döngüden çıkıyor ama bir tane dizinin bile bu şekilde yaratılması 1 saati buluyor.

    Bunun daha kolay bir yolu yok mudur?

                 

    PinDizi dizi = new PinDizi();


    for (int i=0;i<1000000;i++)
    {

    float[] aa = new float[16 *1024 * 1024];
    GCHandle gh = GCHandle.Alloc(aa, GCHandleType.Pinned);
    long sonuc = gh.AddrOfPinnedObject().ToInt64() % 4096;
    if (i < 100)
    {
    Console.WriteLine(sonuc);
    }

    if (sonuc == 0)
    {
    Console.WriteLine("bulundu: "+sonuc);

    dizi.dizi = aa;
    dizi.handle = gh;
    break;
    }
    gh.Free();
    }


    C# içinde kullanacağım dizilerin "pin" işleminden sonra 4096 nın katı bir adresten başlaması gerekiyor. C++ için bu çok kolay, alignedmalloc diye birşey var veya herhangi bir dizi oluşturup ortasından pointer alabiliyorum. C# ise ortasından pointer alsam da C# dizisi gibi davranmıyor çünkü object sayılmıyor artık. Mesela o pointerı alıp .toList() diyemiyorum.

    Yani bir metod olsa, bu metoda float dizisini parametre olarak girsem, metod tamamlandığında artık bu float dizisi bellekte 4096 nın katı ile başlayan bir yere işaret etse güzel olurdu.

    İnternette bayağı bir araştırdım, sordum soruşturdum imkansız dediler.



    < Bu mesaj bu kişi tarafından değiştirildi Tugrul_512bit -- 14 Eylül 2016; 21:28:17 >







  • http://stackoverflow.com/questions/10239659/is-there-a-way-to-new-a-net-object-aligned-to-64-bytes

    şurda benzer bir soru için dll oluşturup c# içerisinden çağırma denmiş belki faydalı olabilir
  • Tugrul_512bit kullanıcısına yanıt
    dostum dogru soruşturamamışındır yıllardır forumu kullanıyorsun daha nereye konu açacagını öğrenememişin
  • Teşekkür ederim ama orası pointer kullanıyor. Sadece dizi olsa güzel olacaktı.

    float köşeli parantezler = fonksiyon(); gibi mesela
  • quote:

    C# içinde kullanacağım dizilerin "pin" işleminden sonra 4096 nın katı bir adresten başlaması gerekiyor.


    Ne üzerinde çalıştığınızı bilmiyorum fakat üstteki gereksinimi ortadan kaldırmanız şu anki sorunuzun cevabını bulmaktan daha efektif ve etkili olmaz mıydı?
  • Console.Writeline sürekli çalışıyorsa çalışma hızı büyük şekilde düşüyor onu silmeyi dene.

    quote:

    C# ise ortasından pointer alsam da C# dizisi gibi davranmıyor çünkü object sayılmıyor artık. Mesela o pointerı alıp .toList() diyemiyorum.


    Kendin yazamaz mısın peki bu işlemi yapan bir fonksiyon?
  • quote:

    Orijinalden alıntı: Ephésus

    quote:

    C# içinde kullanacağım dizilerin "pin" işleminden sonra 4096 nın katı bir adresten başlaması gerekiyor.


    Ne üzerinde çalıştığınızı bilmiyorum fakat üstteki gereksinimi ortadan kaldırmanız şu anki sorunuzun cevabını bulmaktan daha efektif ve etkili olmaz mıydı?

    Gereksinim sayılmaz, sadece bilgiyi ekran kartına aktarma performansı 2 katına çıkacağı için yapmaya çalışıyordum.
  • Yapay Zeka’dan İlgili Konular
    Daha Fazla Göster
  • quote:

    Orijinalden alıntı: bersgurs

    Console.Writeline sürekli çalışıyorsa çalışma hızı büyük şekilde düşüyor onu silmeyi dene.

    quote:

    C# ise ortasından pointer alsam da C# dizisi gibi davranmıyor çünkü object sayılmıyor artık. Mesela o pointerı alıp .toList() diyemiyorum.


    Kendin yazamaz mısın peki bu işlemi yapan bir fonksiyon?

    Kendim C++ ile bellek ayırıp C# tarafında pointerını saklayabiliyorum hatta dizi gibi erişimi de sağlayabilirim belki köşeli parantez operatörü overload ediliyorsa. Benim sorunum tüm projeyi float double int gibi şeylerin dizileri ile çalışacak şekilde tasarlamış olmam. Gelen nesne float dizisi değilse çalışmıyor. Çok yerde değişiklik gerekecek yoksa.

    Şimdi o zaman hem float dizisi mi hem de c++ nesnesi mi diye kontrol ettirmem gerekecek ve aynı şeyi double byte long uint int için de tekrarlamam gerekecek ve bunlar 5 farklı class içindeki 15 er farklı metoda uygulamam gerekecek.

    Bakayım overload yapılıyor muymuş köşeli parantezle.




  • Tam olarak ne yapmak istediğini anlamadım fakat c# unmanaged / unsafe koda bi bak derim
  • C++ ile yapsan olmuyor mu?
  • quote:

    Orijinalden alıntı: elektro_gadget

    C++ ile yapsan olmuyor mu?

    Olmuyor değil de iş uzayacak diye sordum. Zaten projenin yarısı C++ dan oluşuyor ama C# float dizisi ile rahat çalışsın diye bissürü eklenti yaptım şimdi c++ dizisi için de aynı şeyi yapmak zorundayım çünü C# dizileri bellekte sabitlemekten öteye gidemiyor. Bari hizalama yapsa. Neyse C++ ile devam ediyorum gibi. 1000 elemanlı diziyi 4096 nın katı olarak hizalamak yerine 5096 elemanlı dizi oluşturup adresi 4096 nın tam katı olan ilk adresten pointer alacağım ve c# tarafına göndereceğim.

     



    public class Diziler
    {
    public const int DIZI_FLOAT = 0;
    public const int DIZI_DOUBLE = 1;
    public const int DIZI_LONG = 2;
    public const int DIZI_INT = 3;
    public const int DIZI_UINT = 3;
    public const int DIZI_BYTE = 4;

    public static int turBelirle(Type t)
    {
    int a = 0;

    if (t == typeof(float[]))
    return DIZI_FLOAT;

    if (t == typeof(double[]))
    return DIZI_DOUBLE;

    if (t == typeof(long[]))
    return DIZI_LONG;

    if (t == typeof(int[]))
    return DIZI_INT;

    if (t == typeof(uint[]))
    return DIZI_UINT;

    if (t == typeof(byte[]))
    return DIZI_BYTE;
    return a;
    }

    [DllImport("KutuphaneCL", CallingConvention = CallingConvention.Cdecl)]
    private static extern IntPtr diziOlustur(int elemanSayisi,int turu,int alignmentDegeri);

    [DllImport("KutuphaneCL", CallingConvention = CallingConvention.Cdecl)]
    private static extern void diziSil(IntPtr p);
    }


    public class Dizi<T>
    {



    private IntPtr p;
    private int tur;


    public Dizi(int n,Type t,int alignment=4096)
    {
    tur = Diziler.turBelirle(t);
    p = Diziler.diziOlustur(n,tur,alignment);
    }

    public T this[int i]
    {
    get { return Diziler.eleman(p,i); }
    set { Diziler.elemanAta(p,i,value);}
    }
    }


    gibi




  • Peki bu işlemin performansı 2 katına çıkardığını deneyimledin mi yoksa sadece tahmin mi? Amaç veriyi gpu önbelleğine fitlemek mi?

    Bence %10 fark sağlarsa öp başına koy.
  • quote:

    Orijinalden alıntı: elektro_gadget

    Peki bu işlemin performansı 2 katına çıkardığını deneyimledin mi yoksa sadece tahmin mi? Amaç veriyi gpu önbelleğine fitlemek mi?

    Bence %10 fark sağlarsa öp başına koy.

    Valla 2 kat olmayabilir aslında, çok eskiden deneyip sonra silmiştim hatırlamıyorum şimdi o denemedeki şeyi kütühane özelliği olarak koyacağım. Denedikten sonra sonuçları buraya yazarım sistem özellikleriyle beraber. İşlemci hesaplamasını veya ekran kartına bilgi yollamayı hızlandıracak. Atıyorum karta 256MB yollatacam ama işlem 1-2 milisaniye sürecek, onun yanında kopyalama işlemi 20-30 milisaniye olunca fazla kaçacak onu azaltmaya çalışıyorum. Pipeline kullanarak bir kısmını azalttım, şimdi bu iş hallolursa kalanı da silinecek yani en azından pipeline ile daha fazla kısmı çakışacağı için verimlilik artacak.

    Şu anda kart pci-e 2.0 x8 modda çalışıyor ve profiler programın 2.2 GB/s kullandığını yazıyor. 4 GB/s olan kuramsal limitten bu değere inmesi belki hatalı aktarımlardan da kaynaklanıyor olabilir, öyleyse kazanç az olacak. Atıyorum yapabileceği 3GB/s ise o zaman %40 artış demek. Pci-e aktarımında toplam yapabileceği 4GB olması gerek değil mi? Yoksa 5GB mi?

    Bir de hizalama varken kopyalananınca dma kullanıldığı için CPU boşta kalıp başka işleri yapabiliyormuş, o da önemli. Tabi gene belleği meşgul edecek bu dma.



    < Bu mesaj bu kişi tarafından değiştirildi Tugrul_512bit -- 18 Eylül 2016; 23:32:51 >




  • quote:

    Orijinalden alıntı: elektro_gadget

    Peki bu işlemin performansı 2 katına çıkardığını deneyimledin mi yoksa sadece tahmin mi? Amaç veriyi gpu önbelleğine fitlemek mi?

    Bence %10 fark sağlarsa öp başına koy.

    Az önce deneyimledim:


    deney: 16 milyon tane float sayısını 1.1f sayısına bölmek. Yani tamamen bellek erişimi nedeniyle darboğaz yaşayan bir işlem. Yöntem: her aygıtın yapacağı hesap aralığını 16 parçaya bölüp pipeline halinde aygıta yollayıp hesaplatıp geri almak.



    Düz C# dizisi ile:

    Aygıt 0: AMD FX(tm)-8150 Eight-Core Processor, hesap süresi: 38,0149 milisaniye, hesap menzili: 811,008
    Aygıt 1: Oland, hesap süresi: 36,8743 milisaniye, hesap menzili: 15,966,208

    yani "oland" adlı gpuya 15,966,208 adet float işlemi 36 milisaniyede yaptırılmış. Saniyede 443 milyon float işlemine denk geliyor.


    4096'ya hizalı C++ dizisi ile:

    Aygıt 0: AMD FX(tm)-8150 Eight-Core Processor, hesap süresi: 22,532 milisaniye, hesap menzili: 6,418,432
    Aygıt 1: Oland, hesap süresi: 21,8329 milisaniye, hesap menzili: 10,358,784

    yani "oland" adlı gpuya 10,358,784 adet float işlemi 21 milisaniyede yaptırılmış. Saniyede 493 milyon float işlemine denk geliyor.


    493 / 443 = 1.11 yani fazladan %11 hız demek.

    Şimdi işlemi bölme işleminden toplama işlemine çeviriyorum:


    Düz C# dizisi:

    Aygıt 0: AMD FX(tm)-8150 Eight-Core Processor, hesap süresi: 39,2572 milisaniye, hesap menzili: 786,432
    Aygıt 1: Oland, hesap süresi: 37,2828 milisaniye, hesap menzili: 15,990,784

    Yani gpu için saniyede 432 milyon float işlemi.

    Hizalı C++ dizisi:

    Aygıt 0: AMD FX(tm)-8150 Eight-Core Processor, hesap süresi: 23,1016 milisaniye, hesap menzili: 5,701,632
    Aygıt 1: Oland, hesap süresi: 23,4098 milisaniye, hesap menzili: 11,075,584

    Yani gpu için saniyede 481 milyon float işlemi.

    Yeni oran: 481/432 = + %11 performans

    yani yapılan işlem bölmeden toplamaya çevrildiği halde işlem süresi aynı. Neredeyse tamamen bellek darboğazı veya pci-e darboğazı var.



    Sadece gpu kullanıldığında ise C# dizisiyle 38 milisaniye olan süre, C++ dizisiyle 33 milisaniyeye iniyor.


    38/33 = %15

    yani önceki denemelerdeki "CPU nun iş yükünü daha çok üstlenebilmesi" olayı elendiğinde, %15 fazladan hız kazanmış gibi görünüyor. Yani eskiden 2.2 GB ise şimdi 2.5 GB a yaklaşmış olabilir. Profiler yeni yöntemdeki hızı okuyamadığı için deneme yapıyorum. Eskiden şu kadar GB diyebiliyordu profiler ama bunda diyemiyor. Programı kontrol ettim doğru çalışıyor.

    Öptüm ve başıma koydum, bayram harçlığımı da aldım.

    Pipelinesız kullanınca 65 milisaniyeden 33 milisaniyeye iniyor. Yani C++ dizisi için pipeline optimizasyonu olsa da olmasa da 33 milisaniye. Pipeline işe yaramıyor gibi. Yazma+okuma işlemleri dma yoluyla çakıştırılamıyor da olabilir veya C# dizisini okurken yazarken çakıştırınca dma kullanmaya başlıyor da olabilir. Pipeline yokken C# tan C++ ya geçince %90 hızlanma var ama.

    Pipeline türünün oku+hesapla+yaz,oku+hesapla+yaz veya oku+oku, hesapla+hesapla, yaz+yaz olması da birşey değiştirmiyor C++ dizisi için. C# dizisi için birinci pipeline türü daha hızlı nedense.



    < Bu mesaj bu kişi tarafından değiştirildi Tugrul_512bit -- 19 Eylül 2016; 17:55:04 >




  • 
Sayfa: 1
- x
Bildirim
mesajınız kopyalandı (ctrl+v) yapıştırmak istediğiniz yere yapıştırabilirsiniz.