Skocz do: nawigacji, wyszukiwania

MICLAB:Affinity


Środowisko i modele programowania

dr inż. Łukasz Szustak, Politechnika Częstochowska, IITiS
mgr inż. Kamil Halbiniak, Politechnika Częstochowska, IITiS



Affinity


Affinity jest mechanizmem wykorzystywanym do zdefiniowania sposobu w jaki procesy lub wątki przypisane zostaną do rdzeni jednostki obliczeniowej. W związku z tym, iż w architekturze Intel MIC w obrębie pojedynczego rdzenia możliwe jest uruchomienie maksymalnie do 4 wątków, które współdzielą tą samą pamięć podręczną mechanizm ten jest niezwykle istotny. Zastosowanie go, w wielu przypadkach pozwala zredukować liczbę odwołań do pamięci głównej zwiększając tym samym wydajność obliczeń. Określenie sposobu w jaki wątki przypisane zostaną do logicznych rdzeni jednostki obliczeniowej uzależniony jest zazwyczaj od wykorzystywanego standardu programowania równoległego.


Najpopularniejszym sposobem definiowania affinity jest wykorzystanie zmiennej środowiskowej KMP_AFFINITY, jednakże jest ona częścią standardu OpenMP, przez co niekoniecznie współpracuje z innymi standardami programowania równoległego. Najbardziej przydatnymi wartościami przedstawionej zmiennej w przypadku architektury Intel MIC są:

  • compact - uruchomione wątki przypisywane są kolejno do kolejnych logicznych rdzeni. Pojedynczy rdzeń wypełniany jest czterema wątkami, kolejno jeden po drugim (Rys. 1).
Compact.png
Rysunek 1. Affinity compact


  • balanced - najbardziej użyteczne rozwiązanie w przypadku architektury Intel MIC. Wątki rozmieszczane są równomiernie pomiędzy dostępne rdzenie obliczeniowe. Rozwiązanie to (Rys. 2) zapewnia efektywniejsze wykorzystanie pamięci podręcznej zwłaszcza w przypadku dużej liczby wątków. Tego rodzaju affinity jest dostępne jedynie w przypadku koprocesorów Intel Xeon Phi.


Balanced.png
Rysunek 2. Affinity balanced


  • scatter - wątki rozmieszczane są pomiędzy rdzeniami na podstawie algorytmu round and robin. Tego typu affinity powoduje, iż w większości przypadków wątki nie współdzielą tej samej pamięci podręcznej. Rozwiązanie to (Rys. 3) jest akceptowalne jedynie w przypadku, kiedy lokalność danych jest nieistotna.


Scatter.png
Rysunek 3. Affinity scatter


Ustawienia affinity możliwe jest również z poziomu kodu źródłowego przy pomocy funkcji systemowych. W przypadku systemu Linux możliwe jest to przy pomocy funkcji sched_setaffinity(), która jest częścią biblioteki sched.h. Przykład kodu ilustrującego ręczne zdefiniowanie affinity przedstawiono na Listingu 1.


#include <iostream> 
#include <stdio.h>
#include "thread"
#include <sched.h>
using namespace std;

void DoWork(int tID) 
{
   printf("Thread ID: %d, CPU: %d\n", tID, sched_getcpu());
}

int main() 
{
   int numberOfThreads = 240;
   thread threads[numberOfThreads];
   cpu_set_t cpus;

   for(int i=0; i<numberOfThreads; ++i) 
   {
      CPU_ZERO(&cpus);
      CPU_SET(i, &cpus);
      sched_setaffinity(threads[i].native_handle(), sizeof(cpus), &cpus);
      threads[i] = thread(DoWork, i);
   }

   for(int i=0; i<numberOfThreads; ++i)
   { 
      threads[i].join();
   }
 return 0;
}
Listing 1. Ręczne zdefiniowanie affinity w standardzie C++11 Threads



< Standardy programowania równoległego - MPI

Wektoryzacja obliczeń >