C++11 – Threadlere Cpu Tahsisi (Cpu Affinity)

C++ dilinin tercih edilmesinin en önemli sebeplerinden biri de performanstır. Hem düşük seviyeli hem de birden çok programlama yaklaşımına (procedural, OOP) destek vermesi dili farklı bir noktaya taşır.

Performans kayıplarının birçok sebebi olsa da en önemli sebeplerinden biri de ‘context switching’ dir. Yani işletim sisteminin takvimlendirmesine (scheduling) göre çalıştırılabilir dosyanızın farklı cpular veya corelar arasında durumunun değişerek taşınması. Bu durum aynı zamanda cpunun en hızlı şekilde eriştiği cache bellek alanlarının da sürekli farklı veri ile dolmasına sebebiyet verir. Bunu engellemek için herhangi bir core’ a posix thread kütüphanesini kullanarak programınızı atayabilirsiniz, örneğin

 

   cpu_set_t cpuset;
   CPU_ZERO(&cpuset);
   CPU_SET(core_id, &cpuset); // core_id atamak istediginiz core' un idsi
   pthread_t current_thread = pthread_self();    
   pthread_setaffinity_np(current_thread, sizeof(cpu_set_t), &cpuset);

şeklinde dileğimizi gerçekleştirebiliriz. Fakat yıl olmuş 2018, kimsenin std::thread varken posix threadi kullanmaz diye düşünüyorum :).

std::thread::native_handle

Daha önce bahsettiğimiz gibi std::thread sınıfı posix thread kütüphanesi üzerine bina edilmiş. native_handle fonksiyonu ise direkt olarak pthread_t türünden mevcut threadi döner. Bu bize her ne kadar standart kütüphanede thread affinityi gerçekleştirme imkanımız olmasa da pthread kütüphanesinin fonksiyonlarını kullanmamızı sağlar. O halde,

#include <iostream>
#include <thread>
#include <pthread.h>

using namespace std;

#define  core_id  0

void func()
{
    int loop = 10000000;
    while (loop--) {
        cout << "loop:" << loop << endl;
    }

}

int main()
{
    thread th(func);

    cpu_set_t cpuset;
    CPU_ZERO(&cpuset);
    CPU_SET(core_id, &cpuset); // core_id atamak istediginiz core' un idsi

    pthread_setaffinity_np(th.native_handle(), sizeof(cpu_set_t), &cpuset);

    th.join();
    return 0;
}

şeklinde std::thread şablon sınıfını kullanarak niyetimizi gerçekleyebiliriz.

Unutmadan şu noktalara değinmekte fayda var, eğer cpunuz hyper threading teknolojisine sahipse kapatmak da fayda var. Hyper threading teknolojisi donanımsal coreları çoklayarak birden fazla core gibi kullanmayı sağlar biz de tam olarak bundan kaçıyoruz. Bir diğer optimizasyon ise işletim sistemine de sen sadece şu coreları kullanabilirsin diye ayar yapmak lazım. Aksi halde işletim sistemi sizin kendi programınız için ayırdığınızı core’u da kendi yönettiği herhangi bir process için kullanabilir. Bu da yine context switchinge sebep olabilir.

Bu şekilde kullanmak benim pek hoşuma gitmediği için, kendi kullanımım için std::threadi sarmalayan, daha önce öğrendiklerimizi de kullanarak bir sınıf yapısı kullanmak istedim. Bu sınıf yapısı tıpkı std::thread gibi değişken sayıda parametre alabilecek. İsteğe bağlı olarak std::threaddeki diğer yardımcı fonksiyonlar da eklebilir.

#include 
#include 
#include 

using namespace std;

class DedicatedThread
{
public:
 template
 explicit DedicatedThread(size_t core, Function&& f, Args&&... args);
 DedicatedThread() = default;
 DedicatedThread(DedicatedThread &&r);
 DedicatedThread& operator=(DedicatedThread&& r);
 void join();
 ~DedicatedThread();

private:
 size_t mCore;
 std::thread mThread;
};

template
DedicatedThread::DedicatedThread(size_t core, Function&& f, Args&&... args):
 mCore(core),
 mThread(std::forward(f), std::forward(args)...)
{
 cout << "ctor" << endl;
 cpu_set_t cpu_set;
 CPU_ZERO(&cpu_set);
 CPU_SET(mCore, &cpu_set);
 pthread_setaffinity_np(mThread.native_handle(), sizeof(cpu_set_t), &cpu_set);
}

DedicatedThread& DedicatedThread::operator=(DedicatedThread&& r)
{
 mThread = std::move(r.mThread);
 mCore = std::move(r.mCore);
 return *this;
}

void DedicatedThread::join()
{
 if (mThread.joinable())
 mThread.join();
}

DedicatedThread::~DedicatedThread()
{
 cout << "dtor" << endl;
}

DedicatedThread::DedicatedThread(DedicatedThread &&r)
{

cout << "move ctor" << endl;
 mThread = std::move(r.mThread);
 mCore = std::move(r.mCore);
}

class A
{

public:

void f()
 {
 size_t loop = 100000000000;
 while (loop--) {
 cout << "loop:" << loop << endl;
 }
 }

};

int main()
{
 A a;

DedicatedThread dt = std::move(DedicatedThread(2, &A::f, &a));

 dt.join();

return 0;
}

Advertisements

One thought on “C++11 – Threadlere Cpu Tahsisi (Cpu Affinity)

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s