Tezde sona yaklaştıkça yaptığım işler de hızlanıyor. Son bir haftadır projedeki bir bug'ı çözmeye çalışıyordum. İşte de biraz yoğun olduğum için teze fazla vakit ayıramıyorum hafta içi, bu yüzden bu kadar uzun sürdü biraz da. Tez projemi C++ ve arka planda MPI, Boost kütüphanelerini kullanarak yapıyorum. Biraz takip edenler anlamışlardır sanırım, veri madenciliği konusunda önemli bir algoritmayı paralelleştiriyorum. Amacım grid veya herhangi bir dağıtık hesaplama kümesinde hızlı bir şekilde bu algoritmayı çalıştırıyor olmak. Yani performans çok önemli benim tezim için. Algoritmayı paralel çalışacak hale soktum. C++ cidden karmaşık bir dil, yanında OpenMPI, bunu saran (wrapper) Boost::MPI ve objeleri işlemciden işlemciye yollarken kullandığım Boost::Serialization kullandığım önemli kütüphaneler arasındalar. MPI hakkında daha fazla bilgiyi önceki yazılarımda bulabilirsiniz.

Performans testleri yaparken aklıma gelen parametreler şunlar:

  • OpenMPI ve MVAPICH hız farkları
  • Derleyici eniyilemeleri (optimizations)
  • reduce&broadcast, reduceAll ve pure send&receive arasındaki hız farkları
  • Tabii ki farklı veri kümeleri (data sets) arasındaki hız farkları. Veriler arasındaki farklılıklar ne yazık ki çok değişik olabiliyor ve bunlar hızı çok fazla etkiliyorlar. Örneğin verinin yoğun veya seyrek olması (dense/sparse), veri büyüklüğü, destek (support) değerleri hep farklı algoritmik uygulamaların hızını büyük oranda etkiliyorlar.

Beni bir haftadır uğraştıran sorun aslında bir segmentation fault hatası. Genel olarak bellekteki sizin olmayan bir bölüme dokunduğunuzda işletim sistemi tarafından programınızın kafasına vurulduğunda ortaya çıkar. Benim hata bir işlemciden başka bir işlemciye çok büyük bir ağaç yapısını gönderirken ortaya çıkıyordu. Daha küçük verilerde ortaya çıkmıyordu, bu benim başlangıç noktam oldu. İlk başta ağaç yapımın döngüler veya hatalı başka yapılar içermesinden kuşkulandım. DDD ile debug etmek için baya bir uğraştım. Aslında hata ayıklama konusunda çok deneyimli değilim. gdb'yi çok kez kullandım ama çok karmaşık işler için değil. where, up, down gibi birçok komutu iyi kullanmayı öğrendim. 6-7 gün DDD ile cebelleşirken benim segmentation fault'un hep farklı fonksiyonlarda ortaya çıktığını farkettim. Gerçi hep Boost'ın Serialize kütüphanesi içindeydi ama bu kütüphanenin farklı sınıflarında hata veriyordu. Daha sonra farkettim ki benim programın stack trace'i yüzbinlerce çağrıdan oluşuyordu. Stack Size bitiyordu. Bunun nedeni yerel değişkenler kullanmam değildi (new ile heap'den yer alıyorum çünkü), ama çağrı sayısının çok büyük olmasıydı. Nitekim 8 MB olarak ayrılmış olan stack büyüklüğü bir süre sonra bitiyordu. Boost::Serialize ile hatanın bağlantısı ise şu: Boost::Serialize gerçekten çok miktarda fonksiyon çağrısı yapıyor. "ulimit -s unlimited" komutu ile stack size'ı limitsiz yapınca benim hata da yok oldu.

Kıssadan hisse: Veri yapınız büyük olunca ve serialize kullanıyorsanız çok büyük miktarda fonksiyon çağrıları yapıyor olabilirsiniz, ve bu durumda stack bölgesi doluyor olabilir. Sonuç seg fault olacaktır. Büyük veriler ile daha dikkatli olmak gerekiyor.