<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="hu">
		<id>https://wiki.niif.hu/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Szigeti%28AT%29niif.hu</id>
		<title>Pro-M - Oktatási Üzletág - Wiki - Szerkesztő közreműködései [hu]</title>
		<link rel="self" type="application/atom+xml" href="https://wiki.niif.hu/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Szigeti%28AT%29niif.hu"/>
		<link rel="alternate" type="text/html" href="https://wiki.niif.hu/index.php?title=Speci%C3%A1lis:Szerkeszt%C5%91_k%C3%B6zrem%C5%B1k%C3%B6d%C3%A9sei/Szigeti(AT)niif.hu"/>
		<updated>2026-11-07T20:55:44Z</updated>
		<subtitle>Szerkesztő közreműködései</subtitle>
		<generator>MediaWiki 1.30.0</generator>

	<entry>
		<id>https://wiki.niif.hu/index.php?title=Intel_Xeon_Phi&amp;diff=4140</id>
		<title>Intel Xeon Phi</title>
		<link rel="alternate" type="text/html" href="https://wiki.niif.hu/index.php?title=Intel_Xeon_Phi&amp;diff=4140"/>
				<updated>2017-05-09T07:15:57Z</updated>
		
		<summary type="html">&lt;p&gt;Szigeti(AT)niif.hu: /* automatikus kódátvitel */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Az oldalon található információk segítséget nyújtanak a HPC felhasználóinknak, hogy alkalmazásaikat miként portolhatják valamint optimalizálhatják a hatékonyabb futást szem előtt tartva Intel Xeon Phi koprocesszor kártyákon.&lt;br /&gt;
&lt;br /&gt;
{{INFO|Egy 10 alkalmas, angol nyelvű Xeon Phi programozás tutoriál videóanyaggal és példaprogrammal elérhető a [[http://colfaxresearch.com/how-16-04/ Colfax oldalán]]}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Hardver bemutatása==&lt;br /&gt;
Intel® Xeon Phi™ Coprocessor 7120P&lt;br /&gt;
&amp;lt;gallery&amp;gt;&lt;br /&gt;
Fájl:Xeon-phi-family-rwd.png.rendition.intel.web.720.405.png|&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-  align=&amp;quot;center&amp;quot;&lt;br /&gt;
! CPU&lt;br /&gt;
! threads/Core&lt;br /&gt;
!max threads&lt;br /&gt;
! CPU Freq&lt;br /&gt;
! Memória&lt;br /&gt;
! L1 cache&lt;br /&gt;
! L2 cache&lt;br /&gt;
! Interfész&lt;br /&gt;
! Ptot&lt;br /&gt;
! Számítási kapacitás&lt;br /&gt;
|-  align=&amp;quot;center&amp;quot;&lt;br /&gt;
| 61&lt;br /&gt;
| 4 Hardver&lt;br /&gt;
| 244&lt;br /&gt;
| 1238 GHz&lt;br /&gt;
| 16GB GDDR5&lt;br /&gt;
| 32KB/core&lt;br /&gt;
| 512KB/core&lt;br /&gt;
| PCIe&lt;br /&gt;
| 300W&lt;br /&gt;
| 1,2 TFlop/s dupla pontosság&lt;br /&gt;
|-  align=&amp;quot;center&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
| OpenMP-nél csak '''240'''&lt;br /&gt;
|&lt;br /&gt;
| ~100 cycles&lt;br /&gt;
~174 GB/s&lt;br /&gt;
| ~3 cycles&lt;br /&gt;
| ~10 cycles&lt;br /&gt;
| ~7 GB/s&lt;br /&gt;
~1 microsecond&lt;br /&gt;
|&lt;br /&gt;
| 2,4 TFlop/s egyszeres pontosság&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Az Intel® 1. generációs Xeon Phi™ koprocesszorában található 61 processzor ugyanazt a 16GB memóriát éri el, de semmilyen módon se fér hozzá közvetlenül a hoszt gép fizikai memóriájához.&amp;lt;br /&amp;gt;&lt;br /&gt;
[[Budapest2_klaszter|Budapest2]] és [[HPC#Szupersz.C3.A1m.C3.ADt.C3.B3g.C3.A9peink_.C3.B6sszehasonl.C3.ADt.C3.A1sa|Debrecen3-Phi]] gépeinkben számítási nodonként 2-2 Phi™ koprocesszor található. &amp;lt;br/&amp;gt;&lt;br /&gt;
A kártyákon speciális célú Linux kernel fut melyre a belépés limitáltan csak a rendszergazdák számára lehetséges SSH-n keresztül a hoszt gépről. Természetesen a kártyára belépve olyan érzésünk lehet, mintha egy sok processzoros gépen dolgoznánk, ami rendelkezik rengeteg memóriával. Ez így is van, de a hatékonyabb kihasználás érdekében az SSH-n keresztüli elérés nem lehetséges, csak a Slurm segítségével valamint a programozási nyelvek által támogatott csatornákon keresztül. Ennek részletezése lentebb megtalálható.&lt;br /&gt;
&lt;br /&gt;
==Használati módok==&lt;br /&gt;
A koprocesszor használatai módjait két csoportba lehet sorolni. A '''natív''' és az '''offload''' használatot.  &amp;lt;br/&amp;gt;&lt;br /&gt;
Ezen használati módokról a lenti fejezetekben több részletet is megtudhat. Az itt megfogalmazott &amp;quot;támogatás&amp;quot; csak a mi infrastruktúránkra érvényes! &amp;lt;br /&amp;gt;&lt;br /&gt;
A bemutatott példák ''c++'' nyelven vannak, de természetesen ''c'' és ''fortran'' esetén is megtalálható az ekvivalens módszer.&lt;br /&gt;
&lt;br /&gt;
===a) nem támogatott mód===&lt;br /&gt;
====közvetlen natív használat====&lt;br /&gt;
A natív mód arra szolgál, hogy a MIC kártyára  lefordított alkalmazásunkat felmásoljuk a kártyára, majd belépünk SSH-n a kártyára és azon futtatjuk a bináris kódunkat. &amp;lt;br /&amp;gt;&lt;br /&gt;
Biztonsági okokból a közvetlen natív használat az NIIF HPC infrastruktúrán nem elérhető. &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===b) támogatott, de nem preferált használat===&lt;br /&gt;
====automatikus kódátvitel====&lt;br /&gt;
Ebben az esetben nem kell átmásolnunk a bináris alkalmazásunkat a kártyára sőt még csak be se kell lépnünk, mert ezt a két lépést egy beépített tool végzi el. &amp;lt;br/&amp;gt;&lt;br /&gt;
A tool eléréséhez szükséges parancs neve: '''micnativeloadex'''&lt;br /&gt;
&lt;br /&gt;
Ebben az esetben a workflow a következőképpen módosul:&lt;br /&gt;
# Alkalmazás megírása. például [[Média:hello.cc|''hello.cc'']]&lt;br /&gt;
# Alkalmazás fordítása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;icpc -o hello-MIC -mmic hello.cc&amp;lt;/pre&amp;gt;&lt;br /&gt;
# A tool futásához szükséges library-k megadása &amp;lt;br&amp;gt; &amp;lt;pre&amp;gt;export SINK_LD_LIBRARY_PATH=/opt/intel/composerxe-&amp;lt;verzió&amp;gt;/compiler/lib/mic&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Alkalmazás futtatása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;micnativeloadex hello-MIC&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| BUDAPEST2[cn10] phi (0)$ export SINK_LD_LIBRARY_PATH=/opt/intel/composerxe-2011.4.191/compiler/lib/mic &amp;lt;br/&amp;gt;BUDAPEST2[cn10] phi (0)$ micnativeloadex hello-MIC &amp;lt;br/&amp;gt;Hello world! I have 244 logical cores.&amp;lt;br/&amp;gt;BUDAPEST2[cn10] phi (0)$&lt;br /&gt;
|}&lt;br /&gt;
Intel MKL használat esetén a SINK_LD_LIBRARY_PATH természetesen kiegészítendő az MKL könyvtárak elérésével.&lt;br /&gt;
&amp;lt;pre&amp;gt;intelhome=/opt/intel/compilers_and_libraries_2016.1.150/linux&lt;br /&gt;
export SINK_LD_LIBRARY_PATH=$intelhome/compiler/lib/mic:$intelhome/mkl/lib/mic&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===c) támogatott módok===&lt;br /&gt;
====Offload====&lt;br /&gt;
Az Offload modell esetén szükséges a forráskód módosítása, mégpedig úgy, hogy azon kódrészletet, amit a koprocesszoron szeretnénk futtatni, ki kell egészíteni egy fordítási direktívával. Az alkalmazás fordításakor elegendő csak a CPU-ra való binárist készíteni mivel a modell végzi a megfelelő forrás részlet mozgatását és futtatását.&amp;lt;br /&amp;gt;&lt;br /&gt;
[[Fájl:offload.png|Offload modell]] &amp;lt;br /&amp;gt;&lt;br /&gt;
Példánkban jól látható, hogy a második kiíratás már a koprocesszoron fut.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
int main(int argc, char * argv[]) {&lt;br /&gt;
  printf(&amp;quot;Hello world from host!\n&amp;quot;);&lt;br /&gt;
#pragma offload target(mic)&lt;br /&gt;
  {&lt;br /&gt;
    printf(&amp;quot;Hello world from coprocessor!\n&amp;quot;); fflush(stdout);&lt;br /&gt;
  }&lt;br /&gt;
  printf(&amp;quot;Bye\n&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ebben a példában az operációs rendszer választja ki, hogy melyik Phi kártyán fusson le a kódrészlet. Amennyiben mi szeretnénk explicite megmondani, hogy melyik kártyán fusson, akkor a fordítási direktívában a mic kulcsszó után kell tenni egy &amp;quot;:&amp;quot;-t és a kártya sorszámát [mi esetünkben 0 vagy 1]. Ekkor így nézne ki a direktíva: ''#pragma offload target(mic:1)''&lt;br /&gt;
&lt;br /&gt;
Amennyiben az offload szekcióban függvény hívás is szerepel, akkor szükségeltetik az függvény futásához szükséges változók adatterületének átmásolása a Phi kártyára, majd a lefutás után visszamásolni a hoszt gépre. Ezen másolást az ''offload_attribute'' ''push'' és ''pop'' paraméterei segítségével határozhatjuk meg. &amp;lt;br /&amp;gt;&lt;br /&gt;
A következő mátrix-vektor szorzás példánál az A mátrix globális változó, míg a b és c vektorok a függvény paraméterei.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//A változók felmásolásra kerülnek a Xeon Phi-re&lt;br /&gt;
#pragma offload_attribute(push, target(mic))&lt;br /&gt;
  double* A;&lt;br /&gt;
  void MatrixVector(double *b, double *c, double m, double n) {&lt;br /&gt;
    ...&lt;br /&gt;
    c[] = A[..]*b[]&lt;br /&gt;
  }&lt;br /&gt;
// felmásolt objektumok visszamásolása&lt;br /&gt;
#pragma offload_attribute(pop)&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
  ...&lt;br /&gt;
#pragma offload target(mic) in(A[0:n*m])&lt;br /&gt;
// így is meg lehetne adni: #pragma offload target(mic:0) in(A : length(n*m))	        &lt;br /&gt;
  {&lt;br /&gt;
    MatrixVector(b, c, m, n);&lt;br /&gt;
  }&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ekkor minden memória területet 2x kell másolni, egyszer fel a kártyára, majd másodszor vissza a hoszt gépre, pedig elegendő lenne csak az eredmény vektort visszamásolni. Ezen optimalizálással egy későbbi fejezetben foglalkozunk. &amp;lt;p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Futás során kaphatunk részletes riportot is, hogy pontosan milyen adatmozgatások történnek az alkalmazás futása során. Ehhez nincs szükség másra csak az '''OFFLOAD_REPORT''' környezeti változó megfelelő beállítása. A beállított értékek Automatic Offload esetén 1, 2, míg Compiler Assisted Offload esetén 1, 2 és 3 lehetnek, attól függően mennyire részletes riportot szeretnénk kapni. &amp;lt;br /&amp;gt;&lt;br /&gt;
beállítás:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export OFFLOAD_REPORT=3&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
példa kimenet a hello alkalmazásra:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Hello world from host!&lt;br /&gt;
[Offload] [MIC 0] [File]                    hello_offload.cc&lt;br /&gt;
[Offload] [MIC 0] [Line]                    5&lt;br /&gt;
[Offload] [MIC 0] [Tag]                     Tag 0&lt;br /&gt;
Hello world from coprocessor!&lt;br /&gt;
[Offload] [HOST]  [Tag 0] [CPU Time]        0.554653(seconds)&lt;br /&gt;
[Offload] [MIC 0] [Tag 0] [CPU-&amp;gt;MIC Data]   0 (bytes)&lt;br /&gt;
[Offload] [MIC 0] [Tag 0] [MIC Time]        0.000140(seconds)&lt;br /&gt;
[Offload] [MIC 0] [Tag 0] [MIC-&amp;gt;CPU Data]   0 (bytes)&lt;br /&gt;
Bye&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Offload + OpenMP====&lt;br /&gt;
Amennyiben nem csak 1 processzort szeretnénk használni a Phi kártyánkon és az alkalmazásunkban használt számítás megengedi a párhuzamosítást, akkor az OpenMP segítségével párhuzamosíthatjuk a megfelelő számítást. &amp;lt;br /&amp;gt;&lt;br /&gt;
[[Fájl:Offload+openmp.png|Offload modell és OpenMP]] &amp;lt;br /&amp;gt;&lt;br /&gt;
Példánk maradjon ugyanúgy a mátrix-vektor szorzás. Amennyiben a teljes kártya adta kapacitást ki szeretnénk használni, akkor az OMP threadek számát 240-re kell beállítani. Azért nem 244-re, mivel 1 CPU-t dedikáltan csak a kommunikációra használ a rendszer és 1 CPU 4 hardver threaddel rendelkezik.&lt;br /&gt;
Ekkor a példánk így változik:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;omp.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#pragma offload_attribute(push, target(mic))&lt;br /&gt;
void multiply(int n, int m, double* A, double* b, double* c){&lt;br /&gt;
  const int nCPUs = omp_get_max_threads();   //240&lt;br /&gt;
  omp_set_num_threads(nCPUs);&lt;br /&gt;
#pragma omp parallel for schedule(dynamic, 1)&lt;br /&gt;
  for ( int i = 0 ; i &amp;lt; m ; i++)&lt;br /&gt;
     for ( int j = 0 ; j &amp;lt; n ; j++)&lt;br /&gt;
       c[i] += A[i*n+j] * b[j];&lt;br /&gt;
}&lt;br /&gt;
#pragma offload_attribute(pop)&lt;br /&gt;
&lt;br /&gt;
int main(){&lt;br /&gt;
  double * A = (double*) malloc(sizeof(double)*n*m);&lt;br /&gt;
  ...&lt;br /&gt;
#pragma offload target(mic) in(A:length(n*m))&lt;br /&gt;
     multiply(n, m, A, b, c);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Az alkalmazás fordításánál a fordítónak meg kell adni, hogy használhja az openmp library-ket is. Ezt a '''-qopenmp''' kapcsolóval tudjuk elérni.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
icpc -o matrix-CPU -qopenmp matrix.cc&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====MPI + Offload + OpenMP====&lt;br /&gt;
Abban az esetben, ha 1 számítási node nem elég, MPI segítségével össze tudunk kötni több nodet, heterogén rendszert alkotva, és azokon alkalmazható a fenti Offload + OpenMP modell. &amp;lt;br /&amp;gt;&lt;br /&gt;
[[Fájl:Mpi+offload+openmp.png|MPI elosztás valamint Offload modell és OpenMP]] &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Phi használat slurm-mel==&lt;br /&gt;
Slurm esetén a mic opciót kell megadni a gres-knél. Amennyiben egynél több kártyára van szükség, akkor azt a ''mic:2''-vel lehetséges megadni. Ekkor két kártyát foglal le az ütemező a futtatandó feladatnak. &amp;lt;br /&amp;gt;&lt;br /&gt;
Egy lehetséges feladat, amit srun-nal lehet a slurmben beküldeni. debrecen3 esetén a ''prod-phi'' particiót kell használni&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ cat slurm_job_openmp_phi&lt;br /&gt;
&lt;br /&gt;
#!/bin/bash&lt;br /&gt;
#SBATCH -A &amp;lt;PROJECT NEVE&amp;gt;&lt;br /&gt;
#SBATCH --job-name=&amp;lt;JOB NEVE&amp;gt;&lt;br /&gt;
#SBATCH --gres mic:2&lt;br /&gt;
#SBATCH --time=1:00:00&lt;br /&gt;
#SBATCH --partition=prod&lt;br /&gt;
#SBATCH -o slurm-%A.out&lt;br /&gt;
export OMP_NUM_THREADS=2&lt;br /&gt;
./runme_openmp_2phi&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Ekkor a feladat beküldése a következő paranccsal lehetséges.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
srun ./slurm_job_openmp_phi&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Alkalmazások optimalizálása a kártyára==&lt;br /&gt;
Ebben a fejezetben optimalizálási lehetőségeket mutatunk be, melyek segítségül szolgálhatnak az egyes alkalmazások hatékonyabb futása érdekében ezzel jobban kihasználni a rendelkezésre álló erőforrások adta számítási kapacitás.&lt;br /&gt;
&lt;br /&gt;
===Környezeti változók===&lt;br /&gt;
Lehetséges csak a Phi kártyákon használt környezeti változók beállítása, amit az alkalmazásunk használni is tud. &amp;lt;br /&amp;gt;&lt;br /&gt;
Példánkban az OpenMP esetén használható szálak számát állíthatjuk be:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export MIC_ENV_PREFIX=XEONPHI&lt;br /&gt;
export OMP_NUM_THREADS=12&lt;br /&gt;
export XEONPHI_OMP_NUM_THREADS=240&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
A beállított prefix tetszőleges karaktersorozat lehet, a lényeg, hogy ezt a beállított prefixet használjuk a környezeti változók esetén is!&lt;br /&gt;
 &lt;br /&gt;
===Hosszredukció===&lt;br /&gt;
Többnyire a fordítók, ha nem mondjuk meg explicite, a dupla pontosságú változatot választják a lehetséges változó- és függvény típusok közül. Amennyiben a számításunk nem igényli a dupla pontosságot, akkor használjuk a forrásban mindenhol az adott típus vagy függvény egyszeres pontosságú változatát, mellyel jelentős teljesítmény növekedést tudunk elérni. Továbbá az egyes műveletek futási ideje nem egyforma, így a műveletet helyettesíthetjük más művelettel, ami gyorsabb, akkor ismét javíthatunk a hatékonyságon. &amp;lt;br /&amp;gt;&lt;br /&gt;
Íme pár példa, amit alkalmazni lehet:&lt;br /&gt;
* &amp;quot;''/''&amp;quot; művelet helyett &amp;quot;''*''&amp;quot; -t használjunk, mert azt gyorsabban végre tudja hajtani a processzor&lt;br /&gt;
* ''pow'' =&amp;gt; ''1.0 / sqrt()''    , tetszőleges hatványozás, pl.: 3/2-ik hatvány&lt;br /&gt;
* hardver áltatl támogatott műveletek:&lt;br /&gt;
** ''exp'' =&amp;gt; ''exp2''&lt;br /&gt;
** ''log'' =&amp;gt; ''log2''&lt;br /&gt;
* dupla pontosság -&amp;gt; egyszeres pontosság lebegőpontos számok és műveletek esetén &lt;br /&gt;
** ''1.0'' =&amp;gt; ''1.0f''&lt;br /&gt;
** ''pow'' =&amp;gt; ''powf''&lt;br /&gt;
&lt;br /&gt;
===Vektorizáció===&lt;br /&gt;
A vektorizáció azon fordítási időben eldönthető optimalizációs faktor, amely nagy mértékben függ attól a hardver felépítésétől, amire éppen a fordítás történni fog. Legfontosabb befolyásoló tényezők a processzor cache méretei illetve a vektor műveletekért felelős aritmetikai egységének jellemzői mivel ezeket használja fel a fordító az optimalizálási folyamata során. &amp;lt;br /&amp;gt;&lt;br /&gt;
Az automatikus vektorizáció minimum -O2 szinttől érvényes, de mivel általában ez a szint az alapértelmezett, ezért minden fordításnál alkalmazásra kerül. &amp;lt;br /&amp;gt;&lt;br /&gt;
A fordító programnak a következő kapcsolókat lehet megadni, melyek segítenek a minél jobb vektorizációban:&lt;br /&gt;
* ''-qopt-report=&amp;lt;szám&amp;gt;'' ; az optimalizációs jelentés részletességét lehet megadni 0-tól 5-ig, ahol az 5-s szint a legrészletesebbet jelenti&lt;br /&gt;
* ''-qopt-report-phase:vec'' ; a vektorizációról szeretnék jelentést kapni&lt;br /&gt;
* ''-opt-report-file=&amp;lt;fájlnév&amp;gt;'' ; megadható a riportfájl neve&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
icpc autovec.cc -mmic -qopt-report=5 -opt-report-file=vectorization.oMIC.optrpt -qopt-report-phase:vec&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====SIMD====&lt;br /&gt;
Az adatok hatékonyabb feldolgozására az egyik megvalósítás a SIMD (Single Instruction Multiple Data), amikor is a különálló adatokat úgy kezeli, mintha az vektorok volnának és azon végzi el a vektor-műveletet. Ezzel egy a hardver által támogatott műveletet végzünk egy lépésben egy nagyobb adatmennyiségen, ezzel párhuzamosítva a feldolgozási folyamatot. Ezzel a módszerrel nagyon jó adat szintű párhuzamosítást lehet elérni.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Fájl:Simd.png|SIMD]]&lt;br /&gt;
&lt;br /&gt;
Főbb támogatott műveletek:&lt;br /&gt;
* aritmetikai műveletek, bináris operátorok&lt;br /&gt;
* összehasonlítások&lt;br /&gt;
* konverziók és típus kasztolások&lt;br /&gt;
* bit-műveletek: NOT, AND, OR, XOR, XAND&lt;br /&gt;
&lt;br /&gt;
Használata: '''simd''' fordítási direktívával lehetséges &amp;lt;br /&amp;gt;&lt;br /&gt;
Például:&lt;br /&gt;
* egyszerű vektorizáció&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma simd&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
  A[i] += B[i];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* adat párhuzamosítás OpenMP segítségével&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for simd&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
  DoSomeWork(A[i]);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====AoS -&amp;gt; SoA====&lt;br /&gt;
A kiindulás probléma az, hogy a struktúrákat tartalmazó vektort nem lehet jól vektorizálni, mivel a cache-ben tárolt adatok nagy többsége feleslegesen van bent, mivel azon adatokat csak egy későbbi ciklus futás során fogja az alkalmazás feldolgozni. Tehát ezen adatok jelenleg feleslegesen foglalják a cache területet, ahelyett, hogy hasznos adatot tárolnánk ott.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
struct MyStructure {&lt;br /&gt;
  float x, y, z;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void My_Function(cons int nItem, MyStructure* const Items, const float delta) {&lt;br /&gt;
  for (int i = 0; i &amp;lt; nItem; i++) {&lt;br /&gt;
    ...&lt;br /&gt;
    for (int j = 0; j &amp;lt; nItem; j++) {&lt;br /&gt;
      const float dx = Items[j].x - Items[i].x;&lt;br /&gt;
      const float dy = Items[j].y - Items[i].y;&lt;br /&gt;
      const float dz = Items[j].z - Items[i].z;&lt;br /&gt;
      ...  &lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  ... &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A vektorban a struktúrák tárolása helyett minden elemnek egy dedikált vektort allokálunk és így könnyen tudja a fordító vektorizálni azt. Természetesen segítségként a vektor feldolgozását &amp;quot;tömbösítettük&amp;quot; a tileSize méret megadásával illetve a ''unroll'' fordítási direktíva segítségével.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
struct MyStructureSet {&lt;br /&gt;
  float *x, *y, *z;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void My_Function(cons int nItem, MyStructure* const Items, const float delta) {&lt;br /&gt;
  const int tileSize = 16;&lt;br /&gt;
#pragma omp parallel for schedule(guided)&lt;br /&gt;
  for (int ii = 0; ii &amp;lt; nItem; ii+= tileSize) {&lt;br /&gt;
    ...&lt;br /&gt;
#pragma unroll(tileSize)&lt;br /&gt;
    for (int j = 0; j &amp;lt; nItem; j++) {&lt;br /&gt;
#pragma unroll(tileSize)&lt;br /&gt;
      for (int i = ii; i &amp;lt; ii + tileSize; i++) {&lt;br /&gt;
        const float dx = Items.x[j] - Items.x[i];&lt;br /&gt;
        const float dy = Items.y[j] - Items.y[i];&lt;br /&gt;
        const float dz = Items.z[j] - Items.z[i];&lt;br /&gt;
        ...&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  ... &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Hatékony hossz választás====&lt;br /&gt;
Olyan esetben jelent előnyt, ha például az n*n-es mátrix esetén n nem osztható 16-val, mert akkor a vektorizáció nem teljesen hajtható végre. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// n%16 != 0&lt;br /&gt;
float* A = _mm_malloc(sizeof(float)*n*n, 64);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Ilyen esetben mindig marad egy kis vektor részlet, amire elemenként kell elvégezni a műveletet.&amp;lt;br /&amp;gt;&lt;br /&gt;
A megoldást az jelenti, hogy a memória allokálást kiterjesztjük és nagyobb memória területet foglalunk a változónak, így már alkalmazható lesz a vektorizáció, mivel a maradvány területen is el tudjuk végezni a műveletet egy lépésben.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int lda=n;&lt;br /&gt;
if (n % 16 != 0) lda += (16 - n%16); // most lda%16==0&lt;br /&gt;
float* A = _mm_malloc(sizeof(float)*n*lda, 64);&lt;br /&gt;
&lt;br /&gt;
// használat&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
  for (int j = 0; j &amp;lt; n; j++)&lt;br /&gt;
    A[i*lda + j] = ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====&amp;quot;Héj ciklusok&amp;quot; kiküszöbölése====&lt;br /&gt;
Ciklusok esetén lehetséges, hogy a vektorizációt nem teljes mértékben ismeri fel a fordító program. &lt;br /&gt;
Ekkor segíthetünk a fordítónak egy fordítási direktívával ('''#pragma vector aligned'''), ha teljes mértékben biztosak vagyunk, hogy a ciklusunk vektorizálható!&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma vector aligned&lt;br /&gt;
  for (int j = 0; j &amp;lt; n; j++)&lt;br /&gt;
    A[i*lda + j] -= ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Ettől jelentős futás idejű javulást érhetünk el, mivel ezzel a #pragma-val kikapcsoltuk a futás idejű ellenőrzést, de előfordulhatnak olyan esetek, főleg amikor nem jól mértük fel a ciklusunkat és nem sikerült a vektorizálás, hogy ''Segmentation fault'' hibára fogunk futni.&lt;br /&gt;
&lt;br /&gt;
====Regularizáció====&lt;br /&gt;
Van egy ciklusunk, ami egy vektor elemeit módosítja. A processzor az adatokat &amp;quot;csokorba szedve&amp;quot; emeli be a belső kis méretű cache-be. Egyszerre természetesen több vektor elem is bekerül a a cache-be ezzel gyorsítva a feldolgozást. Ezen vektor iterációk valahogyan ráilleszkedik a vektorra.&lt;br /&gt;
&lt;br /&gt;
[[Fájl:regularization.png|Vektor regularizáció]]&lt;br /&gt;
&lt;br /&gt;
Abban az esetben, amikor ez az illesztés nem teljesen tökéletes (lsd. piros elemek), keletkeznem héj ciklusok, melyeket a regularizációval is eliminálni lehet. &amp;lt;br /&amp;gt;&lt;br /&gt;
A feladat eredetileg valahogyan így nézhet ki:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
for (int i = 0; i&amp;lt;n; i++)&lt;br /&gt;
  A[i]=...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ez a következő átalakítással már alkalmas vektorizációra és nem lesz &amp;quot;peel loop&amp;quot; probléma.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
for (int b=0; b&amp;lt;n; b++) {&lt;br /&gt;
  const int jStart = b- b%16; // mindig osztható 16-val&lt;br /&gt;
  ...&lt;br /&gt;
#pragma simd&lt;br /&gt;
  for (int j = jStart; j&amp;lt;n; j++)&lt;br /&gt;
    A[j]=...&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Párhuzamosítás===&lt;br /&gt;
Több féle mód létezik a párhuzamosításra. Korábban említettük a SIMD technikát, ami adatpárhuzamosításra való 1 processzoron belül, de létezik még processzorok közti párhuzamosítás, amit az OpenMP segítségével tudunk hatékonyan megvalósítani. Amennyiben gépek közti párhuzamosításra, üzenek küldésre van szükség, akkor az MPI-t kell segítségül hívni. Általános megoldás nem létezik minden feladatra, mert MPI is alkalmas lehet gépen belüli párhuzamosításra, nem csak az OpenMP. Amit mérlegelni kell, hogy mi az amire az alkalmazásnak szükséges van illetve mekkora kommunikációs overhead keletkezik, ha lecseréljük az OpenMP-t MPI-ra. Általában az egymástól független számításokat OpenMP-vel, míg a kommunikációt igénylő feladatokra MPI-t szoktak használni.&lt;br /&gt;
&lt;br /&gt;
====Szálak párhuzamosítása====&lt;br /&gt;
Az OpenMP-nek környezeti változók segítségével megadható a párhuzamosan futtatható szálak száma. Ezt a következő paranccsal tudjuk megtenni, ahol 5-re állítjuk.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export OMP_NUM_THREADS=5&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Forrásban a párhuzamos szálakat a ''#pragma omp parallel'' segítségével adhatjuk meg.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for              	//külső for szálak párhuzamosítása&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
#pragma simd				// vektorizáció a belső cikluson&lt;br /&gt;
  for (int j = 0; j &amp;lt; m; j++)&lt;br /&gt;
    My_Function(A[i][j]);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A fordítási direktívák össze is vonhatóak, ilyenkor 1 ciklus is elég.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for simd&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
    My_Function(A[i]);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=====Változók láthatósága, megosztása=====&lt;br /&gt;
Változók hatókörét a programozási nyelven kívül pragma-k segítségével is beállíthatjuk, hogy megosztott legyen a változó vagy a szálra nézve saját példány. Amennyiben nem definiáljuk felül, akkor minden változó megosztott. A felül definiálást a private és a shared paraméterekkel lehet megadni az OpenMP-nek.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int A, B, C;&lt;br /&gt;
&lt;br /&gt;
#pragma omp parallel private(A) shared(B)&lt;br /&gt;
{...}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Az itteni példában az B és C megosztott változó a szálak között, míg A-t minden szálnak lemásolja a futás kezdetén.&lt;br /&gt;
&lt;br /&gt;
====Párhuzamos szálak ütemezése====&lt;br /&gt;
OpenMp a szálak ütemezésére három féle módod kínál fel.&lt;br /&gt;
Módok:&lt;br /&gt;
* '''static''' (alapértelmezett): egyenletesen arányban egymás után elosztja a processzeket az indított szálakon, ezért a futási idő tetszőlegesen elhúzódhat, ha egyes processzek lassabban futnak le.&lt;br /&gt;
* '''dynamic''': minden esetben az első szabad szálra kerül a következő processz ütemezése, folyamatos kihasználtságot elérve, de &amp;quot;költséges&amp;quot; a sok-sok ütemezés&lt;br /&gt;
* '''guided''': Az előző két mód ötvözése. Nagyobb procesz számot ütemez az éppen szabad szálakra, melyek számát folyamatosan csökkenti a futás előre haladtával, hogy körülbelül egyszerre fejeződjön be minden szál futása, ezzel csökkentve a dynamic esetén magad ütemezési időráfordítást.&lt;br /&gt;
&lt;br /&gt;
Mindhárom esetben megadható az egyszerre ütemezett processzek száma, amit a ''chunk'' értéke határoz meg. Ha megvan adva a chunk értéke, akkor az minden esetben annak megfelelő processzámot ütemez a szálakra.&lt;br /&gt;
Használata:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel [schedule (&amp;lt;mode&amp;gt;,&amp;lt;chunk&amp;gt;) {...}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Szálak szinkronizációja====&lt;br /&gt;
Szála szinkronizálását többféle módon lehet megvalósítani, de ezen megoldások sajno jelentősen lelassítják a program futását.&lt;br /&gt;
* mutexxel: nagyon lassú, állandóan várnak egymásra a párhuzamos szálak&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++) {&lt;br /&gt;
#pragma omp critical&lt;br /&gt;
  { // kritikus szekció, csak egy szál hajthatja végre&lt;br /&gt;
    total = total + i;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* atomi művelet definiálása: sajnos még ez is lassú, de itt már nem olyan &amp;quot;erős&amp;quot; a szinkronizáció, ezért rendkívül sok limitációja van az itt használható atomi műveleteknek.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++) {&lt;br /&gt;
#pragma omp atomic&lt;br /&gt;
   total += i;&lt;br /&gt;
}	&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=====Párhuzamos redukció=====&lt;br /&gt;
Az itt bemutatott eljárás gyorsabb mint a fenti szinkronizációs megoldás, de sajnos nem alkalmazható vektorokra! A redukciót a ''reduction(operation: scalar)'' formában kell megadni.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int sum = 0;&lt;br /&gt;
#pragma omp parallel for reduction(+: sum)&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++) {&lt;br /&gt;
  sum = sum + i;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Tulajdonképpen a fenti problémakörre a következő minta ad megoldást. Ekkor egy külön változóban akkumuláltatjuk a szál általi összeget és ezen értékeke összegyűjtését végezzük csak atomi műveletként.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int sum = 0;&lt;br /&gt;
#pragma omp parallel&lt;br /&gt;
{&lt;br /&gt;
  int sum_thr = 0;&lt;br /&gt;
#pragma omp for&lt;br /&gt;
  for (int i=0; i&amp;lt;n; i++)&lt;br /&gt;
    sum_thr += i;&lt;br /&gt;
&lt;br /&gt;
#pragma omp atomic&lt;br /&gt;
  sum += sum_thr;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Ciklusok párhuzamosítása====&lt;br /&gt;
Ciklus párhuzamosítására a következő mintát szokás használni.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel&lt;br /&gt;
{&lt;br /&gt;
  // Az itt definiált forrást minden szál esetén végre fog hajtani&lt;br /&gt;
  ...&lt;br /&gt;
#pragma omp for [schedule (&amp;lt;mode&amp;gt;,&amp;lt;chunk&amp;gt;)&lt;br /&gt;
  for (int i = 0; i &amp;lt; n; i++) {&lt;br /&gt;
    // Ez a kódrész lesz párhuzamosítva&lt;br /&gt;
    ...&lt;br /&gt;
  }&lt;br /&gt;
  // ... és ez a köd is minden szál esetén végrehajtódik&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Ciklus kifejtés====&lt;br /&gt;
A technika már egy példa erejéig a vektorizációnál is látható, de ott nem került részletezésre. A megoldás lényege, hogy a kellően nagy ciklust szétdaraboljuk kisebb darabokra, amit már külön feldolgozhatóak, ezzel párhuzamosítva a folyamatokat. Az egy darab kiindulási ciklusból két darab egymásba ágyazott ciklust hozunk létre, ami ugyanazon a feladattéren dolgozik. &lt;br /&gt;
&lt;br /&gt;
Eredeti állapot:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++) {...}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Átírt változat:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int STRIP=1024;&lt;br /&gt;
for (int ii = 0; ii &amp;lt; n; ii += STRIP)&lt;br /&gt;
 for (int i = ii; i &amp;lt; ii+STRIP; i++) {...}&lt;br /&gt;
&lt;br /&gt;
// ha még van maradék elem, akkor azokon is végrehajtjuk a számításokat&lt;br /&gt;
if (n%STRIP != 0) {&lt;br /&gt;
  for (i=n-(n%STRIP)+1; i&amp;lt;n; i++) {...}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A darabok mérete (STRIP értéke) az adott rendszer és feladat függvényében változhat, tehát ez egyfajta &amp;quot;tuning&amp;quot; paraméter.&lt;br /&gt;
&lt;br /&gt;
====Automatikus ciklusösszevonás====&lt;br /&gt;
Az OpenMP támogatást nyújt több egymásba ágyazott ciklus &amp;quot;összevonására&amp;quot;. Ehhez csak a '''collaps''' fordítási direktívát és az összevonandó ciklusok számát kell megadni. &amp;lt;br /&amp;gt;&lt;br /&gt;
FONTOS: ekkor az automatikus vektorizáció kikapcsolásra kerül!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for collaps(2)&lt;br /&gt;
for (int i=0; i&amp;lt;n; i++)&lt;br /&gt;
  for( int j=0; j&amp;lt;m; j++){...}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A fneti példában a 2db for ciklis kerül párhuzamosításra úgy, hogy a fordítás során 1db ciklus generálódik belőle, ami a következő paraméterezéssel ekvivalens:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for&lt;br /&gt;
for (int c=0; c &amp;lt; n*m; c++) {&lt;br /&gt;
  int i = c/n;&lt;br /&gt;
  int j = c%n;&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
[[Kategória: HPC]]&lt;/div&gt;</summary>
		<author><name>Szigeti(AT)niif.hu</name></author>	</entry>

	<entry>
		<id>https://wiki.niif.hu/index.php?title=Intel_Xeon_Phi&amp;diff=4139</id>
		<title>Intel Xeon Phi</title>
		<link rel="alternate" type="text/html" href="https://wiki.niif.hu/index.php?title=Intel_Xeon_Phi&amp;diff=4139"/>
				<updated>2017-05-09T06:53:42Z</updated>
		
		<summary type="html">&lt;p&gt;Szigeti(AT)niif.hu: /* automatikus kódátvitel */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Az oldalon található információk segítséget nyújtanak a HPC felhasználóinknak, hogy alkalmazásaikat miként portolhatják valamint optimalizálhatják a hatékonyabb futást szem előtt tartva Intel Xeon Phi koprocesszor kártyákon.&lt;br /&gt;
&lt;br /&gt;
{{INFO|Egy 10 alkalmas, angol nyelvű Xeon Phi programozás tutoriál videóanyaggal és példaprogrammal elérhető a [[http://colfaxresearch.com/how-16-04/ Colfax oldalán]]}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Hardver bemutatása==&lt;br /&gt;
Intel® Xeon Phi™ Coprocessor 7120P&lt;br /&gt;
&amp;lt;gallery&amp;gt;&lt;br /&gt;
Fájl:Xeon-phi-family-rwd.png.rendition.intel.web.720.405.png|&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-  align=&amp;quot;center&amp;quot;&lt;br /&gt;
! CPU&lt;br /&gt;
! threads/Core&lt;br /&gt;
!max threads&lt;br /&gt;
! CPU Freq&lt;br /&gt;
! Memória&lt;br /&gt;
! L1 cache&lt;br /&gt;
! L2 cache&lt;br /&gt;
! Interfész&lt;br /&gt;
! Ptot&lt;br /&gt;
! Számítási kapacitás&lt;br /&gt;
|-  align=&amp;quot;center&amp;quot;&lt;br /&gt;
| 61&lt;br /&gt;
| 4 Hardver&lt;br /&gt;
| 244&lt;br /&gt;
| 1238 GHz&lt;br /&gt;
| 16GB GDDR5&lt;br /&gt;
| 32KB/core&lt;br /&gt;
| 512KB/core&lt;br /&gt;
| PCIe&lt;br /&gt;
| 300W&lt;br /&gt;
| 1,2 TFlop/s dupla pontosság&lt;br /&gt;
|-  align=&amp;quot;center&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
| OpenMP-nél csak '''240'''&lt;br /&gt;
|&lt;br /&gt;
| ~100 cycles&lt;br /&gt;
~174 GB/s&lt;br /&gt;
| ~3 cycles&lt;br /&gt;
| ~10 cycles&lt;br /&gt;
| ~7 GB/s&lt;br /&gt;
~1 microsecond&lt;br /&gt;
|&lt;br /&gt;
| 2,4 TFlop/s egyszeres pontosság&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Az Intel® 1. generációs Xeon Phi™ koprocesszorában található 61 processzor ugyanazt a 16GB memóriát éri el, de semmilyen módon se fér hozzá közvetlenül a hoszt gép fizikai memóriájához.&amp;lt;br /&amp;gt;&lt;br /&gt;
[[Budapest2_klaszter|Budapest2]] és [[HPC#Szupersz.C3.A1m.C3.ADt.C3.B3g.C3.A9peink_.C3.B6sszehasonl.C3.ADt.C3.A1sa|Debrecen3-Phi]] gépeinkben számítási nodonként 2-2 Phi™ koprocesszor található. &amp;lt;br/&amp;gt;&lt;br /&gt;
A kártyákon speciális célú Linux kernel fut melyre a belépés limitáltan csak a rendszergazdák számára lehetséges SSH-n keresztül a hoszt gépről. Természetesen a kártyára belépve olyan érzésünk lehet, mintha egy sok processzoros gépen dolgoznánk, ami rendelkezik rengeteg memóriával. Ez így is van, de a hatékonyabb kihasználás érdekében az SSH-n keresztüli elérés nem lehetséges, csak a Slurm segítségével valamint a programozási nyelvek által támogatott csatornákon keresztül. Ennek részletezése lentebb megtalálható.&lt;br /&gt;
&lt;br /&gt;
==Használati módok==&lt;br /&gt;
A koprocesszor használatai módjait két csoportba lehet sorolni. A '''natív''' és az '''offload''' használatot.  &amp;lt;br/&amp;gt;&lt;br /&gt;
Ezen használati módokról a lenti fejezetekben több részletet is megtudhat. Az itt megfogalmazott &amp;quot;támogatás&amp;quot; csak a mi infrastruktúránkra érvényes! &amp;lt;br /&amp;gt;&lt;br /&gt;
A bemutatott példák ''c++'' nyelven vannak, de természetesen ''c'' és ''fortran'' esetén is megtalálható az ekvivalens módszer.&lt;br /&gt;
&lt;br /&gt;
===a) nem támogatott mód===&lt;br /&gt;
====közvetlen natív használat====&lt;br /&gt;
A natív mód arra szolgál, hogy a MIC kártyára  lefordított alkalmazásunkat felmásoljuk a kártyára, majd belépünk SSH-n a kártyára és azon futtatjuk a bináris kódunkat. &amp;lt;br /&amp;gt;&lt;br /&gt;
Biztonsági okokból a közvetlen natív használat az NIIF HPC infrastruktúrán nem elérhető. &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===b) támogatott, de nem preferált használat===&lt;br /&gt;
====automatikus kódátvitel====&lt;br /&gt;
Ebben az esetben nem kell átmásolnunk a bináris alkalmazásunkat a kártyára sőt még csak be se kell lépnünk, mert ezt a két lépést egy beépített tool végzi el. &amp;lt;br/&amp;gt;&lt;br /&gt;
A tool eléréséhez szükséges parancs neve: '''micnativeloadex'''&lt;br /&gt;
&lt;br /&gt;
Ebben az esetben a workflow a következőképpen módosul:&lt;br /&gt;
# Alkalmazás megírása. például [[Média:hello.cc|''hello.cc'']]&lt;br /&gt;
# Alkalmazás fordítása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;icpc -o hello-MIC -mmic hello.cc&amp;lt;/pre&amp;gt;&lt;br /&gt;
# A tool futásához szükséges library-k megadása &amp;lt;br&amp;gt; &amp;lt;pre&amp;gt;export SINK_LD_LIBRARY_PATH=/opt/intel/composerxe-&amp;lt;verzió&amp;gt;/compiler/lib/mic&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Alkalmazás futtatása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;micnativeloadex hello-MIC&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| BUDAPEST2[cn10] phi (0)$ export SINK_LD_LIBRARY_PATH=/opt/intel/composerxe-2011.4.191/compiler/lib/mic &amp;lt;br/&amp;gt;BUDAPEST2[cn10] phi (0)$ micnativeloadex hello-MIC &amp;lt;br/&amp;gt;Hello world! I have 244 logical cores.&amp;lt;br/&amp;gt;BUDAPEST2[cn10] phi (0)$&lt;br /&gt;
|}&lt;br /&gt;
Intel MKL használat esetén a SINK_LD_LIBRARY_PATH természetesen kiegészítendő az MKL könyvtárak elérésével.&lt;br /&gt;
&amp;lt;pre&amp;gt;intelhome=/opt/intel/compilers_and_libraries_2016.1.150/linuxexport&lt;br /&gt;
export SINK_LD_LIBRARY_PATH=$intelhome/compiler/lib/mic:$intelhome/mkl/lib/mic&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===c) támogatott módok===&lt;br /&gt;
====Offload====&lt;br /&gt;
Az Offload modell esetén szükséges a forráskód módosítása, mégpedig úgy, hogy azon kódrészletet, amit a koprocesszoron szeretnénk futtatni, ki kell egészíteni egy fordítási direktívával. Az alkalmazás fordításakor elegendő csak a CPU-ra való binárist készíteni mivel a modell végzi a megfelelő forrás részlet mozgatását és futtatását.&amp;lt;br /&amp;gt;&lt;br /&gt;
[[Fájl:offload.png|Offload modell]] &amp;lt;br /&amp;gt;&lt;br /&gt;
Példánkban jól látható, hogy a második kiíratás már a koprocesszoron fut.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
int main(int argc, char * argv[]) {&lt;br /&gt;
  printf(&amp;quot;Hello world from host!\n&amp;quot;);&lt;br /&gt;
#pragma offload target(mic)&lt;br /&gt;
  {&lt;br /&gt;
    printf(&amp;quot;Hello world from coprocessor!\n&amp;quot;); fflush(stdout);&lt;br /&gt;
  }&lt;br /&gt;
  printf(&amp;quot;Bye\n&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ebben a példában az operációs rendszer választja ki, hogy melyik Phi kártyán fusson le a kódrészlet. Amennyiben mi szeretnénk explicite megmondani, hogy melyik kártyán fusson, akkor a fordítási direktívában a mic kulcsszó után kell tenni egy &amp;quot;:&amp;quot;-t és a kártya sorszámát [mi esetünkben 0 vagy 1]. Ekkor így nézne ki a direktíva: ''#pragma offload target(mic:1)''&lt;br /&gt;
&lt;br /&gt;
Amennyiben az offload szekcióban függvény hívás is szerepel, akkor szükségeltetik az függvény futásához szükséges változók adatterületének átmásolása a Phi kártyára, majd a lefutás után visszamásolni a hoszt gépre. Ezen másolást az ''offload_attribute'' ''push'' és ''pop'' paraméterei segítségével határozhatjuk meg. &amp;lt;br /&amp;gt;&lt;br /&gt;
A következő mátrix-vektor szorzás példánál az A mátrix globális változó, míg a b és c vektorok a függvény paraméterei.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//A változók felmásolásra kerülnek a Xeon Phi-re&lt;br /&gt;
#pragma offload_attribute(push, target(mic))&lt;br /&gt;
  double* A;&lt;br /&gt;
  void MatrixVector(double *b, double *c, double m, double n) {&lt;br /&gt;
    ...&lt;br /&gt;
    c[] = A[..]*b[]&lt;br /&gt;
  }&lt;br /&gt;
// felmásolt objektumok visszamásolása&lt;br /&gt;
#pragma offload_attribute(pop)&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
  ...&lt;br /&gt;
#pragma offload target(mic) in(A[0:n*m])&lt;br /&gt;
// így is meg lehetne adni: #pragma offload target(mic:0) in(A : length(n*m))	        &lt;br /&gt;
  {&lt;br /&gt;
    MatrixVector(b, c, m, n);&lt;br /&gt;
  }&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ekkor minden memória területet 2x kell másolni, egyszer fel a kártyára, majd másodszor vissza a hoszt gépre, pedig elegendő lenne csak az eredmény vektort visszamásolni. Ezen optimalizálással egy későbbi fejezetben foglalkozunk. &amp;lt;p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Futás során kaphatunk részletes riportot is, hogy pontosan milyen adatmozgatások történnek az alkalmazás futása során. Ehhez nincs szükség másra csak az '''OFFLOAD_REPORT''' környezeti változó megfelelő beállítása. A beállított értékek Automatic Offload esetén 1, 2, míg Compiler Assisted Offload esetén 1, 2 és 3 lehetnek, attól függően mennyire részletes riportot szeretnénk kapni. &amp;lt;br /&amp;gt;&lt;br /&gt;
beállítás:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export OFFLOAD_REPORT=3&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
példa kimenet a hello alkalmazásra:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Hello world from host!&lt;br /&gt;
[Offload] [MIC 0] [File]                    hello_offload.cc&lt;br /&gt;
[Offload] [MIC 0] [Line]                    5&lt;br /&gt;
[Offload] [MIC 0] [Tag]                     Tag 0&lt;br /&gt;
Hello world from coprocessor!&lt;br /&gt;
[Offload] [HOST]  [Tag 0] [CPU Time]        0.554653(seconds)&lt;br /&gt;
[Offload] [MIC 0] [Tag 0] [CPU-&amp;gt;MIC Data]   0 (bytes)&lt;br /&gt;
[Offload] [MIC 0] [Tag 0] [MIC Time]        0.000140(seconds)&lt;br /&gt;
[Offload] [MIC 0] [Tag 0] [MIC-&amp;gt;CPU Data]   0 (bytes)&lt;br /&gt;
Bye&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Offload + OpenMP====&lt;br /&gt;
Amennyiben nem csak 1 processzort szeretnénk használni a Phi kártyánkon és az alkalmazásunkban használt számítás megengedi a párhuzamosítást, akkor az OpenMP segítségével párhuzamosíthatjuk a megfelelő számítást. &amp;lt;br /&amp;gt;&lt;br /&gt;
[[Fájl:Offload+openmp.png|Offload modell és OpenMP]] &amp;lt;br /&amp;gt;&lt;br /&gt;
Példánk maradjon ugyanúgy a mátrix-vektor szorzás. Amennyiben a teljes kártya adta kapacitást ki szeretnénk használni, akkor az OMP threadek számát 240-re kell beállítani. Azért nem 244-re, mivel 1 CPU-t dedikáltan csak a kommunikációra használ a rendszer és 1 CPU 4 hardver threaddel rendelkezik.&lt;br /&gt;
Ekkor a példánk így változik:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;omp.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#pragma offload_attribute(push, target(mic))&lt;br /&gt;
void multiply(int n, int m, double* A, double* b, double* c){&lt;br /&gt;
  const int nCPUs = omp_get_max_threads();   //240&lt;br /&gt;
  omp_set_num_threads(nCPUs);&lt;br /&gt;
#pragma omp parallel for schedule(dynamic, 1)&lt;br /&gt;
  for ( int i = 0 ; i &amp;lt; m ; i++)&lt;br /&gt;
     for ( int j = 0 ; j &amp;lt; n ; j++)&lt;br /&gt;
       c[i] += A[i*n+j] * b[j];&lt;br /&gt;
}&lt;br /&gt;
#pragma offload_attribute(pop)&lt;br /&gt;
&lt;br /&gt;
int main(){&lt;br /&gt;
  double * A = (double*) malloc(sizeof(double)*n*m);&lt;br /&gt;
  ...&lt;br /&gt;
#pragma offload target(mic) in(A:length(n*m))&lt;br /&gt;
     multiply(n, m, A, b, c);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Az alkalmazás fordításánál a fordítónak meg kell adni, hogy használhja az openmp library-ket is. Ezt a '''-qopenmp''' kapcsolóval tudjuk elérni.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
icpc -o matrix-CPU -qopenmp matrix.cc&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====MPI + Offload + OpenMP====&lt;br /&gt;
Abban az esetben, ha 1 számítási node nem elég, MPI segítségével össze tudunk kötni több nodet, heterogén rendszert alkotva, és azokon alkalmazható a fenti Offload + OpenMP modell. &amp;lt;br /&amp;gt;&lt;br /&gt;
[[Fájl:Mpi+offload+openmp.png|MPI elosztás valamint Offload modell és OpenMP]] &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Phi használat slurm-mel==&lt;br /&gt;
Slurm esetén a mic opciót kell megadni a gres-knél. Amennyiben egynél több kártyára van szükség, akkor azt a ''mic:2''-vel lehetséges megadni. Ekkor két kártyát foglal le az ütemező a futtatandó feladatnak. &amp;lt;br /&amp;gt;&lt;br /&gt;
Egy lehetséges feladat, amit srun-nal lehet a slurmben beküldeni. debrecen3 esetén a ''prod-phi'' particiót kell használni&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ cat slurm_job_openmp_phi&lt;br /&gt;
&lt;br /&gt;
#!/bin/bash&lt;br /&gt;
#SBATCH -A &amp;lt;PROJECT NEVE&amp;gt;&lt;br /&gt;
#SBATCH --job-name=&amp;lt;JOB NEVE&amp;gt;&lt;br /&gt;
#SBATCH --gres mic:2&lt;br /&gt;
#SBATCH --time=1:00:00&lt;br /&gt;
#SBATCH --partition=prod&lt;br /&gt;
#SBATCH -o slurm-%A.out&lt;br /&gt;
export OMP_NUM_THREADS=2&lt;br /&gt;
./runme_openmp_2phi&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Ekkor a feladat beküldése a következő paranccsal lehetséges.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
srun ./slurm_job_openmp_phi&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Alkalmazások optimalizálása a kártyára==&lt;br /&gt;
Ebben a fejezetben optimalizálási lehetőségeket mutatunk be, melyek segítségül szolgálhatnak az egyes alkalmazások hatékonyabb futása érdekében ezzel jobban kihasználni a rendelkezésre álló erőforrások adta számítási kapacitás.&lt;br /&gt;
&lt;br /&gt;
===Környezeti változók===&lt;br /&gt;
Lehetséges csak a Phi kártyákon használt környezeti változók beállítása, amit az alkalmazásunk használni is tud. &amp;lt;br /&amp;gt;&lt;br /&gt;
Példánkban az OpenMP esetén használható szálak számát állíthatjuk be:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export MIC_ENV_PREFIX=XEONPHI&lt;br /&gt;
export OMP_NUM_THREADS=12&lt;br /&gt;
export XEONPHI_OMP_NUM_THREADS=240&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
A beállított prefix tetszőleges karaktersorozat lehet, a lényeg, hogy ezt a beállított prefixet használjuk a környezeti változók esetén is!&lt;br /&gt;
 &lt;br /&gt;
===Hosszredukció===&lt;br /&gt;
Többnyire a fordítók, ha nem mondjuk meg explicite, a dupla pontosságú változatot választják a lehetséges változó- és függvény típusok közül. Amennyiben a számításunk nem igényli a dupla pontosságot, akkor használjuk a forrásban mindenhol az adott típus vagy függvény egyszeres pontosságú változatát, mellyel jelentős teljesítmény növekedést tudunk elérni. Továbbá az egyes műveletek futási ideje nem egyforma, így a műveletet helyettesíthetjük más művelettel, ami gyorsabb, akkor ismét javíthatunk a hatékonyságon. &amp;lt;br /&amp;gt;&lt;br /&gt;
Íme pár példa, amit alkalmazni lehet:&lt;br /&gt;
* &amp;quot;''/''&amp;quot; művelet helyett &amp;quot;''*''&amp;quot; -t használjunk, mert azt gyorsabban végre tudja hajtani a processzor&lt;br /&gt;
* ''pow'' =&amp;gt; ''1.0 / sqrt()''    , tetszőleges hatványozás, pl.: 3/2-ik hatvány&lt;br /&gt;
* hardver áltatl támogatott műveletek:&lt;br /&gt;
** ''exp'' =&amp;gt; ''exp2''&lt;br /&gt;
** ''log'' =&amp;gt; ''log2''&lt;br /&gt;
* dupla pontosság -&amp;gt; egyszeres pontosság lebegőpontos számok és műveletek esetén &lt;br /&gt;
** ''1.0'' =&amp;gt; ''1.0f''&lt;br /&gt;
** ''pow'' =&amp;gt; ''powf''&lt;br /&gt;
&lt;br /&gt;
===Vektorizáció===&lt;br /&gt;
A vektorizáció azon fordítási időben eldönthető optimalizációs faktor, amely nagy mértékben függ attól a hardver felépítésétől, amire éppen a fordítás történni fog. Legfontosabb befolyásoló tényezők a processzor cache méretei illetve a vektor műveletekért felelős aritmetikai egységének jellemzői mivel ezeket használja fel a fordító az optimalizálási folyamata során. &amp;lt;br /&amp;gt;&lt;br /&gt;
Az automatikus vektorizáció minimum -O2 szinttől érvényes, de mivel általában ez a szint az alapértelmezett, ezért minden fordításnál alkalmazásra kerül. &amp;lt;br /&amp;gt;&lt;br /&gt;
A fordító programnak a következő kapcsolókat lehet megadni, melyek segítenek a minél jobb vektorizációban:&lt;br /&gt;
* ''-qopt-report=&amp;lt;szám&amp;gt;'' ; az optimalizációs jelentés részletességét lehet megadni 0-tól 5-ig, ahol az 5-s szint a legrészletesebbet jelenti&lt;br /&gt;
* ''-qopt-report-phase:vec'' ; a vektorizációról szeretnék jelentést kapni&lt;br /&gt;
* ''-opt-report-file=&amp;lt;fájlnév&amp;gt;'' ; megadható a riportfájl neve&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
icpc autovec.cc -mmic -qopt-report=5 -opt-report-file=vectorization.oMIC.optrpt -qopt-report-phase:vec&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====SIMD====&lt;br /&gt;
Az adatok hatékonyabb feldolgozására az egyik megvalósítás a SIMD (Single Instruction Multiple Data), amikor is a különálló adatokat úgy kezeli, mintha az vektorok volnának és azon végzi el a vektor-műveletet. Ezzel egy a hardver által támogatott műveletet végzünk egy lépésben egy nagyobb adatmennyiségen, ezzel párhuzamosítva a feldolgozási folyamatot. Ezzel a módszerrel nagyon jó adat szintű párhuzamosítást lehet elérni.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Fájl:Simd.png|SIMD]]&lt;br /&gt;
&lt;br /&gt;
Főbb támogatott műveletek:&lt;br /&gt;
* aritmetikai műveletek, bináris operátorok&lt;br /&gt;
* összehasonlítások&lt;br /&gt;
* konverziók és típus kasztolások&lt;br /&gt;
* bit-műveletek: NOT, AND, OR, XOR, XAND&lt;br /&gt;
&lt;br /&gt;
Használata: '''simd''' fordítási direktívával lehetséges &amp;lt;br /&amp;gt;&lt;br /&gt;
Például:&lt;br /&gt;
* egyszerű vektorizáció&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma simd&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
  A[i] += B[i];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* adat párhuzamosítás OpenMP segítségével&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for simd&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
  DoSomeWork(A[i]);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====AoS -&amp;gt; SoA====&lt;br /&gt;
A kiindulás probléma az, hogy a struktúrákat tartalmazó vektort nem lehet jól vektorizálni, mivel a cache-ben tárolt adatok nagy többsége feleslegesen van bent, mivel azon adatokat csak egy későbbi ciklus futás során fogja az alkalmazás feldolgozni. Tehát ezen adatok jelenleg feleslegesen foglalják a cache területet, ahelyett, hogy hasznos adatot tárolnánk ott.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
struct MyStructure {&lt;br /&gt;
  float x, y, z;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void My_Function(cons int nItem, MyStructure* const Items, const float delta) {&lt;br /&gt;
  for (int i = 0; i &amp;lt; nItem; i++) {&lt;br /&gt;
    ...&lt;br /&gt;
    for (int j = 0; j &amp;lt; nItem; j++) {&lt;br /&gt;
      const float dx = Items[j].x - Items[i].x;&lt;br /&gt;
      const float dy = Items[j].y - Items[i].y;&lt;br /&gt;
      const float dz = Items[j].z - Items[i].z;&lt;br /&gt;
      ...  &lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  ... &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A vektorban a struktúrák tárolása helyett minden elemnek egy dedikált vektort allokálunk és így könnyen tudja a fordító vektorizálni azt. Természetesen segítségként a vektor feldolgozását &amp;quot;tömbösítettük&amp;quot; a tileSize méret megadásával illetve a ''unroll'' fordítási direktíva segítségével.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
struct MyStructureSet {&lt;br /&gt;
  float *x, *y, *z;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void My_Function(cons int nItem, MyStructure* const Items, const float delta) {&lt;br /&gt;
  const int tileSize = 16;&lt;br /&gt;
#pragma omp parallel for schedule(guided)&lt;br /&gt;
  for (int ii = 0; ii &amp;lt; nItem; ii+= tileSize) {&lt;br /&gt;
    ...&lt;br /&gt;
#pragma unroll(tileSize)&lt;br /&gt;
    for (int j = 0; j &amp;lt; nItem; j++) {&lt;br /&gt;
#pragma unroll(tileSize)&lt;br /&gt;
      for (int i = ii; i &amp;lt; ii + tileSize; i++) {&lt;br /&gt;
        const float dx = Items.x[j] - Items.x[i];&lt;br /&gt;
        const float dy = Items.y[j] - Items.y[i];&lt;br /&gt;
        const float dz = Items.z[j] - Items.z[i];&lt;br /&gt;
        ...&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  ... &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Hatékony hossz választás====&lt;br /&gt;
Olyan esetben jelent előnyt, ha például az n*n-es mátrix esetén n nem osztható 16-val, mert akkor a vektorizáció nem teljesen hajtható végre. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// n%16 != 0&lt;br /&gt;
float* A = _mm_malloc(sizeof(float)*n*n, 64);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Ilyen esetben mindig marad egy kis vektor részlet, amire elemenként kell elvégezni a műveletet.&amp;lt;br /&amp;gt;&lt;br /&gt;
A megoldást az jelenti, hogy a memória allokálást kiterjesztjük és nagyobb memória területet foglalunk a változónak, így már alkalmazható lesz a vektorizáció, mivel a maradvány területen is el tudjuk végezni a műveletet egy lépésben.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int lda=n;&lt;br /&gt;
if (n % 16 != 0) lda += (16 - n%16); // most lda%16==0&lt;br /&gt;
float* A = _mm_malloc(sizeof(float)*n*lda, 64);&lt;br /&gt;
&lt;br /&gt;
// használat&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
  for (int j = 0; j &amp;lt; n; j++)&lt;br /&gt;
    A[i*lda + j] = ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====&amp;quot;Héj ciklusok&amp;quot; kiküszöbölése====&lt;br /&gt;
Ciklusok esetén lehetséges, hogy a vektorizációt nem teljes mértékben ismeri fel a fordító program. &lt;br /&gt;
Ekkor segíthetünk a fordítónak egy fordítási direktívával ('''#pragma vector aligned'''), ha teljes mértékben biztosak vagyunk, hogy a ciklusunk vektorizálható!&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma vector aligned&lt;br /&gt;
  for (int j = 0; j &amp;lt; n; j++)&lt;br /&gt;
    A[i*lda + j] -= ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Ettől jelentős futás idejű javulást érhetünk el, mivel ezzel a #pragma-val kikapcsoltuk a futás idejű ellenőrzést, de előfordulhatnak olyan esetek, főleg amikor nem jól mértük fel a ciklusunkat és nem sikerült a vektorizálás, hogy ''Segmentation fault'' hibára fogunk futni.&lt;br /&gt;
&lt;br /&gt;
====Regularizáció====&lt;br /&gt;
Van egy ciklusunk, ami egy vektor elemeit módosítja. A processzor az adatokat &amp;quot;csokorba szedve&amp;quot; emeli be a belső kis méretű cache-be. Egyszerre természetesen több vektor elem is bekerül a a cache-be ezzel gyorsítva a feldolgozást. Ezen vektor iterációk valahogyan ráilleszkedik a vektorra.&lt;br /&gt;
&lt;br /&gt;
[[Fájl:regularization.png|Vektor regularizáció]]&lt;br /&gt;
&lt;br /&gt;
Abban az esetben, amikor ez az illesztés nem teljesen tökéletes (lsd. piros elemek), keletkeznem héj ciklusok, melyeket a regularizációval is eliminálni lehet. &amp;lt;br /&amp;gt;&lt;br /&gt;
A feladat eredetileg valahogyan így nézhet ki:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
for (int i = 0; i&amp;lt;n; i++)&lt;br /&gt;
  A[i]=...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ez a következő átalakítással már alkalmas vektorizációra és nem lesz &amp;quot;peel loop&amp;quot; probléma.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
for (int b=0; b&amp;lt;n; b++) {&lt;br /&gt;
  const int jStart = b- b%16; // mindig osztható 16-val&lt;br /&gt;
  ...&lt;br /&gt;
#pragma simd&lt;br /&gt;
  for (int j = jStart; j&amp;lt;n; j++)&lt;br /&gt;
    A[j]=...&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Párhuzamosítás===&lt;br /&gt;
Több féle mód létezik a párhuzamosításra. Korábban említettük a SIMD technikát, ami adatpárhuzamosításra való 1 processzoron belül, de létezik még processzorok közti párhuzamosítás, amit az OpenMP segítségével tudunk hatékonyan megvalósítani. Amennyiben gépek közti párhuzamosításra, üzenek küldésre van szükség, akkor az MPI-t kell segítségül hívni. Általános megoldás nem létezik minden feladatra, mert MPI is alkalmas lehet gépen belüli párhuzamosításra, nem csak az OpenMP. Amit mérlegelni kell, hogy mi az amire az alkalmazásnak szükséges van illetve mekkora kommunikációs overhead keletkezik, ha lecseréljük az OpenMP-t MPI-ra. Általában az egymástól független számításokat OpenMP-vel, míg a kommunikációt igénylő feladatokra MPI-t szoktak használni.&lt;br /&gt;
&lt;br /&gt;
====Szálak párhuzamosítása====&lt;br /&gt;
Az OpenMP-nek környezeti változók segítségével megadható a párhuzamosan futtatható szálak száma. Ezt a következő paranccsal tudjuk megtenni, ahol 5-re állítjuk.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export OMP_NUM_THREADS=5&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Forrásban a párhuzamos szálakat a ''#pragma omp parallel'' segítségével adhatjuk meg.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for              	//külső for szálak párhuzamosítása&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
#pragma simd				// vektorizáció a belső cikluson&lt;br /&gt;
  for (int j = 0; j &amp;lt; m; j++)&lt;br /&gt;
    My_Function(A[i][j]);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A fordítási direktívák össze is vonhatóak, ilyenkor 1 ciklus is elég.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for simd&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
    My_Function(A[i]);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=====Változók láthatósága, megosztása=====&lt;br /&gt;
Változók hatókörét a programozási nyelven kívül pragma-k segítségével is beállíthatjuk, hogy megosztott legyen a változó vagy a szálra nézve saját példány. Amennyiben nem definiáljuk felül, akkor minden változó megosztott. A felül definiálást a private és a shared paraméterekkel lehet megadni az OpenMP-nek.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int A, B, C;&lt;br /&gt;
&lt;br /&gt;
#pragma omp parallel private(A) shared(B)&lt;br /&gt;
{...}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Az itteni példában az B és C megosztott változó a szálak között, míg A-t minden szálnak lemásolja a futás kezdetén.&lt;br /&gt;
&lt;br /&gt;
====Párhuzamos szálak ütemezése====&lt;br /&gt;
OpenMp a szálak ütemezésére három féle módod kínál fel.&lt;br /&gt;
Módok:&lt;br /&gt;
* '''static''' (alapértelmezett): egyenletesen arányban egymás után elosztja a processzeket az indított szálakon, ezért a futási idő tetszőlegesen elhúzódhat, ha egyes processzek lassabban futnak le.&lt;br /&gt;
* '''dynamic''': minden esetben az első szabad szálra kerül a következő processz ütemezése, folyamatos kihasználtságot elérve, de &amp;quot;költséges&amp;quot; a sok-sok ütemezés&lt;br /&gt;
* '''guided''': Az előző két mód ötvözése. Nagyobb procesz számot ütemez az éppen szabad szálakra, melyek számát folyamatosan csökkenti a futás előre haladtával, hogy körülbelül egyszerre fejeződjön be minden szál futása, ezzel csökkentve a dynamic esetén magad ütemezési időráfordítást.&lt;br /&gt;
&lt;br /&gt;
Mindhárom esetben megadható az egyszerre ütemezett processzek száma, amit a ''chunk'' értéke határoz meg. Ha megvan adva a chunk értéke, akkor az minden esetben annak megfelelő processzámot ütemez a szálakra.&lt;br /&gt;
Használata:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel [schedule (&amp;lt;mode&amp;gt;,&amp;lt;chunk&amp;gt;) {...}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Szálak szinkronizációja====&lt;br /&gt;
Szála szinkronizálását többféle módon lehet megvalósítani, de ezen megoldások sajno jelentősen lelassítják a program futását.&lt;br /&gt;
* mutexxel: nagyon lassú, állandóan várnak egymásra a párhuzamos szálak&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++) {&lt;br /&gt;
#pragma omp critical&lt;br /&gt;
  { // kritikus szekció, csak egy szál hajthatja végre&lt;br /&gt;
    total = total + i;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* atomi művelet definiálása: sajnos még ez is lassú, de itt már nem olyan &amp;quot;erős&amp;quot; a szinkronizáció, ezért rendkívül sok limitációja van az itt használható atomi műveleteknek.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++) {&lt;br /&gt;
#pragma omp atomic&lt;br /&gt;
   total += i;&lt;br /&gt;
}	&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=====Párhuzamos redukció=====&lt;br /&gt;
Az itt bemutatott eljárás gyorsabb mint a fenti szinkronizációs megoldás, de sajnos nem alkalmazható vektorokra! A redukciót a ''reduction(operation: scalar)'' formában kell megadni.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int sum = 0;&lt;br /&gt;
#pragma omp parallel for reduction(+: sum)&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++) {&lt;br /&gt;
  sum = sum + i;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Tulajdonképpen a fenti problémakörre a következő minta ad megoldást. Ekkor egy külön változóban akkumuláltatjuk a szál általi összeget és ezen értékeke összegyűjtését végezzük csak atomi műveletként.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int sum = 0;&lt;br /&gt;
#pragma omp parallel&lt;br /&gt;
{&lt;br /&gt;
  int sum_thr = 0;&lt;br /&gt;
#pragma omp for&lt;br /&gt;
  for (int i=0; i&amp;lt;n; i++)&lt;br /&gt;
    sum_thr += i;&lt;br /&gt;
&lt;br /&gt;
#pragma omp atomic&lt;br /&gt;
  sum += sum_thr;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Ciklusok párhuzamosítása====&lt;br /&gt;
Ciklus párhuzamosítására a következő mintát szokás használni.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel&lt;br /&gt;
{&lt;br /&gt;
  // Az itt definiált forrást minden szál esetén végre fog hajtani&lt;br /&gt;
  ...&lt;br /&gt;
#pragma omp for [schedule (&amp;lt;mode&amp;gt;,&amp;lt;chunk&amp;gt;)&lt;br /&gt;
  for (int i = 0; i &amp;lt; n; i++) {&lt;br /&gt;
    // Ez a kódrész lesz párhuzamosítva&lt;br /&gt;
    ...&lt;br /&gt;
  }&lt;br /&gt;
  // ... és ez a köd is minden szál esetén végrehajtódik&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Ciklus kifejtés====&lt;br /&gt;
A technika már egy példa erejéig a vektorizációnál is látható, de ott nem került részletezésre. A megoldás lényege, hogy a kellően nagy ciklust szétdaraboljuk kisebb darabokra, amit már külön feldolgozhatóak, ezzel párhuzamosítva a folyamatokat. Az egy darab kiindulási ciklusból két darab egymásba ágyazott ciklust hozunk létre, ami ugyanazon a feladattéren dolgozik. &lt;br /&gt;
&lt;br /&gt;
Eredeti állapot:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++) {...}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Átírt változat:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int STRIP=1024;&lt;br /&gt;
for (int ii = 0; ii &amp;lt; n; ii += STRIP)&lt;br /&gt;
 for (int i = ii; i &amp;lt; ii+STRIP; i++) {...}&lt;br /&gt;
&lt;br /&gt;
// ha még van maradék elem, akkor azokon is végrehajtjuk a számításokat&lt;br /&gt;
if (n%STRIP != 0) {&lt;br /&gt;
  for (i=n-(n%STRIP)+1; i&amp;lt;n; i++) {...}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A darabok mérete (STRIP értéke) az adott rendszer és feladat függvényében változhat, tehát ez egyfajta &amp;quot;tuning&amp;quot; paraméter.&lt;br /&gt;
&lt;br /&gt;
====Automatikus ciklusösszevonás====&lt;br /&gt;
Az OpenMP támogatást nyújt több egymásba ágyazott ciklus &amp;quot;összevonására&amp;quot;. Ehhez csak a '''collaps''' fordítási direktívát és az összevonandó ciklusok számát kell megadni. &amp;lt;br /&amp;gt;&lt;br /&gt;
FONTOS: ekkor az automatikus vektorizáció kikapcsolásra kerül!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for collaps(2)&lt;br /&gt;
for (int i=0; i&amp;lt;n; i++)&lt;br /&gt;
  for( int j=0; j&amp;lt;m; j++){...}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A fneti példában a 2db for ciklis kerül párhuzamosításra úgy, hogy a fordítás során 1db ciklus generálódik belőle, ami a következő paraméterezéssel ekvivalens:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for&lt;br /&gt;
for (int c=0; c &amp;lt; n*m; c++) {&lt;br /&gt;
  int i = c/n;&lt;br /&gt;
  int j = c%n;&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
[[Kategória: HPC]]&lt;/div&gt;</summary>
		<author><name>Szigeti(AT)niif.hu</name></author>	</entry>

	<entry>
		<id>https://wiki.niif.hu/index.php?title=Intel_Xeon_Phi&amp;diff=4138</id>
		<title>Intel Xeon Phi</title>
		<link rel="alternate" type="text/html" href="https://wiki.niif.hu/index.php?title=Intel_Xeon_Phi&amp;diff=4138"/>
				<updated>2017-05-09T06:08:23Z</updated>
		
		<summary type="html">&lt;p&gt;Szigeti(AT)niif.hu: /* automatikus kódátvitel */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Az oldalon található információk segítséget nyújtanak a HPC felhasználóinknak, hogy alkalmazásaikat miként portolhatják valamint optimalizálhatják a hatékonyabb futást szem előtt tartva Intel Xeon Phi koprocesszor kártyákon.&lt;br /&gt;
&lt;br /&gt;
{{INFO|Egy 10 alkalmas, angol nyelvű Xeon Phi programozás tutoriál videóanyaggal és példaprogrammal elérhető a [[http://colfaxresearch.com/how-16-04/ Colfax oldalán]]}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Hardver bemutatása==&lt;br /&gt;
Intel® Xeon Phi™ Coprocessor 7120P&lt;br /&gt;
&amp;lt;gallery&amp;gt;&lt;br /&gt;
Fájl:Xeon-phi-family-rwd.png.rendition.intel.web.720.405.png|&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-  align=&amp;quot;center&amp;quot;&lt;br /&gt;
! CPU&lt;br /&gt;
! threads/Core&lt;br /&gt;
!max threads&lt;br /&gt;
! CPU Freq&lt;br /&gt;
! Memória&lt;br /&gt;
! L1 cache&lt;br /&gt;
! L2 cache&lt;br /&gt;
! Interfész&lt;br /&gt;
! Ptot&lt;br /&gt;
! Számítási kapacitás&lt;br /&gt;
|-  align=&amp;quot;center&amp;quot;&lt;br /&gt;
| 61&lt;br /&gt;
| 4 Hardver&lt;br /&gt;
| 244&lt;br /&gt;
| 1238 GHz&lt;br /&gt;
| 16GB GDDR5&lt;br /&gt;
| 32KB/core&lt;br /&gt;
| 512KB/core&lt;br /&gt;
| PCIe&lt;br /&gt;
| 300W&lt;br /&gt;
| 1,2 TFlop/s dupla pontosság&lt;br /&gt;
|-  align=&amp;quot;center&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
| OpenMP-nél csak '''240'''&lt;br /&gt;
|&lt;br /&gt;
| ~100 cycles&lt;br /&gt;
~174 GB/s&lt;br /&gt;
| ~3 cycles&lt;br /&gt;
| ~10 cycles&lt;br /&gt;
| ~7 GB/s&lt;br /&gt;
~1 microsecond&lt;br /&gt;
|&lt;br /&gt;
| 2,4 TFlop/s egyszeres pontosság&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Az Intel® 1. generációs Xeon Phi™ koprocesszorában található 61 processzor ugyanazt a 16GB memóriát éri el, de semmilyen módon se fér hozzá közvetlenül a hoszt gép fizikai memóriájához.&amp;lt;br /&amp;gt;&lt;br /&gt;
[[Budapest2_klaszter|Budapest2]] és [[HPC#Szupersz.C3.A1m.C3.ADt.C3.B3g.C3.A9peink_.C3.B6sszehasonl.C3.ADt.C3.A1sa|Debrecen3-Phi]] gépeinkben számítási nodonként 2-2 Phi™ koprocesszor található. &amp;lt;br/&amp;gt;&lt;br /&gt;
A kártyákon speciális célú Linux kernel fut melyre a belépés limitáltan csak a rendszergazdák számára lehetséges SSH-n keresztül a hoszt gépről. Természetesen a kártyára belépve olyan érzésünk lehet, mintha egy sok processzoros gépen dolgoznánk, ami rendelkezik rengeteg memóriával. Ez így is van, de a hatékonyabb kihasználás érdekében az SSH-n keresztüli elérés nem lehetséges, csak a Slurm segítségével valamint a programozási nyelvek által támogatott csatornákon keresztül. Ennek részletezése lentebb megtalálható.&lt;br /&gt;
&lt;br /&gt;
==Használati módok==&lt;br /&gt;
A koprocesszor használatai módjait két csoportba lehet sorolni. A '''natív''' és az '''offload''' használatot.  &amp;lt;br/&amp;gt;&lt;br /&gt;
Ezen használati módokról a lenti fejezetekben több részletet is megtudhat. Az itt megfogalmazott &amp;quot;támogatás&amp;quot; csak a mi infrastruktúránkra érvényes! &amp;lt;br /&amp;gt;&lt;br /&gt;
A bemutatott példák ''c++'' nyelven vannak, de természetesen ''c'' és ''fortran'' esetén is megtalálható az ekvivalens módszer.&lt;br /&gt;
&lt;br /&gt;
===a) nem támogatott mód===&lt;br /&gt;
====közvetlen natív használat====&lt;br /&gt;
A natív mód arra szolgál, hogy a MIC kártyára  lefordított alkalmazásunkat felmásoljuk a kártyára, majd belépünk SSH-n a kártyára és azon futtatjuk a bináris kódunkat. &amp;lt;br /&amp;gt;&lt;br /&gt;
Biztonsági okokból a közvetlen natív használat az NIIF HPC infrastruktúrán nem elérhető. &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===b) támogatott, de nem preferált használat===&lt;br /&gt;
====automatikus kódátvitel====&lt;br /&gt;
Ebben az esetben nem kell átmásolnunk a bináris alkalmazásunkat a kártyára sőt még csak be se kell lépnünk, mert ezt a két lépést egy beépített tool végzi el. &amp;lt;br/&amp;gt;&lt;br /&gt;
A tool eléréséhez szükséges parancs neve: '''micnativeloadex'''&lt;br /&gt;
&lt;br /&gt;
Ebben az esetben a workflow a következőképpen módosul:&lt;br /&gt;
# Alkalmazás megírása. például [[Média:hello.cc|''hello.cc'']]&lt;br /&gt;
# Alkalmazás fordítása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;icpc -o hello-MIC -mmic hello.cc&amp;lt;/pre&amp;gt;&lt;br /&gt;
# A tool futásához szükséges library-k megadása &amp;lt;br&amp;gt; &amp;lt;pre&amp;gt;export SINK_LD_LIBRARY_PATH=/opt/intel/composerxe/compiler/lib/mic&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Alkalmazás futtatása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;micnativeloadex hello-MIC&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| BUDAPEST2[cn10] phi (0)$ export SINK_LD_LIBRARY_PATH=/opt/intel/composerxe/compiler/lib/mic &amp;lt;br/&amp;gt;BUDAPEST2[cn10] phi (0)$ micnativeloadex hello-MIC &amp;lt;br/&amp;gt;Hello world! I have 244 logical cores.&amp;lt;br/&amp;gt;BUDAPEST2[cn10] phi (0)$&lt;br /&gt;
|}&lt;br /&gt;
Intel MKL használat esetén a SINK_LD_LIBRARY_PATH természetesen kiegészítendő az MKL könyvtárak elérésével.&lt;br /&gt;
&amp;lt;pre&amp;gt;intelhome=/opt/intel/compilers_and_libraries_2016.1.150/linuxexport&lt;br /&gt;
export SINK_LD_LIBRARY_PATH=$intelhome/compiler/lib/mic:$intelhome/mkl/lib/mic&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===c) támogatott módok===&lt;br /&gt;
====Offload====&lt;br /&gt;
Az Offload modell esetén szükséges a forráskód módosítása, mégpedig úgy, hogy azon kódrészletet, amit a koprocesszoron szeretnénk futtatni, ki kell egészíteni egy fordítási direktívával. Az alkalmazás fordításakor elegendő csak a CPU-ra való binárist készíteni mivel a modell végzi a megfelelő forrás részlet mozgatását és futtatását.&amp;lt;br /&amp;gt;&lt;br /&gt;
[[Fájl:offload.png|Offload modell]] &amp;lt;br /&amp;gt;&lt;br /&gt;
Példánkban jól látható, hogy a második kiíratás már a koprocesszoron fut.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
int main(int argc, char * argv[]) {&lt;br /&gt;
  printf(&amp;quot;Hello world from host!\n&amp;quot;);&lt;br /&gt;
#pragma offload target(mic)&lt;br /&gt;
  {&lt;br /&gt;
    printf(&amp;quot;Hello world from coprocessor!\n&amp;quot;); fflush(stdout);&lt;br /&gt;
  }&lt;br /&gt;
  printf(&amp;quot;Bye\n&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ebben a példában az operációs rendszer választja ki, hogy melyik Phi kártyán fusson le a kódrészlet. Amennyiben mi szeretnénk explicite megmondani, hogy melyik kártyán fusson, akkor a fordítási direktívában a mic kulcsszó után kell tenni egy &amp;quot;:&amp;quot;-t és a kártya sorszámát [mi esetünkben 0 vagy 1]. Ekkor így nézne ki a direktíva: ''#pragma offload target(mic:1)''&lt;br /&gt;
&lt;br /&gt;
Amennyiben az offload szekcióban függvény hívás is szerepel, akkor szükségeltetik az függvény futásához szükséges változók adatterületének átmásolása a Phi kártyára, majd a lefutás után visszamásolni a hoszt gépre. Ezen másolást az ''offload_attribute'' ''push'' és ''pop'' paraméterei segítségével határozhatjuk meg. &amp;lt;br /&amp;gt;&lt;br /&gt;
A következő mátrix-vektor szorzás példánál az A mátrix globális változó, míg a b és c vektorok a függvény paraméterei.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//A változók felmásolásra kerülnek a Xeon Phi-re&lt;br /&gt;
#pragma offload_attribute(push, target(mic))&lt;br /&gt;
  double* A;&lt;br /&gt;
  void MatrixVector(double *b, double *c, double m, double n) {&lt;br /&gt;
    ...&lt;br /&gt;
    c[] = A[..]*b[]&lt;br /&gt;
  }&lt;br /&gt;
// felmásolt objektumok visszamásolása&lt;br /&gt;
#pragma offload_attribute(pop)&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
  ...&lt;br /&gt;
#pragma offload target(mic) in(A[0:n*m])&lt;br /&gt;
// így is meg lehetne adni: #pragma offload target(mic:0) in(A : length(n*m))	        &lt;br /&gt;
  {&lt;br /&gt;
    MatrixVector(b, c, m, n);&lt;br /&gt;
  }&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ekkor minden memória területet 2x kell másolni, egyszer fel a kártyára, majd másodszor vissza a hoszt gépre, pedig elegendő lenne csak az eredmény vektort visszamásolni. Ezen optimalizálással egy későbbi fejezetben foglalkozunk. &amp;lt;p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Futás során kaphatunk részletes riportot is, hogy pontosan milyen adatmozgatások történnek az alkalmazás futása során. Ehhez nincs szükség másra csak az '''OFFLOAD_REPORT''' környezeti változó megfelelő beállítása. A beállított értékek Automatic Offload esetén 1, 2, míg Compiler Assisted Offload esetén 1, 2 és 3 lehetnek, attól függően mennyire részletes riportot szeretnénk kapni. &amp;lt;br /&amp;gt;&lt;br /&gt;
beállítás:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export OFFLOAD_REPORT=3&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
példa kimenet a hello alkalmazásra:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Hello world from host!&lt;br /&gt;
[Offload] [MIC 0] [File]                    hello_offload.cc&lt;br /&gt;
[Offload] [MIC 0] [Line]                    5&lt;br /&gt;
[Offload] [MIC 0] [Tag]                     Tag 0&lt;br /&gt;
Hello world from coprocessor!&lt;br /&gt;
[Offload] [HOST]  [Tag 0] [CPU Time]        0.554653(seconds)&lt;br /&gt;
[Offload] [MIC 0] [Tag 0] [CPU-&amp;gt;MIC Data]   0 (bytes)&lt;br /&gt;
[Offload] [MIC 0] [Tag 0] [MIC Time]        0.000140(seconds)&lt;br /&gt;
[Offload] [MIC 0] [Tag 0] [MIC-&amp;gt;CPU Data]   0 (bytes)&lt;br /&gt;
Bye&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Offload + OpenMP====&lt;br /&gt;
Amennyiben nem csak 1 processzort szeretnénk használni a Phi kártyánkon és az alkalmazásunkban használt számítás megengedi a párhuzamosítást, akkor az OpenMP segítségével párhuzamosíthatjuk a megfelelő számítást. &amp;lt;br /&amp;gt;&lt;br /&gt;
[[Fájl:Offload+openmp.png|Offload modell és OpenMP]] &amp;lt;br /&amp;gt;&lt;br /&gt;
Példánk maradjon ugyanúgy a mátrix-vektor szorzás. Amennyiben a teljes kártya adta kapacitást ki szeretnénk használni, akkor az OMP threadek számát 240-re kell beállítani. Azért nem 244-re, mivel 1 CPU-t dedikáltan csak a kommunikációra használ a rendszer és 1 CPU 4 hardver threaddel rendelkezik.&lt;br /&gt;
Ekkor a példánk így változik:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;omp.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#pragma offload_attribute(push, target(mic))&lt;br /&gt;
void multiply(int n, int m, double* A, double* b, double* c){&lt;br /&gt;
  const int nCPUs = omp_get_max_threads();   //240&lt;br /&gt;
  omp_set_num_threads(nCPUs);&lt;br /&gt;
#pragma omp parallel for schedule(dynamic, 1)&lt;br /&gt;
  for ( int i = 0 ; i &amp;lt; m ; i++)&lt;br /&gt;
     for ( int j = 0 ; j &amp;lt; n ; j++)&lt;br /&gt;
       c[i] += A[i*n+j] * b[j];&lt;br /&gt;
}&lt;br /&gt;
#pragma offload_attribute(pop)&lt;br /&gt;
&lt;br /&gt;
int main(){&lt;br /&gt;
  double * A = (double*) malloc(sizeof(double)*n*m);&lt;br /&gt;
  ...&lt;br /&gt;
#pragma offload target(mic) in(A:length(n*m))&lt;br /&gt;
     multiply(n, m, A, b, c);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Az alkalmazás fordításánál a fordítónak meg kell adni, hogy használhja az openmp library-ket is. Ezt a '''-qopenmp''' kapcsolóval tudjuk elérni.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
icpc -o matrix-CPU -qopenmp matrix.cc&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====MPI + Offload + OpenMP====&lt;br /&gt;
Abban az esetben, ha 1 számítási node nem elég, MPI segítségével össze tudunk kötni több nodet, heterogén rendszert alkotva, és azokon alkalmazható a fenti Offload + OpenMP modell. &amp;lt;br /&amp;gt;&lt;br /&gt;
[[Fájl:Mpi+offload+openmp.png|MPI elosztás valamint Offload modell és OpenMP]] &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Phi használat slurm-mel==&lt;br /&gt;
Slurm esetén a mic opciót kell megadni a gres-knél. Amennyiben egynél több kártyára van szükség, akkor azt a ''mic:2''-vel lehetséges megadni. Ekkor két kártyát foglal le az ütemező a futtatandó feladatnak. &amp;lt;br /&amp;gt;&lt;br /&gt;
Egy lehetséges feladat, amit srun-nal lehet a slurmben beküldeni. debrecen3 esetén a ''prod-phi'' particiót kell használni&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ cat slurm_job_openmp_phi&lt;br /&gt;
&lt;br /&gt;
#!/bin/bash&lt;br /&gt;
#SBATCH -A &amp;lt;PROJECT NEVE&amp;gt;&lt;br /&gt;
#SBATCH --job-name=&amp;lt;JOB NEVE&amp;gt;&lt;br /&gt;
#SBATCH --gres mic:2&lt;br /&gt;
#SBATCH --time=1:00:00&lt;br /&gt;
#SBATCH --partition=prod&lt;br /&gt;
#SBATCH -o slurm-%A.out&lt;br /&gt;
export OMP_NUM_THREADS=2&lt;br /&gt;
./runme_openmp_2phi&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Ekkor a feladat beküldése a következő paranccsal lehetséges.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
srun ./slurm_job_openmp_phi&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Alkalmazások optimalizálása a kártyára==&lt;br /&gt;
Ebben a fejezetben optimalizálási lehetőségeket mutatunk be, melyek segítségül szolgálhatnak az egyes alkalmazások hatékonyabb futása érdekében ezzel jobban kihasználni a rendelkezésre álló erőforrások adta számítási kapacitás.&lt;br /&gt;
&lt;br /&gt;
===Környezeti változók===&lt;br /&gt;
Lehetséges csak a Phi kártyákon használt környezeti változók beállítása, amit az alkalmazásunk használni is tud. &amp;lt;br /&amp;gt;&lt;br /&gt;
Példánkban az OpenMP esetén használható szálak számát állíthatjuk be:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export MIC_ENV_PREFIX=XEONPHI&lt;br /&gt;
export OMP_NUM_THREADS=12&lt;br /&gt;
export XEONPHI_OMP_NUM_THREADS=240&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
A beállított prefix tetszőleges karaktersorozat lehet, a lényeg, hogy ezt a beállított prefixet használjuk a környezeti változók esetén is!&lt;br /&gt;
 &lt;br /&gt;
===Hosszredukció===&lt;br /&gt;
Többnyire a fordítók, ha nem mondjuk meg explicite, a dupla pontosságú változatot választják a lehetséges változó- és függvény típusok közül. Amennyiben a számításunk nem igényli a dupla pontosságot, akkor használjuk a forrásban mindenhol az adott típus vagy függvény egyszeres pontosságú változatát, mellyel jelentős teljesítmény növekedést tudunk elérni. Továbbá az egyes műveletek futási ideje nem egyforma, így a műveletet helyettesíthetjük más művelettel, ami gyorsabb, akkor ismét javíthatunk a hatékonyságon. &amp;lt;br /&amp;gt;&lt;br /&gt;
Íme pár példa, amit alkalmazni lehet:&lt;br /&gt;
* &amp;quot;''/''&amp;quot; művelet helyett &amp;quot;''*''&amp;quot; -t használjunk, mert azt gyorsabban végre tudja hajtani a processzor&lt;br /&gt;
* ''pow'' =&amp;gt; ''1.0 / sqrt()''    , tetszőleges hatványozás, pl.: 3/2-ik hatvány&lt;br /&gt;
* hardver áltatl támogatott műveletek:&lt;br /&gt;
** ''exp'' =&amp;gt; ''exp2''&lt;br /&gt;
** ''log'' =&amp;gt; ''log2''&lt;br /&gt;
* dupla pontosság -&amp;gt; egyszeres pontosság lebegőpontos számok és műveletek esetén &lt;br /&gt;
** ''1.0'' =&amp;gt; ''1.0f''&lt;br /&gt;
** ''pow'' =&amp;gt; ''powf''&lt;br /&gt;
&lt;br /&gt;
===Vektorizáció===&lt;br /&gt;
A vektorizáció azon fordítási időben eldönthető optimalizációs faktor, amely nagy mértékben függ attól a hardver felépítésétől, amire éppen a fordítás történni fog. Legfontosabb befolyásoló tényezők a processzor cache méretei illetve a vektor műveletekért felelős aritmetikai egységének jellemzői mivel ezeket használja fel a fordító az optimalizálási folyamata során. &amp;lt;br /&amp;gt;&lt;br /&gt;
Az automatikus vektorizáció minimum -O2 szinttől érvényes, de mivel általában ez a szint az alapértelmezett, ezért minden fordításnál alkalmazásra kerül. &amp;lt;br /&amp;gt;&lt;br /&gt;
A fordító programnak a következő kapcsolókat lehet megadni, melyek segítenek a minél jobb vektorizációban:&lt;br /&gt;
* ''-qopt-report=&amp;lt;szám&amp;gt;'' ; az optimalizációs jelentés részletességét lehet megadni 0-tól 5-ig, ahol az 5-s szint a legrészletesebbet jelenti&lt;br /&gt;
* ''-qopt-report-phase:vec'' ; a vektorizációról szeretnék jelentést kapni&lt;br /&gt;
* ''-opt-report-file=&amp;lt;fájlnév&amp;gt;'' ; megadható a riportfájl neve&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
icpc autovec.cc -mmic -qopt-report=5 -opt-report-file=vectorization.oMIC.optrpt -qopt-report-phase:vec&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====SIMD====&lt;br /&gt;
Az adatok hatékonyabb feldolgozására az egyik megvalósítás a SIMD (Single Instruction Multiple Data), amikor is a különálló adatokat úgy kezeli, mintha az vektorok volnának és azon végzi el a vektor-műveletet. Ezzel egy a hardver által támogatott műveletet végzünk egy lépésben egy nagyobb adatmennyiségen, ezzel párhuzamosítva a feldolgozási folyamatot. Ezzel a módszerrel nagyon jó adat szintű párhuzamosítást lehet elérni.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Fájl:Simd.png|SIMD]]&lt;br /&gt;
&lt;br /&gt;
Főbb támogatott műveletek:&lt;br /&gt;
* aritmetikai műveletek, bináris operátorok&lt;br /&gt;
* összehasonlítások&lt;br /&gt;
* konverziók és típus kasztolások&lt;br /&gt;
* bit-műveletek: NOT, AND, OR, XOR, XAND&lt;br /&gt;
&lt;br /&gt;
Használata: '''simd''' fordítási direktívával lehetséges &amp;lt;br /&amp;gt;&lt;br /&gt;
Például:&lt;br /&gt;
* egyszerű vektorizáció&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma simd&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
  A[i] += B[i];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* adat párhuzamosítás OpenMP segítségével&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for simd&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
  DoSomeWork(A[i]);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====AoS -&amp;gt; SoA====&lt;br /&gt;
A kiindulás probléma az, hogy a struktúrákat tartalmazó vektort nem lehet jól vektorizálni, mivel a cache-ben tárolt adatok nagy többsége feleslegesen van bent, mivel azon adatokat csak egy későbbi ciklus futás során fogja az alkalmazás feldolgozni. Tehát ezen adatok jelenleg feleslegesen foglalják a cache területet, ahelyett, hogy hasznos adatot tárolnánk ott.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
struct MyStructure {&lt;br /&gt;
  float x, y, z;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void My_Function(cons int nItem, MyStructure* const Items, const float delta) {&lt;br /&gt;
  for (int i = 0; i &amp;lt; nItem; i++) {&lt;br /&gt;
    ...&lt;br /&gt;
    for (int j = 0; j &amp;lt; nItem; j++) {&lt;br /&gt;
      const float dx = Items[j].x - Items[i].x;&lt;br /&gt;
      const float dy = Items[j].y - Items[i].y;&lt;br /&gt;
      const float dz = Items[j].z - Items[i].z;&lt;br /&gt;
      ...  &lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  ... &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A vektorban a struktúrák tárolása helyett minden elemnek egy dedikált vektort allokálunk és így könnyen tudja a fordító vektorizálni azt. Természetesen segítségként a vektor feldolgozását &amp;quot;tömbösítettük&amp;quot; a tileSize méret megadásával illetve a ''unroll'' fordítási direktíva segítségével.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
struct MyStructureSet {&lt;br /&gt;
  float *x, *y, *z;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void My_Function(cons int nItem, MyStructure* const Items, const float delta) {&lt;br /&gt;
  const int tileSize = 16;&lt;br /&gt;
#pragma omp parallel for schedule(guided)&lt;br /&gt;
  for (int ii = 0; ii &amp;lt; nItem; ii+= tileSize) {&lt;br /&gt;
    ...&lt;br /&gt;
#pragma unroll(tileSize)&lt;br /&gt;
    for (int j = 0; j &amp;lt; nItem; j++) {&lt;br /&gt;
#pragma unroll(tileSize)&lt;br /&gt;
      for (int i = ii; i &amp;lt; ii + tileSize; i++) {&lt;br /&gt;
        const float dx = Items.x[j] - Items.x[i];&lt;br /&gt;
        const float dy = Items.y[j] - Items.y[i];&lt;br /&gt;
        const float dz = Items.z[j] - Items.z[i];&lt;br /&gt;
        ...&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  ... &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Hatékony hossz választás====&lt;br /&gt;
Olyan esetben jelent előnyt, ha például az n*n-es mátrix esetén n nem osztható 16-val, mert akkor a vektorizáció nem teljesen hajtható végre. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// n%16 != 0&lt;br /&gt;
float* A = _mm_malloc(sizeof(float)*n*n, 64);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Ilyen esetben mindig marad egy kis vektor részlet, amire elemenként kell elvégezni a műveletet.&amp;lt;br /&amp;gt;&lt;br /&gt;
A megoldást az jelenti, hogy a memória allokálást kiterjesztjük és nagyobb memória területet foglalunk a változónak, így már alkalmazható lesz a vektorizáció, mivel a maradvány területen is el tudjuk végezni a műveletet egy lépésben.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int lda=n;&lt;br /&gt;
if (n % 16 != 0) lda += (16 - n%16); // most lda%16==0&lt;br /&gt;
float* A = _mm_malloc(sizeof(float)*n*lda, 64);&lt;br /&gt;
&lt;br /&gt;
// használat&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
  for (int j = 0; j &amp;lt; n; j++)&lt;br /&gt;
    A[i*lda + j] = ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====&amp;quot;Héj ciklusok&amp;quot; kiküszöbölése====&lt;br /&gt;
Ciklusok esetén lehetséges, hogy a vektorizációt nem teljes mértékben ismeri fel a fordító program. &lt;br /&gt;
Ekkor segíthetünk a fordítónak egy fordítási direktívával ('''#pragma vector aligned'''), ha teljes mértékben biztosak vagyunk, hogy a ciklusunk vektorizálható!&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma vector aligned&lt;br /&gt;
  for (int j = 0; j &amp;lt; n; j++)&lt;br /&gt;
    A[i*lda + j] -= ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Ettől jelentős futás idejű javulást érhetünk el, mivel ezzel a #pragma-val kikapcsoltuk a futás idejű ellenőrzést, de előfordulhatnak olyan esetek, főleg amikor nem jól mértük fel a ciklusunkat és nem sikerült a vektorizálás, hogy ''Segmentation fault'' hibára fogunk futni.&lt;br /&gt;
&lt;br /&gt;
====Regularizáció====&lt;br /&gt;
Van egy ciklusunk, ami egy vektor elemeit módosítja. A processzor az adatokat &amp;quot;csokorba szedve&amp;quot; emeli be a belső kis méretű cache-be. Egyszerre természetesen több vektor elem is bekerül a a cache-be ezzel gyorsítva a feldolgozást. Ezen vektor iterációk valahogyan ráilleszkedik a vektorra.&lt;br /&gt;
&lt;br /&gt;
[[Fájl:regularization.png|Vektor regularizáció]]&lt;br /&gt;
&lt;br /&gt;
Abban az esetben, amikor ez az illesztés nem teljesen tökéletes (lsd. piros elemek), keletkeznem héj ciklusok, melyeket a regularizációval is eliminálni lehet. &amp;lt;br /&amp;gt;&lt;br /&gt;
A feladat eredetileg valahogyan így nézhet ki:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
for (int i = 0; i&amp;lt;n; i++)&lt;br /&gt;
  A[i]=...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ez a következő átalakítással már alkalmas vektorizációra és nem lesz &amp;quot;peel loop&amp;quot; probléma.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
for (int b=0; b&amp;lt;n; b++) {&lt;br /&gt;
  const int jStart = b- b%16; // mindig osztható 16-val&lt;br /&gt;
  ...&lt;br /&gt;
#pragma simd&lt;br /&gt;
  for (int j = jStart; j&amp;lt;n; j++)&lt;br /&gt;
    A[j]=...&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Párhuzamosítás===&lt;br /&gt;
Több féle mód létezik a párhuzamosításra. Korábban említettük a SIMD technikát, ami adatpárhuzamosításra való 1 processzoron belül, de létezik még processzorok közti párhuzamosítás, amit az OpenMP segítségével tudunk hatékonyan megvalósítani. Amennyiben gépek közti párhuzamosításra, üzenek küldésre van szükség, akkor az MPI-t kell segítségül hívni. Általános megoldás nem létezik minden feladatra, mert MPI is alkalmas lehet gépen belüli párhuzamosításra, nem csak az OpenMP. Amit mérlegelni kell, hogy mi az amire az alkalmazásnak szükséges van illetve mekkora kommunikációs overhead keletkezik, ha lecseréljük az OpenMP-t MPI-ra. Általában az egymástól független számításokat OpenMP-vel, míg a kommunikációt igénylő feladatokra MPI-t szoktak használni.&lt;br /&gt;
&lt;br /&gt;
====Szálak párhuzamosítása====&lt;br /&gt;
Az OpenMP-nek környezeti változók segítségével megadható a párhuzamosan futtatható szálak száma. Ezt a következő paranccsal tudjuk megtenni, ahol 5-re állítjuk.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export OMP_NUM_THREADS=5&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Forrásban a párhuzamos szálakat a ''#pragma omp parallel'' segítségével adhatjuk meg.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for              	//külső for szálak párhuzamosítása&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
#pragma simd				// vektorizáció a belső cikluson&lt;br /&gt;
  for (int j = 0; j &amp;lt; m; j++)&lt;br /&gt;
    My_Function(A[i][j]);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A fordítási direktívák össze is vonhatóak, ilyenkor 1 ciklus is elég.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for simd&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
    My_Function(A[i]);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=====Változók láthatósága, megosztása=====&lt;br /&gt;
Változók hatókörét a programozási nyelven kívül pragma-k segítségével is beállíthatjuk, hogy megosztott legyen a változó vagy a szálra nézve saját példány. Amennyiben nem definiáljuk felül, akkor minden változó megosztott. A felül definiálást a private és a shared paraméterekkel lehet megadni az OpenMP-nek.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int A, B, C;&lt;br /&gt;
&lt;br /&gt;
#pragma omp parallel private(A) shared(B)&lt;br /&gt;
{...}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Az itteni példában az B és C megosztott változó a szálak között, míg A-t minden szálnak lemásolja a futás kezdetén.&lt;br /&gt;
&lt;br /&gt;
====Párhuzamos szálak ütemezése====&lt;br /&gt;
OpenMp a szálak ütemezésére három féle módod kínál fel.&lt;br /&gt;
Módok:&lt;br /&gt;
* '''static''' (alapértelmezett): egyenletesen arányban egymás után elosztja a processzeket az indított szálakon, ezért a futási idő tetszőlegesen elhúzódhat, ha egyes processzek lassabban futnak le.&lt;br /&gt;
* '''dynamic''': minden esetben az első szabad szálra kerül a következő processz ütemezése, folyamatos kihasználtságot elérve, de &amp;quot;költséges&amp;quot; a sok-sok ütemezés&lt;br /&gt;
* '''guided''': Az előző két mód ötvözése. Nagyobb procesz számot ütemez az éppen szabad szálakra, melyek számát folyamatosan csökkenti a futás előre haladtával, hogy körülbelül egyszerre fejeződjön be minden szál futása, ezzel csökkentve a dynamic esetén magad ütemezési időráfordítást.&lt;br /&gt;
&lt;br /&gt;
Mindhárom esetben megadható az egyszerre ütemezett processzek száma, amit a ''chunk'' értéke határoz meg. Ha megvan adva a chunk értéke, akkor az minden esetben annak megfelelő processzámot ütemez a szálakra.&lt;br /&gt;
Használata:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel [schedule (&amp;lt;mode&amp;gt;,&amp;lt;chunk&amp;gt;) {...}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Szálak szinkronizációja====&lt;br /&gt;
Szála szinkronizálását többféle módon lehet megvalósítani, de ezen megoldások sajno jelentősen lelassítják a program futását.&lt;br /&gt;
* mutexxel: nagyon lassú, állandóan várnak egymásra a párhuzamos szálak&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++) {&lt;br /&gt;
#pragma omp critical&lt;br /&gt;
  { // kritikus szekció, csak egy szál hajthatja végre&lt;br /&gt;
    total = total + i;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* atomi művelet definiálása: sajnos még ez is lassú, de itt már nem olyan &amp;quot;erős&amp;quot; a szinkronizáció, ezért rendkívül sok limitációja van az itt használható atomi műveleteknek.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++) {&lt;br /&gt;
#pragma omp atomic&lt;br /&gt;
   total += i;&lt;br /&gt;
}	&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=====Párhuzamos redukció=====&lt;br /&gt;
Az itt bemutatott eljárás gyorsabb mint a fenti szinkronizációs megoldás, de sajnos nem alkalmazható vektorokra! A redukciót a ''reduction(operation: scalar)'' formában kell megadni.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int sum = 0;&lt;br /&gt;
#pragma omp parallel for reduction(+: sum)&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++) {&lt;br /&gt;
  sum = sum + i;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Tulajdonképpen a fenti problémakörre a következő minta ad megoldást. Ekkor egy külön változóban akkumuláltatjuk a szál általi összeget és ezen értékeke összegyűjtését végezzük csak atomi műveletként.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int sum = 0;&lt;br /&gt;
#pragma omp parallel&lt;br /&gt;
{&lt;br /&gt;
  int sum_thr = 0;&lt;br /&gt;
#pragma omp for&lt;br /&gt;
  for (int i=0; i&amp;lt;n; i++)&lt;br /&gt;
    sum_thr += i;&lt;br /&gt;
&lt;br /&gt;
#pragma omp atomic&lt;br /&gt;
  sum += sum_thr;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Ciklusok párhuzamosítása====&lt;br /&gt;
Ciklus párhuzamosítására a következő mintát szokás használni.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel&lt;br /&gt;
{&lt;br /&gt;
  // Az itt definiált forrást minden szál esetén végre fog hajtani&lt;br /&gt;
  ...&lt;br /&gt;
#pragma omp for [schedule (&amp;lt;mode&amp;gt;,&amp;lt;chunk&amp;gt;)&lt;br /&gt;
  for (int i = 0; i &amp;lt; n; i++) {&lt;br /&gt;
    // Ez a kódrész lesz párhuzamosítva&lt;br /&gt;
    ...&lt;br /&gt;
  }&lt;br /&gt;
  // ... és ez a köd is minden szál esetén végrehajtódik&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Ciklus kifejtés====&lt;br /&gt;
A technika már egy példa erejéig a vektorizációnál is látható, de ott nem került részletezésre. A megoldás lényege, hogy a kellően nagy ciklust szétdaraboljuk kisebb darabokra, amit már külön feldolgozhatóak, ezzel párhuzamosítva a folyamatokat. Az egy darab kiindulási ciklusból két darab egymásba ágyazott ciklust hozunk létre, ami ugyanazon a feladattéren dolgozik. &lt;br /&gt;
&lt;br /&gt;
Eredeti állapot:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++) {...}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Átírt változat:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int STRIP=1024;&lt;br /&gt;
for (int ii = 0; ii &amp;lt; n; ii += STRIP)&lt;br /&gt;
 for (int i = ii; i &amp;lt; ii+STRIP; i++) {...}&lt;br /&gt;
&lt;br /&gt;
// ha még van maradék elem, akkor azokon is végrehajtjuk a számításokat&lt;br /&gt;
if (n%STRIP != 0) {&lt;br /&gt;
  for (i=n-(n%STRIP)+1; i&amp;lt;n; i++) {...}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A darabok mérete (STRIP értéke) az adott rendszer és feladat függvényében változhat, tehát ez egyfajta &amp;quot;tuning&amp;quot; paraméter.&lt;br /&gt;
&lt;br /&gt;
====Automatikus ciklusösszevonás====&lt;br /&gt;
Az OpenMP támogatást nyújt több egymásba ágyazott ciklus &amp;quot;összevonására&amp;quot;. Ehhez csak a '''collaps''' fordítási direktívát és az összevonandó ciklusok számát kell megadni. &amp;lt;br /&amp;gt;&lt;br /&gt;
FONTOS: ekkor az automatikus vektorizáció kikapcsolásra kerül!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for collaps(2)&lt;br /&gt;
for (int i=0; i&amp;lt;n; i++)&lt;br /&gt;
  for( int j=0; j&amp;lt;m; j++){...}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A fneti példában a 2db for ciklis kerül párhuzamosításra úgy, hogy a fordítás során 1db ciklus generálódik belőle, ami a következő paraméterezéssel ekvivalens:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for&lt;br /&gt;
for (int c=0; c &amp;lt; n*m; c++) {&lt;br /&gt;
  int i = c/n;&lt;br /&gt;
  int j = c%n;&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
[[Kategória: HPC]]&lt;/div&gt;</summary>
		<author><name>Szigeti(AT)niif.hu</name></author>	</entry>

	<entry>
		<id>https://wiki.niif.hu/index.php?title=HPC_software&amp;diff=4057</id>
		<title>HPC software</title>
		<link rel="alternate" type="text/html" href="https://wiki.niif.hu/index.php?title=HPC_software&amp;diff=4057"/>
				<updated>2017-03-13T12:15:18Z</updated>
		
		<summary type="html">&lt;p&gt;Szigeti(AT)niif.hu: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The current version of software on specific machines might be higher than described here. We are encouraging all users to install other tools to be used on our servers if service AUP and license restrictions are respected.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
!Software component&lt;br /&gt;
!Debrecen&lt;br /&gt;
!Pécs&lt;br /&gt;
!Szeged&lt;br /&gt;
!Budapest&lt;br /&gt;
!Budapest2&lt;br /&gt;
!Debrecen2,3 (Leo, Apollo)&lt;br /&gt;
!Miskolc&lt;br /&gt;
|-&lt;br /&gt;
| colspan=8| '''Compiler environment (compiler, debugger, profiler)'''&lt;br /&gt;
|-&lt;br /&gt;
| AMD [http://developer.amd.com/tools-and-sdks/cpu-development/x86-open64-compiler-suite/ compilers ((opencc, openf90, openf95))] || colspan=2|-||colspan=2|Available||colspan=3|-&lt;br /&gt;
|-&lt;br /&gt;
| Math. Library (AMD) [http://developer.amd.com/tools/cpu-development/amd-core-math-library-acml ACML] ||colspan=2|-||colspan=2|4.2.4||colspan=3|-&lt;br /&gt;
|-&lt;br /&gt;
| [http://sources.redhat.com/gdb/ GDB] || colspan=2|7|| colspan=2|7.0.1|| colspan=2|7.2||7.5&lt;br /&gt;
|-&lt;br /&gt;
| [http://gcc.gnu.org/ GNU compilers (gcc, g++, gfortran)]||4.3||4.1.2 || colspan=2|4.1.2, 4.4.4|| colspan=2|4.4.7|| 4.8.5&lt;br /&gt;
|-&lt;br /&gt;
| Gprof|| colspan=2|2.2|| colspan=2|2.17|| colspan=2|2.20|| 2.26.1&lt;br /&gt;
|-&lt;br /&gt;
| Intel [http://software.intel.com/en-us/articles/intel-composer-xe/ compilers (icc, ifort)], [http://software.intel.com/en-us/articles/intel-math-kernel-library-documentation/ MKL],  [http://software.intel.com/en-us/articles/intel-mkl-link-line-advisor/ Linking]  ||colspan=2|12.1.5 (composerxe-2011 Update 11) ||colspan=2|-|| colspan=3|16.0&lt;br /&gt;
|- &lt;br /&gt;
| [http://oprofile.sourceforge.net/ Oprofile] ||  colspan=3|- ||0.9.4||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| colspan=8|'''Libraries supporting parallel development'''&lt;br /&gt;
|- &lt;br /&gt;
| LAM-MPI ||colspan=2|- ||colspan=2|7.1.2||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| MPICH2 ||colspan=2|1.4||colspan=2|1.2||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| [http://openmp.org/ OpenMP]||colspan=2|2.5 (GNU), 3.0 (Intel)||colspan=2|2.5 (GNU), 2.5 (AMD) ||colspan=3|2.5 (GNU), 3.0 (Intel)&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.open-mpi.org/ OpenMPI] ||1.4.2 ||1.3.2 ||colspan=2|1.4.3 || colspan=2|1.8.5||1.6.3&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.csm.ornl.gov/pvm/ PVM] ||colspan=4|3.4.5 ||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| [http://videotorium.hu/hu/recordings/details/12291,Overview_and_usage_of_the_SGI_HPC_softwares_on_the_SGI_ICE_and_SGI_UV_supercomputers SGI Perf. Suite] ||colspan=2|Available||colspan=2|-||colspan=2|-||Available&lt;br /&gt;
|- &lt;br /&gt;
| SGI-MPI (MPT) ||colspan=2|2.04||colspan=2|-||colspan=2|-||2.13&lt;br /&gt;
|- &lt;br /&gt;
| PGI Compiler and Tools ||colspan=5|-|| 15.10 || -&lt;br /&gt;
|- &lt;br /&gt;
| colspan=8|'''Libraries supporting scientific work'''&lt;br /&gt;
|- &lt;br /&gt;
| AmberTool || 16 ||colspan=6|-&lt;br /&gt;
|-&lt;br /&gt;
| Atlas ||colspan=4|3.8.3||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| BLACS ||colspan=4|1.1 (OpenMPI, MPICH2) ||1.1 (OpenMPI Intel)||3.1.3||-&lt;br /&gt;
|- &lt;br /&gt;
| BLAS ||colspan=2|3.2.2 ||colspan=2|3||3.6.1||-||3&lt;br /&gt;
|- &lt;br /&gt;
| Boost||colspan=2|1.36||colspan=2|1.33||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| FFTW ||colspan=2|3.1.2 ||colspan=2|3.2.2||3.3.5 ||3.3.4||3.3.3&lt;br /&gt;
|- &lt;br /&gt;
| Gromacs ||colspan=2|4.5.4 ||colspan=2|4.5.5||colspan=2|-||4.6.2&lt;br /&gt;
|- &lt;br /&gt;
| GUILE ||colspan=2|1.8.5 ||colspan=2|-||colspan=2|-||1.8.8&lt;br /&gt;
|- &lt;br /&gt;
| HDF5 ||colspan=2|1.8.7||colspan=2|-||colspan=2|1.8.14||-&lt;br /&gt;
|- &lt;br /&gt;
| LAMMPS ||colspan=4|-||16.05.14 (Phi)||16.05.14 (GPU, Phi)||-&lt;br /&gt;
|-&lt;br /&gt;
| Lapack ||colspan=2|3.2.2 ||colspan=2|3 ||3.6.1 ||colspan=2|-&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.maplesoft.com/ Maple] ||colspan=2| - ||colspan=2|18||-||2015||-&lt;br /&gt;
|- &lt;br /&gt;
| [https://wiki.niif.hu/index.php?title=NIIF_szuperszámítógépek_használata#Matlab_feladatok Matlab] ||colspan=2|7.13.0 (R2011b)|| colspan=2|-||-||R2015b||-&lt;br /&gt;
|- &lt;br /&gt;
| NetCDF ||4.1.3 ||- ||colspan=2|4.2.1.1||4.4.1||4.3.3.1||-&lt;br /&gt;
|- &lt;br /&gt;
| NumPy ||colspan=2|1.3||colspan=2|1.7.1||1.11.2 ||-||1.7.0&lt;br /&gt;
|- &lt;br /&gt;
| PETSc||colspan=4|-||3.7.4 Intel||colspan=2|-&lt;br /&gt;
|- &lt;br /&gt;
| Python||colspan=4|2.7.5||3.5.2||colspan=2|-&lt;br /&gt;
|- &lt;br /&gt;
| Octave ||colspan=2|3.4||colspan=2|3.0.5 ||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| Qt||colspan=2|3.3.8b, 4.6.2||colspan=2|3.3.6 ||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| R ||2.8.1, 3.2.0, 3.2.5(Intel) || 2.8.1 ||colspan=2|-||colspan=2|-||3.0.1&lt;br /&gt;
|- &lt;br /&gt;
| Scalapack||colspan=4|1.7.5||colspan=2|2.0.2||-&lt;br /&gt;
|- &lt;br /&gt;
| Scilab ||colspan=2|5.3.2||colspan=2|-||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| Scipy||colspan=4|0.12.0||0.18.1 ||colspan=2|-&lt;br /&gt;
|- &lt;br /&gt;
| Sun JRE ||colspan=2|1.6||colspan=2|-||colspan=1|-||1.8||1.7&lt;br /&gt;
|- &lt;br /&gt;
| Gaussian||colspan=3|09_REV_C.01||-||-||colspan=2|09_REV_D.01&lt;br /&gt;
|- &lt;br /&gt;
| colspan=8|'''Middleware'''&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.nordugrid.org/arc/ ARC grid middleware] ||colspan=7|4.1.0 (A-REX, ARIS, clients))&lt;br /&gt;
|- &lt;br /&gt;
| colspan=8|'''Miscellaneous software'''&lt;br /&gt;
|- &lt;br /&gt;
| Emacs ||colspan=2|22.3||colspan=2|21.4||-||23.1.1||22.3.1&lt;br /&gt;
|- &lt;br /&gt;
| GNU Plot ||colspan=2|4.2.3 ||colspan=2|4||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| ImageMagick ||colspan=2|6.4.3 ||colspan=2|6.2.8||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.virtualgl.org/About/TigerVNC Turbo VNC]||colspan=4|1.1||colspan=3|2&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.blender.org/ Blender]||colspan=3|2.49||-||colspan=2|2.49||-&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.virtualgl.org/ VirtualGL]||colspan=3|2.3.2||-||colspan=2|2.4||-&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.paraview.org/ Paraview]||colspan=3|3.14.1||-||colspan=3|3.14.1&lt;br /&gt;
|- &lt;br /&gt;
| ffmpeg||colspan=2|-||colspan=2|0.4.9||colspan=3|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
[[Kategória: HPC]]&lt;/div&gt;</summary>
		<author><name>Szigeti(AT)niif.hu</name></author>	</entry>

	<entry>
		<id>https://wiki.niif.hu/index.php?title=NIIF_HPC_(English)&amp;diff=4039</id>
		<title>NIIF HPC (English)</title>
		<link rel="alternate" type="text/html" href="https://wiki.niif.hu/index.php?title=NIIF_HPC_(English)&amp;diff=4039"/>
				<updated>2017-02-16T11:55:31Z</updated>
		
		<summary type="html">&lt;p&gt;Szigeti(AT)niif.hu: /* Supercomputing */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[HPC|Magyar változat]]&lt;br /&gt;
== Supercomputing ==&lt;br /&gt;
NIIF Institute has been operating supercomputer since 2001. The service provides scientific computation and data storage facilities. The components of the supercomputing infrastructure found in five locations:&lt;br /&gt;
 &lt;br /&gt;
* NIIF centre Budapest&lt;br /&gt;
* University of Debrecen&lt;br /&gt;
* University of Pécs&lt;br /&gt;
* University of Szeged&lt;br /&gt;
* University of Miskolc&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Fájl:Orszagos-attekinto_hpc.png|center|700px|Az NIIF szuperszámítógépei térképen]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Various supercomputer architectures are available in different locations, because we need to support different types of scientific computing tasks. In order to support all kinds of scientific computational needs different computer architectures are available. We have cluster based supercomputers in three locations and two ccNUMA machine. The supercomputers are operated by NIIF Institute staff. The latest development tools and scientific applications are available for the users. The documentation of the supercomputers can be found here.&lt;br /&gt;
 &lt;br /&gt;
Any person or research group who has a contractual relationship with the NIIF Institute can use the service. Usage of the supercomputers is free for authorised users. New users should request a HPC project here. After the project is accepted invitations can be sent out for group members by the principal investigator. Invitations for the HPC portal can be managed [https://portal.hpc.niif.hu/ here].&lt;br /&gt;
&lt;br /&gt;
[http://hpc.niif.hu/index_en.php NIIF HPC website]&lt;br /&gt;
&lt;br /&gt;
==== NIIF HPC resources ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot; &lt;br /&gt;
|- &lt;br /&gt;
| Location&lt;br /&gt;
| Budapest&lt;br /&gt;
| [[Budapest2_klaszter|Budapest2]]&lt;br /&gt;
| Szeged&lt;br /&gt;
| Debrecen&lt;br /&gt;
| [[Debrecen2_GPU_klaszter|Debrecen2-GPU (LEO)]] &lt;br /&gt;
| [[Debrecen2_Phi_klaszter|Debrecen3-Phi (Apollo) (test phase)]]&lt;br /&gt;
| Pécs&lt;br /&gt;
| [[Miskolc_UV_2000|Miskolc]] (test phase)&lt;br /&gt;
|- &lt;br /&gt;
| Type&lt;br /&gt;
| HP CP4000SL&lt;br /&gt;
| HP SL250s &lt;br /&gt;
| HP CP4000BL&lt;br /&gt;
| SGI ICE8400EX&lt;br /&gt;
| HP SL250s&lt;br /&gt;
&lt;br /&gt;
| HP Apollo 8000&lt;br /&gt;
| SGI UV 1000&lt;br /&gt;
&lt;br /&gt;
| SGI UV 2000&lt;br /&gt;
|- &lt;br /&gt;
| CPU / node &lt;br /&gt;
| 2&lt;br /&gt;
| 2 &lt;br /&gt;
| 4 &lt;br /&gt;
| 2&lt;br /&gt;
| 2&lt;br /&gt;
&lt;br /&gt;
| 2 &lt;br /&gt;
| 192&lt;br /&gt;
&lt;br /&gt;
| 44 &lt;br /&gt;
|- &lt;br /&gt;
| Core / CPU &lt;br /&gt;
| 12&lt;br /&gt;
| 10&lt;br /&gt;
| 12&lt;br /&gt;
| 6&lt;br /&gt;
| 8&lt;br /&gt;
| 12&lt;br /&gt;
| 6&lt;br /&gt;
| 8&lt;br /&gt;
|- &lt;br /&gt;
| Memory / node &lt;br /&gt;
| 66 GB&lt;br /&gt;
| 63 GB &lt;br /&gt;
| 132 GB&lt;br /&gt;
| 47 GB&lt;br /&gt;
| 125 GB&lt;br /&gt;
| 125 GB&lt;br /&gt;
| 6 TB&lt;br /&gt;
| 1.4 TB&lt;br /&gt;
|- &lt;br /&gt;
| Memory / core &lt;br /&gt;
| 2.6 GB&lt;br /&gt;
| 3 GB &lt;br /&gt;
| 2.6 GB&lt;br /&gt;
| 2.6 GB&lt;br /&gt;
| 7.5 GB&lt;br /&gt;
| 7.5 GB&lt;br /&gt;
| 5 GB&lt;br /&gt;
| 4 GB&lt;br /&gt;
|- &lt;br /&gt;
| CPU &lt;br /&gt;
| AMD Opteron 6174 @ 2.2GHz&lt;br /&gt;
| Intel Xeon E5-2680 v2 @ 2.80GHz    &lt;br /&gt;
| AMD Opteron 6174 @ 2.2GHz &lt;br /&gt;
| Intel Xeon X5680 @ 3.33 GHz&lt;br /&gt;
| Intel Xeon E5-2650 v2 @ 2.60GHz &lt;br /&gt;
&lt;br /&gt;
| Intel Xeon E5-2670 v3 @ 2.30GHz&lt;br /&gt;
| Intel Xeon X7542 @ 2.66 GHz&lt;br /&gt;
| Intel Xeon E5-4627 v2 @ 3.33 GHz&lt;br /&gt;
|- &lt;br /&gt;
| GPU &lt;br /&gt;
| -&lt;br /&gt;
| -   &lt;br /&gt;
| 2 * 6 Nvidia M2070&lt;br /&gt;
| -&lt;br /&gt;
| 68 * 3 Nvidia K20x + 16 * 3 Nvidia K40x &lt;br /&gt;
| -&lt;br /&gt;
| -&lt;br /&gt;
| -&lt;br /&gt;
|-&lt;br /&gt;
| Intel Xeon Phi (KNC)&lt;br /&gt;
| -&lt;br /&gt;
| 14 * 2 * Intel(R) Xeon Phi(TM) MIC SE10/7120&lt;br /&gt;
| -&lt;br /&gt;
| -  &lt;br /&gt;
| -&lt;br /&gt;
&lt;br /&gt;
| 45 * 2 * Intel(R) Xeon Phi(TM) MIC SE10/7120&lt;br /&gt;
| -&lt;br /&gt;
| -&lt;br /&gt;
|- &lt;br /&gt;
| Linpack Performance (Rmax)&lt;br /&gt;
| 5 Tflops&lt;br /&gt;
| 27 Tflops&lt;br /&gt;
| 20 Tflops&lt;br /&gt;
| 18 Tlops&lt;br /&gt;
| 254 Tflops&lt;br /&gt;
| ~106 Tflops&lt;br /&gt;
| 10 Tflops&lt;br /&gt;
| 8 Tflops&lt;br /&gt;
|- &lt;br /&gt;
| Compute nodes&lt;br /&gt;
| 32&lt;br /&gt;
| 14&lt;br /&gt;
| 50&lt;br /&gt;
| 128&lt;br /&gt;
| 84&lt;br /&gt;
| 45&lt;br /&gt;
| 1&lt;br /&gt;
&lt;br /&gt;
| 1&lt;br /&gt;
|-  &lt;br /&gt;
| Dedicated storage &lt;br /&gt;
| 50 TB&lt;br /&gt;
| 500 TB&lt;br /&gt;
| 250 TB&lt;br /&gt;
| 500 TB&lt;br /&gt;
| 585 TB (combined with Phi)&lt;br /&gt;
| 585 TB (combined with GPU)&lt;br /&gt;
| 500 TB&lt;br /&gt;
| 240 TB&lt;br /&gt;
|- &lt;br /&gt;
| Interconnect &lt;br /&gt;
| IB QDR&lt;br /&gt;
| IB NB FDR&lt;br /&gt;
| IB QDR&lt;br /&gt;
| IB QDR&lt;br /&gt;
| IB NB FDR&lt;br /&gt;
| IB NB FDR&lt;br /&gt;
| Numalink 6&lt;br /&gt;
| Numalink 6&lt;br /&gt;
|- &lt;br /&gt;
| Scheduler &lt;br /&gt;
| [http://slurm.schedmd.com/ SLURM]&lt;br /&gt;
| [http://slurm.schedmd.com/ SLURM]  &lt;br /&gt;
| [http://slurm.schedmd.com/ SLURM]&lt;br /&gt;
| [http://slurm.schedmd.com/ SLURM]&lt;br /&gt;
| [http://slurm.schedmd.com/ SLURM] &lt;br /&gt;
| [http://slurm.schedmd.com/ SLURM]&lt;br /&gt;
| [http://slurm.schedmd.com/ SLURM] &lt;br /&gt;
| [http://slurm.schedmd.com/ SLURM] &lt;br /&gt;
|- &lt;br /&gt;
| MPI &lt;br /&gt;
| OpenMPI (ompi)&lt;br /&gt;
| IntelMPI (impi) &lt;br /&gt;
| OpenMPI (ompi) &lt;br /&gt;
| SGI MPT (mpt)&lt;br /&gt;
| OpenMPI (ompi)&lt;br /&gt;
&lt;br /&gt;
| OpenMPI (ompi)&lt;br /&gt;
| SGI MPT (mpt)&lt;br /&gt;
&lt;br /&gt;
| SGI MPT (mpt)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Contact ====&lt;br /&gt;
 &lt;br /&gt;
If you have any questions send email to the following address: hpc-support@niif.hu&lt;br /&gt;
 &lt;br /&gt;
Or contact the HPC users in Hungary on the following mailing list: hpc-forum@listserv.niif.hu, you can subscribe [https://listserv.niif.hu/mailman/listinfo/hpc-forum here].&lt;br /&gt;
&lt;br /&gt;
== More information ==&lt;br /&gt;
* [http://www.niif.hu/node/679 HPC information at NIIF.HU]&lt;br /&gt;
* [[HPC_software|List of Software installed on HPCs]]&lt;br /&gt;
&lt;br /&gt;
* [[PRACE_User_Support|User Support (PRACE, and general)]]&lt;br /&gt;
* [http://www.training.prace-ri.eu PRACE training portal]&lt;br /&gt;
* [http://colfaxresearch.com/how-16-04/ Xeon Phi Workshop / Tutorial]&lt;br /&gt;
&lt;br /&gt;
[[Kategória: HPC]]&lt;br /&gt;
[[Kategória: Összefoglaló lapok]]&lt;/div&gt;</summary>
		<author><name>Szigeti(AT)niif.hu</name></author>	</entry>

	<entry>
		<id>https://wiki.niif.hu/index.php?title=HPC&amp;diff=4038</id>
		<title>HPC</title>
		<link rel="alternate" type="text/html" href="https://wiki.niif.hu/index.php?title=HPC&amp;diff=4038"/>
				<updated>2017-02-16T11:55:08Z</updated>
		
		<summary type="html">&lt;p&gt;Szigeti(AT)niif.hu: /* A szuperszámítógépekről */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
[[HPC_en|NIIF HPC in ENGLISH]]&lt;br /&gt;
&lt;br /&gt;
== A szuperszámítógépekről ==&lt;br /&gt;
Az NIIF Intézet szuperszámítógép szolgáltatása tudományos számítási feladatok futtatására, valamint tudományos célú adattárolásra szolgál. A jelenleg integrált szuperszámítógép-rendszer komponensei négy helyszínen találhatók:&lt;br /&gt;
&lt;br /&gt;
* Debreceni Egyetem&lt;br /&gt;
* NIIFI központ&lt;br /&gt;
* Pécsi Tudományegyetem&lt;br /&gt;
* Szegedi Tudományegyetem&lt;br /&gt;
* Miskolci Egyetem&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Fájl:Orszagos-attekinto_hpc.png|center|700px|alt=Az NIIF szuperszámítógépei térképen]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
A tudományos számítási feladatok különböző típusainak minél szélesebb körű lefedettsége érdekében az egyes helyszíneken különböző felépítésű gépek találhatók: két helyszínen ccNUMA, három helyszínen pedig &amp;quot;fat-node&amp;quot; fürtözött megoldás. Az alrendszereket az NIIFI nagy sávszélességű, alacsony késleltetésű HBONE+ adathálózatán keresztül, ARC grid köztesréteg, valamint harmonizált felhasználói azonosítás segítségével integráljuk egységes elvek mentén elérhető számítási egységgé. Az erőforrás jelenleg Magyarország legnagyobb tudományos számítás céljára felhasználható erőforrása, amely összesen 50 billió lebegőpontos művelet elvégzését teszi lehetővé másodpercenként.&lt;br /&gt;
A szuperszámítógép-rendszert az NIIFI üzemelteti és fejleszti. A rendszerhez regisztrációt követően, minden olyan személy vagy kutatócsoport hozzáférhet, amely tagintézményi szerződéses kapcsolatban áll az NIIF Intézettel.&lt;br /&gt;
A gépen megtalálhatók és futtathatók a legkorszerűbb fejlesztőeszközök és tudományos számításra szolgáló alkalmazások, valamint, az NIIF adatközpontban elhelyezett alrendszer kivételével, valamennyi alrendszer kiegészül a számítási feladatok eredményeit megjelenítő vizualizációs eszközökkel.&lt;br /&gt;
&lt;br /&gt;
==== Szuperszámítógépeink összehasonlítása ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot; &lt;br /&gt;
|- &lt;br /&gt;
| Helyszín&lt;br /&gt;
| Budapest&lt;br /&gt;
| [[Budapest2_klaszter|Budapest2]]&lt;br /&gt;
| Szeged&lt;br /&gt;
| Debrecen&lt;br /&gt;
| [[Debrecen2_GPU_klaszter|Debrecen2-GPU (Leo)]] &lt;br /&gt;
| [[Debrecen2_Phi_klaszter|Debrecen3-Phi (Apollo) (tesztüzem)]] &lt;br /&gt;
| Pécs&lt;br /&gt;
&lt;br /&gt;
| [[Miskolc_UV_2000|Miskolc]] (tesztüzem)&lt;br /&gt;
|- &lt;br /&gt;
| Típus&lt;br /&gt;
| HP CP4000SL&lt;br /&gt;
| HP SL250s &lt;br /&gt;
| HP CP4000BL&lt;br /&gt;
| SGI ICE8400EX&lt;br /&gt;
| HP SL250s&lt;br /&gt;
&lt;br /&gt;
| HP Apollo 8000&lt;br /&gt;
| SGI UV 1000&lt;br /&gt;
&lt;br /&gt;
| SGI UV 2000&lt;br /&gt;
|- &lt;br /&gt;
| CPU-k / node &lt;br /&gt;
| 2&lt;br /&gt;
| 2 &lt;br /&gt;
| 4 &lt;br /&gt;
| 2&lt;br /&gt;
| 2&lt;br /&gt;
&lt;br /&gt;
| 2 &lt;br /&gt;
| 192&lt;br /&gt;
&lt;br /&gt;
| 44 &lt;br /&gt;
|- &lt;br /&gt;
| Core-ok / CPU &lt;br /&gt;
| 12&lt;br /&gt;
| 10&lt;br /&gt;
| 12&lt;br /&gt;
| 6&lt;br /&gt;
| 8&lt;br /&gt;
| 12&lt;br /&gt;
| 6&lt;br /&gt;
| 8&lt;br /&gt;
|- &lt;br /&gt;
| Memória / node &lt;br /&gt;
| 66 GB&lt;br /&gt;
| 63 GB &lt;br /&gt;
| 132 GB&lt;br /&gt;
| 47 GB&lt;br /&gt;
| 125 GB&lt;br /&gt;
| 125 GB&lt;br /&gt;
| 6 TB&lt;br /&gt;
| 1.4 TB&lt;br /&gt;
|- &lt;br /&gt;
| Memória / core &lt;br /&gt;
| 2.6 GB&lt;br /&gt;
| 3 GB &lt;br /&gt;
| 2.6 GB&lt;br /&gt;
| 2.6 GB&lt;br /&gt;
| 7.5 GB&lt;br /&gt;
| 5,2 GB&lt;br /&gt;
| 5 GB&lt;br /&gt;
| 4 GB&lt;br /&gt;
|- &lt;br /&gt;
| CPU &lt;br /&gt;
| AMD Opteron 6174 @ 2.2GHz&lt;br /&gt;
| Intel Xeon E5-2680 v2 @ 2.80GHz    &lt;br /&gt;
| AMD Opteron 6174 @ 2.2GHz &lt;br /&gt;
| Intel Xeon X5680 @ 3.33 GHz&lt;br /&gt;
| Intel Xeon E5-2650 v2 @ 2.60GHz &lt;br /&gt;
| Intel Xeon E5-2670 v3 @ 2.30GHz&lt;br /&gt;
| Intel Xeon X7542 @ 2.66 GHz&lt;br /&gt;
| Intel Xeon E5-4627 v2 @ 3.33 GHz&lt;br /&gt;
|- &lt;br /&gt;
| GPU &lt;br /&gt;
| -&lt;br /&gt;
| -   &lt;br /&gt;
| 2 * 6 Nvidia M2070&lt;br /&gt;
| -&lt;br /&gt;
| 68 * 3 Nvidia K20x + 16 * 3 Nvidia K40x &lt;br /&gt;
| -&lt;br /&gt;
| -&lt;br /&gt;
| -&lt;br /&gt;
|-&lt;br /&gt;
| Intel Xeon Phi (KNC)&lt;br /&gt;
| -&lt;br /&gt;
| [[Intel_Xeon_Phi |14 * 2 * Intel(R) Xeon Phi(TM) MIC SE10/7120]]&lt;br /&gt;
| -&lt;br /&gt;
| -  &lt;br /&gt;
| -&lt;br /&gt;
| [[Intel_Xeon_Phi |45 * 2 * Intel(R) Xeon Phi(TM) MIC SE10/7120]]&lt;br /&gt;
| -&lt;br /&gt;
| -&lt;br /&gt;
|- &lt;br /&gt;
| Linpack teljesítmény (Rmax)&lt;br /&gt;
| 5 Tflops&lt;br /&gt;
| 27 Tflops&lt;br /&gt;
| 20 Tflops&lt;br /&gt;
| 18 Tlops&lt;br /&gt;
| 254 Tflops&lt;br /&gt;
| ~106 Tflops&lt;br /&gt;
| 10 Tflops&lt;br /&gt;
| 8 Tflops&lt;br /&gt;
|- &lt;br /&gt;
| Compute node-ok száma &lt;br /&gt;
| 32&lt;br /&gt;
| 14&lt;br /&gt;
| 50&lt;br /&gt;
| 128&lt;br /&gt;
| 84&lt;br /&gt;
| 45&lt;br /&gt;
| 1&lt;br /&gt;
| 1&lt;br /&gt;
|-  &lt;br /&gt;
| Dedikált storage &lt;br /&gt;
| 50 TB&lt;br /&gt;
| 500 TB&lt;br /&gt;
| 250 TB&lt;br /&gt;
| 500 TB&lt;br /&gt;
| 585 TB (Phi-vel közös)&lt;br /&gt;
| 585 TB (GPU-val közös)&lt;br /&gt;
| 500 TB&lt;br /&gt;
| 240 TB&lt;br /&gt;
|- &lt;br /&gt;
| Interconnect &lt;br /&gt;
| IB QDR&lt;br /&gt;
| IB NB FDR&lt;br /&gt;
| IB QDR&lt;br /&gt;
| IB QDR&lt;br /&gt;
| IB NB FDR&lt;br /&gt;
| IB NB FDR&lt;br /&gt;
| Numalink 5&lt;br /&gt;
| Numalink 6&lt;br /&gt;
|- &lt;br /&gt;
| Scheduler &lt;br /&gt;
| [http://slurm.schedmd.com/ SLURM]&lt;br /&gt;
| [http://slurm.schedmd.com/ SLURM]  &lt;br /&gt;
| [http://slurm.schedmd.com/ SLURM]&lt;br /&gt;
| [http://slurm.schedmd.com/ SLURM]&lt;br /&gt;
| [http://slurm.schedmd.com/ SLURM] &lt;br /&gt;
| [http://slurm.schedmd.com/ SLURM]&lt;br /&gt;
| [http://slurm.schedmd.com/ SLURM] &lt;br /&gt;
| [http://slurm.schedmd.com/ SLURM] &lt;br /&gt;
|- &lt;br /&gt;
| MPI &lt;br /&gt;
| OpenMPI (ompi)&lt;br /&gt;
| IntelMPI (impi) &lt;br /&gt;
| OpenMPI (ompi) &lt;br /&gt;
| SGI MPT (mpt)&lt;br /&gt;
| OpenMPI (ompi)&lt;br /&gt;
&lt;br /&gt;
| OpenMPI (ompi)&lt;br /&gt;
| SGI MPT (mpt)&lt;br /&gt;
&lt;br /&gt;
| SGI MPT (mpt)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== További információ ==&lt;br /&gt;
=== Az új HPC-k dokumentációja ===&lt;br /&gt;
* [[Budapest2_klaszter|Budapest2]]&lt;br /&gt;
* [[Miskolc_UV_2000|Miskolc]]&lt;br /&gt;
* [[Debrecen2_GPU_klaszter|Debrecen2 GPU klaszter (LEO)]]&lt;br /&gt;
* [[Debrecen2_Phi_klaszter|Debrecen3 Phi klaszter (Apollo)]]&lt;br /&gt;
&lt;br /&gt;
=== Kapcsolódó oldalak===&lt;br /&gt;
* [[HPC-Portal|HPC portál: projekt és témaszámok kezelése, leállások, statisztikák]]&lt;br /&gt;
* [[NIIF_szuperszámítógépek_használata|A szuperszámítógépek használata]]&lt;br /&gt;
* [[HPC-GYIK|GY.I.K]]&lt;br /&gt;
* [http://www.niif.hu/hu/niif_intezet/az_niifi_szuperszamitogepek_hasznalata NIIF.HU HPC oldal]&lt;br /&gt;
* [[HPC_software|Telepített HPC Szoftverek ]]&lt;br /&gt;
* [https://wiki.niif.hu/index.php?title=HPC_felhasznaloi_alapelvek#Az_NIIF_Szupersz.C3.A1m.C3.ADt.C3.B3g.C3.A9p_Felhaszn.C3.A1l.C3.B3i_Szab.C3.A1lyzata Az NIIF Szuperszámítógép Felhasználói Szabályzata]&lt;br /&gt;
* [https://wiki.niif.hu/index.php?title=HPC_konyvtarak#El.C3.A9rhet.C5.91_f.C3.A1jlrendszerek_a_HPC_k.C3.B6zpontokban Elérhető fájlrendszerek a HPC központokban]&lt;br /&gt;
* [https://wiki.niif.hu/index.php?title=HPC_vizualizacio&amp;amp;action=edit HPC vizualizációs infrastruktúra elérése]&lt;br /&gt;
&lt;br /&gt;
=== Saját HPC oktató tartalmaink ===&lt;br /&gt;
&lt;br /&gt;
* [http://videotorium.hu/hu/channels/details/1895 HPC oktató videók]&lt;br /&gt;
* [http://conference.niif.hu/event/3/session/23/contribution/25 HPC tutorial (NWS 2015) anyagai ]&lt;br /&gt;
* [http://videotorium.hu/hu/events/details/1864,GPU_programozas_workshop GPU programozás workshop (videotorium)]&lt;br /&gt;
* [[Intel_Xeon_Phi |Xeon Phi kártyák használata és alkamazások optimalizációja]]&lt;br /&gt;
&lt;br /&gt;
=== Angol nyelvű tréningek, videók, anyagok ===&lt;br /&gt;
* [http://www.training.prace-ri.eu PRACE training portal]&lt;br /&gt;
* [[PRACE_User_Support|PRACE User Support]]&lt;br /&gt;
* [http://colfaxresearch.com/how-16-04/ Xeon Phi Training]&lt;br /&gt;
[[Kategória: HPC]]&lt;br /&gt;
[[Kategória: Összefoglaló lapok]]&lt;/div&gt;</summary>
		<author><name>Szigeti(AT)niif.hu</name></author>	</entry>

	<entry>
		<id>https://wiki.niif.hu/index.php?title=HPC_software&amp;diff=4024</id>
		<title>HPC software</title>
		<link rel="alternate" type="text/html" href="https://wiki.niif.hu/index.php?title=HPC_software&amp;diff=4024"/>
				<updated>2016-11-16T12:28:42Z</updated>
		
		<summary type="html">&lt;p&gt;Szigeti(AT)niif.hu: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The current version of software on specific machines might be higher than described here. We are encouraging all users to install other tools to be used on our servers if service AUP and license restrictions are respected.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
!Software component&lt;br /&gt;
!Debrecen&lt;br /&gt;
!Pécs&lt;br /&gt;
!Szeged&lt;br /&gt;
!Budapest&lt;br /&gt;
!Budapest2&lt;br /&gt;
!Debrecen2,3 (Leo, Apollo)&lt;br /&gt;
!Miskolc&lt;br /&gt;
|-&lt;br /&gt;
| colspan=8| '''Compiler environment (compiler, debugger, profiler)'''&lt;br /&gt;
|-&lt;br /&gt;
| AMD [http://developer.amd.com/tools-and-sdks/cpu-development/x86-open64-compiler-suite/ compilers ((opencc, openf90, openf95))] || colspan=2|-||colspan=2|Available||colspan=3|-&lt;br /&gt;
|-&lt;br /&gt;
| Math. Library (AMD) [http://developer.amd.com/tools/cpu-development/amd-core-math-library-acml ACML] ||colspan=2|-||colspan=2|4.2.4||colspan=3|-&lt;br /&gt;
|-&lt;br /&gt;
| [http://sources.redhat.com/gdb/ GDB] || colspan=2|7|| colspan=2|7.0.1|| colspan=2|7.2||7.5&lt;br /&gt;
|-&lt;br /&gt;
| [http://gcc.gnu.org/ GNU compilers (gcc, g++, gfortran)]||4,3||4.1.2 || colspan=2|4.1.2, 4.4.4|| colspan=2|4.4.7|| 4.8.5&lt;br /&gt;
|-&lt;br /&gt;
| Gprof|| colspan=2|2,2|| colspan=2|2,17|| colspan=2|2.20|| 2.26.1&lt;br /&gt;
|-&lt;br /&gt;
| Intel [http://software.intel.com/en-us/articles/intel-composer-xe/ compilers (icc, ifort)], [http://software.intel.com/en-us/articles/intel-math-kernel-library-documentation/ MKL],  [http://software.intel.com/en-us/articles/intel-mkl-link-line-advisor/ Linking]  ||colspan=2|12.1.5 (composerxe-2011 Update 11) ||colspan=2|-|| colspan=3|16.0&lt;br /&gt;
|- &lt;br /&gt;
| [http://oprofile.sourceforge.net/ Oprofile] ||  colspan=3|- ||0.9.4||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| colspan=8|'''Libraries supporting parallel development'''&lt;br /&gt;
|- &lt;br /&gt;
| LAM-MPI ||colspan=2|- ||colspan=2|7.1.2||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| MPICH2 ||colspan=2|1,4||colspan=2|1,2||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| [http://openmp.org/ OpenMP]||colspan=2|2.5 (GNU), 3.0 (Intel)||colspan=2|2.5 (GNU), 2.5 (AMD) ||colspan=3|2.5 (GNU), 3.0 (Intel)&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.open-mpi.org/ OpenMPI] ||1.4.2 ||1.3.2 ||colspan=2|1.4.3 || colspan=2|1.8.5||1.6.3&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.csm.ornl.gov/pvm/ PVM] ||colspan=4|3.4.5 ||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| [http://videotorium.hu/hu/recordings/details/12291,Overview_and_usage_of_the_SGI_HPC_softwares_on_the_SGI_ICE_and_SGI_UV_supercomputers SGI Perf. Suite] ||colspan=2|Available||colspan=2|-||colspan=2|-||Available&lt;br /&gt;
|- &lt;br /&gt;
| SGI-MPI (MPT) ||colspan=2|2,04||colspan=2|-||colspan=2|-||2.12&lt;br /&gt;
|- &lt;br /&gt;
| PGI Compiler and Tools ||colspan=5|-|| 15.10 || -&lt;br /&gt;
|- &lt;br /&gt;
| colspan=8|'''Libraries supporting scientific work'''&lt;br /&gt;
|- &lt;br /&gt;
| AmberTool || 16 ||colspan=6|-&lt;br /&gt;
|-&lt;br /&gt;
| Atlas ||colspan=4|3.8.3||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| BLACS ||colspan=4|1.1 (OpenMPI, MPICH2) ||1.1 (OpenMPI Intel)||3.1.3||-&lt;br /&gt;
|- &lt;br /&gt;
| BLAS ||colspan=2|3.2.2 ||colspan=2|3||3.6.1||-||3&lt;br /&gt;
|- &lt;br /&gt;
| Boost||colspan=2|1,36||colspan=2|1,33||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| FFTW ||colspan=2|3.1.2 ||colspan=2|3.2.2||3.3.5 ||3.3.4||3.3.3&lt;br /&gt;
|- &lt;br /&gt;
| Gromacs ||colspan=2|4.5.4 ||colspan=2|4.5.5||colspan=2|-||4.6.2&lt;br /&gt;
|- &lt;br /&gt;
| GUILE ||colspan=2|1.8.5 ||colspan=2|-||colspan=2|-||1.8.8&lt;br /&gt;
|- &lt;br /&gt;
| HDF5 ||colspan=2|1.8.7||colspan=2|-||colspan=2|1.8.14||-&lt;br /&gt;
|- &lt;br /&gt;
| LAMMPS ||colspan=4|-||16.05.14 (Phi)||16.05.14 (GPU, Phi)||-&lt;br /&gt;
|-&lt;br /&gt;
| Lapack ||colspan=2|3.2.2 ||colspan=2|3 ||3.6.1 ||colspan=2|-&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.maplesoft.com/ Maple] ||colspan=2| - ||colspan=2|18||-||2015||-&lt;br /&gt;
|- &lt;br /&gt;
| [https://wiki.niif.hu/index.php?title=NIIF_szuperszámítógépek_használata#Matlab_feladatok Matlab] ||colspan=2|7.13.0 (R2011b)|| colspan=2|-||-||R2015b||-&lt;br /&gt;
|- &lt;br /&gt;
| NetCDF ||4.1.3 ||- ||colspan=2|4.2.1.1||4.4.1||4.3.3.1||-&lt;br /&gt;
|- &lt;br /&gt;
| NumPy ||colspan=2|1,3||colspan=2|1.7.1||1.11.2 ||-||1.7.0&lt;br /&gt;
|- &lt;br /&gt;
| PETSc||colspan=4|-||3.7.4 Intel||colspan=2|-&lt;br /&gt;
|- &lt;br /&gt;
| Python||colspan=4|2.7.5||3.5.2||colspan=2|-&lt;br /&gt;
|- &lt;br /&gt;
| Octave ||colspan=2|3,4||colspan=2|3.0.5 ||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| Qt||colspan=2|3.3.8b, 4.6.2||colspan=2|3.3.6 ||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| R ||2.8.1, 3.2.0, 3.2.5(Intel) || 2.8.1 ||colspan=2|-||colspan=2|-||3.0.1&lt;br /&gt;
|- &lt;br /&gt;
| Scalapack||colspan=4|1.7.5||colspan=2|2.0.2||-&lt;br /&gt;
|- &lt;br /&gt;
| Scilab ||colspan=2|5.3.2||colspan=2|-||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| Scipy||colspan=4|0.12.0||0.18.1 ||colspan=2|-&lt;br /&gt;
|- &lt;br /&gt;
| Sun JRE ||colspan=2|1,6||colspan=2|-||colspan=1|-||1.8||1.7&lt;br /&gt;
|- &lt;br /&gt;
| Gaussian||colspan=3|09_REV_C.01||-||-||colspan=2|09_REV_D.01&lt;br /&gt;
|- &lt;br /&gt;
| colspan=8|'''Middleware'''&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.nordugrid.org/arc/ ARC grid middleware] ||colspan=7|4.1.0 (A-REX, ARIS, clients))&lt;br /&gt;
|- &lt;br /&gt;
| colspan=8|'''Miscellaneous software'''&lt;br /&gt;
|- &lt;br /&gt;
| Emacs ||colspan=2|22,3||colspan=2|21,4||-||23.1.1||22.3.1&lt;br /&gt;
|- &lt;br /&gt;
| GNU Plot ||colspan=2|4.2.3 ||colspan=2|4||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| ImageMagick ||colspan=2|6.4.3 ||colspan=2|6.2.8||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.virtualgl.org/About/TigerVNC Turbo VNC]||colspan=4|1,1||colspan=3|2&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.blender.org/ Blender]||colspan=3|2,49||-||colspan=2|2.49||-&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.virtualgl.org/ VirtualGL]||colspan=3|2.3.2||-||colspan=2|2.4||-&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.paraview.org/ Paraview]||colspan=3|3.14.1||-||colspan=3|3.14.1&lt;br /&gt;
|- &lt;br /&gt;
| ffmpeg||colspan=2|-||colspan=2|0.4.9||colspan=3|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
[[Kategória: HPC]]&lt;/div&gt;</summary>
		<author><name>Szigeti(AT)niif.hu</name></author>	</entry>

	<entry>
		<id>https://wiki.niif.hu/index.php?title=HPC_software&amp;diff=4023</id>
		<title>HPC software</title>
		<link rel="alternate" type="text/html" href="https://wiki.niif.hu/index.php?title=HPC_software&amp;diff=4023"/>
				<updated>2016-11-16T09:15:20Z</updated>
		
		<summary type="html">&lt;p&gt;Szigeti(AT)niif.hu: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The current version of software on specific machines might be higher than described here. We are encouraging all users to install other tools to be used on our servers if service AUP and license restrictions are respected.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
!Software component&lt;br /&gt;
!Debrecen&lt;br /&gt;
!Pécs&lt;br /&gt;
!Szeged&lt;br /&gt;
!Budapest&lt;br /&gt;
!Budapest2&lt;br /&gt;
!Debrecen2,3 (Leo, Apollo)&lt;br /&gt;
!Miskolc&lt;br /&gt;
|-&lt;br /&gt;
| colspan=8| '''Compiler environment (compiler, debugger, profiler)'''&lt;br /&gt;
|-&lt;br /&gt;
| AMD [http://developer.amd.com/tools-and-sdks/cpu-development/x86-open64-compiler-suite/ compilers ((opencc, openf90, openf95))] || colspan=2|-||colspan=2|Available||colspan=3|-&lt;br /&gt;
|-&lt;br /&gt;
| Math. Library (AMD) [http://developer.amd.com/tools/cpu-development/amd-core-math-library-acml ACML] ||colspan=2|-||colspan=2|4.2.4||colspan=3|-&lt;br /&gt;
|-&lt;br /&gt;
| [http://sources.redhat.com/gdb/ GDB] || colspan=2|7|| colspan=2|7.0.1|| colspan=2|7.2||7.5&lt;br /&gt;
|-&lt;br /&gt;
| [http://gcc.gnu.org/ GNU compilers (gcc, g++, gfortran)]||4,3||4.1.2 || colspan=2|4.1.2, 4.4.4|| colspan=2|4.4.7|| 4.8.5&lt;br /&gt;
|-&lt;br /&gt;
| Gprof|| colspan=2|2,2|| colspan=2|2,17|| colspan=2|2.20|| 2.26.1&lt;br /&gt;
|-&lt;br /&gt;
| Intel [http://software.intel.com/en-us/articles/intel-composer-xe/ compilers (icc, ifort)], [http://software.intel.com/en-us/articles/intel-math-kernel-library-documentation/ MKL],  [http://software.intel.com/en-us/articles/intel-mkl-link-line-advisor/ Linking]  ||colspan=2|12.1.5 (composerxe-2011 Update 11) ||colspan=2|-|| colspan=3|16.0&lt;br /&gt;
|- &lt;br /&gt;
| [http://oprofile.sourceforge.net/ Oprofile] ||  colspan=3|- ||0.9.4||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| colspan=8|'''Libraries supporting parallel development'''&lt;br /&gt;
|- &lt;br /&gt;
| LAM-MPI ||colspan=2|- ||colspan=2|7.1.2||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| MPICH2 ||colspan=2|1,4||colspan=2|1,2||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| [http://openmp.org/ OpenMP]||colspan=2|2.5 (GNU), 3.0 (Intel)||colspan=2|2.5 (GNU), 2.5 (AMD) ||colspan=3|2.5 (GNU), 3.0 (Intel)&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.open-mpi.org/ OpenMPI] ||1.4.2 ||1.3.2 ||colspan=2|1.4.3 || colspan=2|1.8.5||1.6.3&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.csm.ornl.gov/pvm/ PVM] ||colspan=4|3.4.5 ||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| [http://videotorium.hu/hu/recordings/details/12291,Overview_and_usage_of_the_SGI_HPC_softwares_on_the_SGI_ICE_and_SGI_UV_supercomputers SGI Perf. Suite] ||colspan=2|Available||colspan=2|-||colspan=2|-||Available&lt;br /&gt;
|- &lt;br /&gt;
| SGI-MPI (MPT) ||colspan=2|2,04||colspan=2|-||colspan=2|-||2.12&lt;br /&gt;
|- &lt;br /&gt;
| PGI Compiler and Tools ||colspan=5|-|| 15.10 || -&lt;br /&gt;
|- &lt;br /&gt;
| colspan=8|'''Libraries supporting scientific work'''&lt;br /&gt;
|- &lt;br /&gt;
| AmberTool || 16 ||colspan=6|-&lt;br /&gt;
|-&lt;br /&gt;
| Atlas ||colspan=4|3.8.3||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| BLACS ||colspan=4|1.1 (OpenMPI, MPICH2) ||1.1 (OpenMPI Intel)||3.1.3||-&lt;br /&gt;
|- &lt;br /&gt;
| BLAS ||colspan=2|3.2.2 ||colspan=2|3||3.6.1||-||3&lt;br /&gt;
|- &lt;br /&gt;
| Boost||colspan=2|1,36||colspan=2|1,33||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| FFTW ||colspan=2|3.1.2 ||colspan=2|3.2.2||3.3.5 ||3.3.4||3.3.3&lt;br /&gt;
|- &lt;br /&gt;
| Gromacs ||colspan=2|4.5.4 ||colspan=2|4.5.5||colspan=2|-||4.6.2&lt;br /&gt;
|- &lt;br /&gt;
| GUILE ||colspan=2|1.8.5 ||colspan=2|-||colspan=2|-||1.8.8&lt;br /&gt;
|- &lt;br /&gt;
| HDF5 ||colspan=2|1.8.7||colspan=2|-||colspan=2|1.8.14||-&lt;br /&gt;
|- &lt;br /&gt;
| LAMMPS ||colspan=4|-||16.05.14 (Phi)||16.05.14 (GPU, Phi)||-&lt;br /&gt;
|-&lt;br /&gt;
| Lapack ||colspan=2|3.2.2 ||colspan=2|3 ||3.6.1 ||colspan=2|-&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.maplesoft.com/ Maple] ||colspan=2| - ||colspan=2|18||-||2015||-&lt;br /&gt;
|- &lt;br /&gt;
| [https://wiki.niif.hu/index.php?title=NIIF_szuperszámítógépek_használata#Matlab_feladatok Matlab] ||colspan=2|7.13.0 (R2011b)|| colspan=2|-||-||R2015b||-&lt;br /&gt;
|- &lt;br /&gt;
| NetCDF ||4.1.3 ||- ||colspan=2|4.2.1.1||4.4.1||4.3.3.1||-&lt;br /&gt;
|- &lt;br /&gt;
| NumPy ||colspan=2|1,3||colspan=2|1.7.1||1.11.2 ||-||1.7.0&lt;br /&gt;
|- &lt;br /&gt;
| PETSc||colspan=4|-||3.7.4 Intel||colspan=2|-&lt;br /&gt;
|- &lt;br /&gt;
| Python||colspan=4|2.7.5||3.5.2||colspan=2|-&lt;br /&gt;
|- &lt;br /&gt;
| Octave ||colspan=2|3,4||colspan=2|3.0.5 ||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| Qt||colspan=2|3.3.8b, 4.6.2||colspan=2|3.3.6 ||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| R ||2.8.1, 3.2.0, 3.2.5(Intel) || 2.8.1 ||colspan=2|-||colspan=2|-||3.0.1&lt;br /&gt;
|- &lt;br /&gt;
| Scalapack||colspan=4|1.7.5||colspan=2|2.0.2||-&lt;br /&gt;
|- &lt;br /&gt;
| Scilab ||colspan=2|5.3.2||colspan=2|-||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| Sun JRE ||colspan=2|1,6||colspan=2|-||colspan=1|-||1.8||1.7&lt;br /&gt;
|- &lt;br /&gt;
| Gaussian||colspan=3|09_REV_C.01||-||-||colspan=2|09_REV_D.01&lt;br /&gt;
|- &lt;br /&gt;
| colspan=8|'''Middleware'''&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.nordugrid.org/arc/ ARC grid middleware] ||colspan=7|4.1.0 (A-REX, ARIS, clients))&lt;br /&gt;
|- &lt;br /&gt;
| colspan=8|'''Miscellaneous software'''&lt;br /&gt;
|- &lt;br /&gt;
| Emacs ||colspan=2|22,3||colspan=2|21,4||-||23.1.1||22.3.1&lt;br /&gt;
|- &lt;br /&gt;
| GNU Plot ||colspan=2|4.2.3 ||colspan=2|4||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| ImageMagick ||colspan=2|6.4.3 ||colspan=2|6.2.8||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.virtualgl.org/About/TigerVNC Turbo VNC]||colspan=4|1,1||colspan=3|2&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.blender.org/ Blender]||colspan=3|2,49||-||colspan=2|2.49||-&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.virtualgl.org/ VirtualGL]||colspan=3|2.3.2||-||colspan=2|2.4||-&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.paraview.org/ Paraview]||colspan=3|3.14.1||-||colspan=3|3.14.1&lt;br /&gt;
|- &lt;br /&gt;
| ffmpeg||colspan=2|-||colspan=2|0.4.9||colspan=3|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
[[Kategória: HPC]]&lt;/div&gt;</summary>
		<author><name>Szigeti(AT)niif.hu</name></author>	</entry>

	<entry>
		<id>https://wiki.niif.hu/index.php?title=HPC_software&amp;diff=4022</id>
		<title>HPC software</title>
		<link rel="alternate" type="text/html" href="https://wiki.niif.hu/index.php?title=HPC_software&amp;diff=4022"/>
				<updated>2016-11-16T08:15:17Z</updated>
		
		<summary type="html">&lt;p&gt;Szigeti(AT)niif.hu: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The current version of software on specific machines might be higher than described here. We are encouraging all users to install other tools to be used on our servers if service AUP and license restrictions are respected.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
!Software component&lt;br /&gt;
!Debrecen&lt;br /&gt;
!Pécs&lt;br /&gt;
!Szeged&lt;br /&gt;
!Budapest&lt;br /&gt;
!Budapest2&lt;br /&gt;
!Debrecen2,3 (Leo, Apollo)&lt;br /&gt;
!Miskolc&lt;br /&gt;
|-&lt;br /&gt;
| colspan=8| '''Compiler environment (compiler, debugger, profiler)'''&lt;br /&gt;
|-&lt;br /&gt;
| AMD [http://developer.amd.com/tools-and-sdks/cpu-development/x86-open64-compiler-suite/ compilers ((opencc, openf90, openf95))] || colspan=2|-||colspan=2|Available||colspan=3|-&lt;br /&gt;
|-&lt;br /&gt;
| Math. Library (AMD) [http://developer.amd.com/tools/cpu-development/amd-core-math-library-acml ACML] ||colspan=2|-||colspan=2|4.2.4||colspan=3|-&lt;br /&gt;
|-&lt;br /&gt;
| [http://sources.redhat.com/gdb/ GDB] || colspan=2|7|| colspan=2|7.0.1|| colspan=2|7.2||7.5&lt;br /&gt;
|-&lt;br /&gt;
| [http://gcc.gnu.org/ GNU compilers (gcc, g++, gfortran)]||4,3||4.1.2 || colspan=2|4.1.2, 4.4.4|| colspan=2|4.4.7|| 4.8.5&lt;br /&gt;
|-&lt;br /&gt;
| Gprof|| colspan=2|2,2|| colspan=2|2,17|| colspan=2|2.20|| 2.26.1&lt;br /&gt;
|-&lt;br /&gt;
| Intel [http://software.intel.com/en-us/articles/intel-composer-xe/ compilers (icc, ifort)], [http://software.intel.com/en-us/articles/intel-math-kernel-library-documentation/ MKL],  [http://software.intel.com/en-us/articles/intel-mkl-link-line-advisor/ Linking]  ||colspan=2|12.1.5 (composerxe-2011 Update 11) ||colspan=2|-|| colspan=3|16.0&lt;br /&gt;
|- &lt;br /&gt;
| [http://oprofile.sourceforge.net/ Oprofile] ||  colspan=3|- ||0.9.4||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| colspan=8|'''Libraries supporting parallel development'''&lt;br /&gt;
|- &lt;br /&gt;
| LAM-MPI ||colspan=2|- ||colspan=2|7.1.2||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| MPICH2 ||colspan=2|1,4||colspan=2|1,2||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| [http://openmp.org/ OpenMP]||colspan=2|2.5 (GNU), 3.0 (Intel)||colspan=2|2.5 (GNU), 2.5 (AMD) ||colspan=3|2.5 (GNU), 3.0 (Intel)&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.open-mpi.org/ OpenMPI] ||1.4.2 ||1.3.2 ||colspan=2|1.4.3 || colspan=2|1.8.5||1.6.3&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.csm.ornl.gov/pvm/ PVM] ||colspan=4|3.4.5 ||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| [http://videotorium.hu/hu/recordings/details/12291,Overview_and_usage_of_the_SGI_HPC_softwares_on_the_SGI_ICE_and_SGI_UV_supercomputers SGI Perf. Suite] ||colspan=2|Available||colspan=2|-||colspan=2|-||Available&lt;br /&gt;
|- &lt;br /&gt;
| SGI-MPI (MPT) ||colspan=2|2,04||colspan=2|-||colspan=2|-||2.12&lt;br /&gt;
|- &lt;br /&gt;
| PGI Compiler and Tools ||colspan=5|-|| 15.10 || -&lt;br /&gt;
|- &lt;br /&gt;
| colspan=8|'''Libraries supporting scientific work'''&lt;br /&gt;
|- &lt;br /&gt;
| AmberTool || 16 ||colspan=6|-&lt;br /&gt;
|-&lt;br /&gt;
| Atlas ||colspan=4|3.8.3||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| BLACS ||colspan=4|1.1 (OpenMPI, MPICH2) ||1.1 (OpenMPI Intel)||3.1.3||-&lt;br /&gt;
|- &lt;br /&gt;
| BLAS ||colspan=2|3.2.2 ||colspan=2|3||3.6.1||-||3&lt;br /&gt;
|- &lt;br /&gt;
| Boost||colspan=2|1,36||colspan=2|1,33||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| FFTW ||colspan=2|3.1.2 ||colspan=2|3.2.2||3.3.5 ||3.3.4||3.3.3&lt;br /&gt;
|- &lt;br /&gt;
| Gromacs ||colspan=2|4.5.4 ||colspan=2|4.5.5||colspan=2|-||4.6.2&lt;br /&gt;
|- &lt;br /&gt;
| GUILE ||colspan=2|1.8.5 ||colspan=2|-||colspan=2|-||1.8.8&lt;br /&gt;
|- &lt;br /&gt;
| HDF5 ||colspan=2|1.8.7||colspan=2|-||colspan=2|1.8.14||-&lt;br /&gt;
|- &lt;br /&gt;
| LAMMPS ||colspan=4|-||16.05.14 (Phi)||16.05.14 (GPU, Phi)||-&lt;br /&gt;
|-&lt;br /&gt;
| Lapack ||colspan=2|3.2.2 ||colspan=2|3 ||3.6.1 ||colspan=2|-&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.maplesoft.com/ Maple] ||colspan=2| - ||colspan=2|18||-||2015||-&lt;br /&gt;
|- &lt;br /&gt;
| [https://wiki.niif.hu/index.php?title=NIIF_szuperszámítógépek_használata#Matlab_feladatok Matlab] ||colspan=2|7.13.0 (R2011b)|| colspan=2|-||-||R2015b||-&lt;br /&gt;
|- &lt;br /&gt;
| NetCDF ||4.1.3 ||- ||colspan=2|4.2.1.1||4.4.1||4.3.3.1||-&lt;br /&gt;
|- &lt;br /&gt;
| NumPy ||colspan=2|1,3||colspan=2|1.7.1||colspan=2|-||1.7.0&lt;br /&gt;
|- &lt;br /&gt;
| PETSc||colspan=4|-||3.7.4 Intel||colspan=2|-&lt;br /&gt;
|- &lt;br /&gt;
| Python||colspan=4|2.7.5||3.5.2||colspan=2|-&lt;br /&gt;
|- &lt;br /&gt;
| Octave ||colspan=2|3,4||colspan=2|3.0.5 ||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| Qt||colspan=2|3.3.8b, 4.6.2||colspan=2|3.3.6 ||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| R ||2.8.1, 3.2.0, 3.2.5(Intel) || 2.8.1 ||colspan=2|-||colspan=2|-||3.0.1&lt;br /&gt;
|- &lt;br /&gt;
| Scalapack||colspan=4|1.7.5||colspan=2|2.0.2||-&lt;br /&gt;
|- &lt;br /&gt;
| Scilab ||colspan=2|5.3.2||colspan=2|-||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| Sun JRE ||colspan=2|1,6||colspan=2|-||colspan=1|-||1.8||1.7&lt;br /&gt;
|- &lt;br /&gt;
| Gaussian||colspan=3|09_REV_C.01||-||-||colspan=2|09_REV_D.01&lt;br /&gt;
|- &lt;br /&gt;
| colspan=8|'''Middleware'''&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.nordugrid.org/arc/ ARC grid middleware] ||colspan=7|4.1.0 (A-REX, ARIS, clients))&lt;br /&gt;
|- &lt;br /&gt;
| colspan=8|'''Miscellaneous software'''&lt;br /&gt;
|- &lt;br /&gt;
| Emacs ||colspan=2|22,3||colspan=2|21,4||-||23.1.1||22.3.1&lt;br /&gt;
|- &lt;br /&gt;
| GNU Plot ||colspan=2|4.2.3 ||colspan=2|4||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| ImageMagick ||colspan=2|6.4.3 ||colspan=2|6.2.8||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.virtualgl.org/About/TigerVNC Turbo VNC]||colspan=4|1,1||colspan=3|2&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.blender.org/ Blender]||colspan=3|2,49||-||colspan=2|2.49||-&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.virtualgl.org/ VirtualGL]||colspan=3|2.3.2||-||colspan=2|2.4||-&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.paraview.org/ Paraview]||colspan=3|3.14.1||-||colspan=3|3.14.1&lt;br /&gt;
|- &lt;br /&gt;
| ffmpeg||colspan=2|-||colspan=2|0.4.9||colspan=3|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
[[Kategória: HPC]]&lt;/div&gt;</summary>
		<author><name>Szigeti(AT)niif.hu</name></author>	</entry>

	<entry>
		<id>https://wiki.niif.hu/index.php?title=HPC_software&amp;diff=4021</id>
		<title>HPC software</title>
		<link rel="alternate" type="text/html" href="https://wiki.niif.hu/index.php?title=HPC_software&amp;diff=4021"/>
				<updated>2016-11-15T14:37:28Z</updated>
		
		<summary type="html">&lt;p&gt;Szigeti(AT)niif.hu: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The current version of software on specific machines might be higher than described here. We are encouraging all users to install other tools to be used on our servers if service AUP and license restrictions are respected.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
!Software component&lt;br /&gt;
!Debrecen&lt;br /&gt;
!Pécs&lt;br /&gt;
!Szeged&lt;br /&gt;
!Budapest&lt;br /&gt;
!Budapest2&lt;br /&gt;
!Debrecen2,3 (Leo, Apollo)&lt;br /&gt;
!Miskolc&lt;br /&gt;
|-&lt;br /&gt;
| colspan=8| '''Compiler environment (compiler, debugger, profiler)'''&lt;br /&gt;
|-&lt;br /&gt;
| AMD [http://developer.amd.com/tools-and-sdks/cpu-development/x86-open64-compiler-suite/ compilers ((opencc, openf90, openf95))] || colspan=2|-||colspan=2|Available||colspan=3|-&lt;br /&gt;
|-&lt;br /&gt;
| Math. Library (AMD) [http://developer.amd.com/tools/cpu-development/amd-core-math-library-acml ACML] ||colspan=2|-||colspan=2|4.2.4||colspan=3|-&lt;br /&gt;
|-&lt;br /&gt;
| [http://sources.redhat.com/gdb/ GDB] || colspan=2|7|| colspan=2|7.0.1|| colspan=2|7.2||7.5&lt;br /&gt;
|-&lt;br /&gt;
| [http://gcc.gnu.org/ GNU compilers (gcc, g++, gfortran)]||4,3||4.1.2 || colspan=2|4.1.2, 4.4.4|| colspan=2|4.4.7|| 4.8.5&lt;br /&gt;
|-&lt;br /&gt;
| Gprof|| colspan=2|2,2|| colspan=2|2,17|| colspan=2|2.20|| 2.26.1&lt;br /&gt;
|-&lt;br /&gt;
| Intel [http://software.intel.com/en-us/articles/intel-composer-xe/ compilers (icc, ifort)], [http://software.intel.com/en-us/articles/intel-math-kernel-library-documentation/ MKL],  [http://software.intel.com/en-us/articles/intel-mkl-link-line-advisor/ Linking]  ||colspan=2|12.1.5 (composerxe-2011 Update 11) ||colspan=2|-|| colspan=3|16.0&lt;br /&gt;
|- &lt;br /&gt;
| [http://oprofile.sourceforge.net/ Oprofile] ||  colspan=3|- ||0.9.4||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| colspan=8|'''Libraries supporting parallel development'''&lt;br /&gt;
|- &lt;br /&gt;
| LAM-MPI ||colspan=2|- ||colspan=2|7.1.2||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| MPICH2 ||colspan=2|1,4||colspan=2|1,2||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| [http://openmp.org/ OpenMP]||colspan=2|2.5 (GNU), 3.0 (Intel)||colspan=2|2.5 (GNU), 2.5 (AMD) ||colspan=3|2.5 (GNU), 3.0 (Intel)&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.open-mpi.org/ OpenMPI] ||1.4.2 ||1.3.2 ||colspan=2|1.4.3 || colspan=2|1.8.5||1.6.3&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.csm.ornl.gov/pvm/ PVM] ||colspan=4|3.4.5 ||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| [http://videotorium.hu/hu/recordings/details/12291,Overview_and_usage_of_the_SGI_HPC_softwares_on_the_SGI_ICE_and_SGI_UV_supercomputers SGI Perf. Suite] ||colspan=2|Available||colspan=2|-||colspan=2|-||Available&lt;br /&gt;
|- &lt;br /&gt;
| SGI-MPI (MPT) ||colspan=2|2,04||colspan=2|-||colspan=2|-||2.12&lt;br /&gt;
|- &lt;br /&gt;
| PGI Compiler and Tools ||colspan=5|-|| 15.10 || -&lt;br /&gt;
|- &lt;br /&gt;
| colspan=8|'''Libraries supporting scientific work'''&lt;br /&gt;
|- &lt;br /&gt;
| AmberTool || 16 ||colspan=6|-&lt;br /&gt;
|-&lt;br /&gt;
| Atlas ||colspan=4|3.8.3||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| BLACS ||colspan=4|1.1 (OpenMPI, MPICH2) ||1.1 (OpenMPI Intel)||3.1.3||-&lt;br /&gt;
|- &lt;br /&gt;
| BLAS ||colspan=2|3.2.2 ||colspan=2|3||3.6.1||-||3&lt;br /&gt;
|- &lt;br /&gt;
| Boost||colspan=2|1,36||colspan=2|1,33||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| FFTW ||colspan=2|3.1.2 ||colspan=2|3.2.2||3.3.5 ||3.3.4||3.3.3&lt;br /&gt;
|- &lt;br /&gt;
| Gromacs ||colspan=2|4.5.4 ||colspan=2|4.5.5||colspan=2|-||4.6.2&lt;br /&gt;
|- &lt;br /&gt;
| GUILE ||colspan=2|1.8.5 ||colspan=2|-||colspan=2|-||1.8.8&lt;br /&gt;
|- &lt;br /&gt;
| HDF5 ||colspan=2|1.8.7||colspan=2|-||colspan=2|1.8.14||-&lt;br /&gt;
|- &lt;br /&gt;
| LAMMPS ||colspan=4|-||16.05.14 (Phi)||16.05.14 (GPU, Phi)||-&lt;br /&gt;
|-&lt;br /&gt;
| Lapack ||colspan=2|3.2.2 ||colspan=2|3 ||3.6.1 ||colspan=2|-&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.maplesoft.com/ Maple] ||colspan=2| - ||colspan=2|18||-||2015||-&lt;br /&gt;
|- &lt;br /&gt;
| [https://wiki.niif.hu/index.php?title=NIIF_szuperszámítógépek_használata#Matlab_feladatok Matlab] ||colspan=2|7.13.0 (R2011b)|| colspan=2|-||-||R2015b||-&lt;br /&gt;
|- &lt;br /&gt;
| NetCDF ||4.1.3 ||- ||colspan=2|4.2.1.1||4.4.1||4.3.3.1||-&lt;br /&gt;
|- &lt;br /&gt;
| NumPy ||colspan=2|1,3||colspan=2|1.5.1||colspan=2|-||1.7.0&lt;br /&gt;
|- &lt;br /&gt;
| PETSc||colspan=4|-||3.7.4 Intel||colspan=2|-&lt;br /&gt;
|- &lt;br /&gt;
| Octave ||colspan=2|3,4||colspan=2|3.0.5 ||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| Qt||colspan=2|3.3.8b, 4.6.2||colspan=2|3.3.6 ||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| R ||2.8.1, 3.2.0, 3.2.5(Intel) || 2.8.1 ||colspan=2|-||colspan=2|-||3.0.1&lt;br /&gt;
|- &lt;br /&gt;
| Scalapack||colspan=4|1.7.5||colspan=2|2.0.2||-&lt;br /&gt;
|- &lt;br /&gt;
| Scilab ||colspan=2|5.3.2||colspan=2|-||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| Sun JRE ||colspan=2|1,6||colspan=2|-||colspan=1|-||1.8||1.7&lt;br /&gt;
|- &lt;br /&gt;
| Gaussian||colspan=3|09_REV_C.01||-||-||colspan=2|09_REV_D.01&lt;br /&gt;
|- &lt;br /&gt;
| colspan=8|'''Middleware'''&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.nordugrid.org/arc/ ARC grid middleware] ||colspan=7|4.1.0 (A-REX, ARIS, clients))&lt;br /&gt;
|- &lt;br /&gt;
| colspan=8|'''Miscellaneous software'''&lt;br /&gt;
|- &lt;br /&gt;
| Emacs ||colspan=2|22,3||colspan=2|21,4||-||23.1.1||22.3.1&lt;br /&gt;
|- &lt;br /&gt;
| GNU Plot ||colspan=2|4.2.3 ||colspan=2|4||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| ImageMagick ||colspan=2|6.4.3 ||colspan=2|6.2.8||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.virtualgl.org/About/TigerVNC Turbo VNC]||colspan=4|1,1||colspan=3|2&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.blender.org/ Blender]||colspan=3|2,49||-||colspan=2|2.49||-&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.virtualgl.org/ VirtualGL]||colspan=3|2.3.2||-||colspan=2|2.4||-&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.paraview.org/ Paraview]||colspan=3|3.14.1||-||colspan=3|3.14.1&lt;br /&gt;
|- &lt;br /&gt;
| ffmpeg||colspan=2|-||colspan=2|0.4.9||colspan=3|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
[[Kategória: HPC]]&lt;/div&gt;</summary>
		<author><name>Szigeti(AT)niif.hu</name></author>	</entry>

	<entry>
		<id>https://wiki.niif.hu/index.php?title=HPC_software&amp;diff=4020</id>
		<title>HPC software</title>
		<link rel="alternate" type="text/html" href="https://wiki.niif.hu/index.php?title=HPC_software&amp;diff=4020"/>
				<updated>2016-11-15T13:32:59Z</updated>
		
		<summary type="html">&lt;p&gt;Szigeti(AT)niif.hu: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The current version of software on specific machines might be higher than described here. We are encouraging all users to install other tools to be used on our servers if service AUP and license restrictions are respected.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
!Software component&lt;br /&gt;
!Debrecen&lt;br /&gt;
!Pécs&lt;br /&gt;
!Szeged&lt;br /&gt;
!Budapest&lt;br /&gt;
!Budapest2&lt;br /&gt;
!Debrecen2,3 (Leo, Apollo)&lt;br /&gt;
!Miskolc&lt;br /&gt;
|-&lt;br /&gt;
| colspan=8| '''Compiler environment (compiler, debugger, profiler)'''&lt;br /&gt;
|-&lt;br /&gt;
| AMD [http://developer.amd.com/tools-and-sdks/cpu-development/x86-open64-compiler-suite/ compilers ((opencc, openf90, openf95))] || colspan=2|-||colspan=2|Available||colspan=3|-&lt;br /&gt;
|-&lt;br /&gt;
| Math. Library (AMD) [http://developer.amd.com/tools/cpu-development/amd-core-math-library-acml ACML] ||colspan=2|-||colspan=2|4.2.4||colspan=3|-&lt;br /&gt;
|-&lt;br /&gt;
| [http://sources.redhat.com/gdb/ GDB] || colspan=2|7|| colspan=2|7.0.1|| colspan=2|7.2||7.5&lt;br /&gt;
|-&lt;br /&gt;
| [http://gcc.gnu.org/ GNU compilers (gcc, g++, gfortran)]||4,3||4.1.2 || colspan=2|4.1.2, 4.4.4|| colspan=2|4.4.7|| 4.8.5&lt;br /&gt;
|-&lt;br /&gt;
| Gprof|| colspan=2|2,2|| colspan=2|2,17|| colspan=2|2.20|| 2.26.1&lt;br /&gt;
|-&lt;br /&gt;
| Intel [http://software.intel.com/en-us/articles/intel-composer-xe/ compilers (icc, ifort)], [http://software.intel.com/en-us/articles/intel-math-kernel-library-documentation/ MKL],  [http://software.intel.com/en-us/articles/intel-mkl-link-line-advisor/ Linking]  ||colspan=2|12.1.5 (composerxe-2011 Update 11) ||colspan=2|-|| colspan=3|16.0&lt;br /&gt;
|- &lt;br /&gt;
| [http://oprofile.sourceforge.net/ Oprofile] ||  colspan=3|- ||0.9.4||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| colspan=8|'''Libraries supporting parallel development'''&lt;br /&gt;
|- &lt;br /&gt;
| LAM-MPI ||colspan=2|- ||colspan=2|7.1.2||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| MPICH2 ||colspan=2|1,4||colspan=2|1,2||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| [http://openmp.org/ OpenMP]||colspan=2|2.5 (GNU), 3.0 (Intel)||colspan=2|2.5 (GNU), 2.5 (AMD) ||colspan=3|2.5 (GNU), 3.0 (Intel)&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.open-mpi.org/ OpenMPI] ||1.4.2 ||1.3.2 ||colspan=2|1.4.3 || colspan=2|1.8.5||1.6.3&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.csm.ornl.gov/pvm/ PVM] ||colspan=4|3.4.5 ||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| [http://videotorium.hu/hu/recordings/details/12291,Overview_and_usage_of_the_SGI_HPC_softwares_on_the_SGI_ICE_and_SGI_UV_supercomputers SGI Perf. Suite] ||colspan=2|Available||colspan=2|-||colspan=2|-||Available&lt;br /&gt;
|- &lt;br /&gt;
| SGI-MPI (MPT) ||colspan=2|2,04||colspan=2|-||colspan=2|-||2.12&lt;br /&gt;
|- &lt;br /&gt;
| PGI Compiler and Tools ||colspan=5|-|| 15.10 || -&lt;br /&gt;
|- &lt;br /&gt;
| colspan=8|'''Libraries supporting scientific work'''&lt;br /&gt;
|- &lt;br /&gt;
| AmberTool || 16 ||colspan=6|-&lt;br /&gt;
|-&lt;br /&gt;
| Atlas ||colspan=4|3.8.3||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| BLACS ||colspan=4|1.1 (OpenMPI, MPICH2) ||1.1 (OpenMPI Intel)||3.1.3||-&lt;br /&gt;
|- &lt;br /&gt;
| BLAS ||colspan=2|3.2.2 ||colspan=2|3||3.6.1||-||3&lt;br /&gt;
|- &lt;br /&gt;
| Boost||colspan=2|1,36||colspan=2|1,33||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| FFTW ||colspan=2|3.1.2 ||colspan=2|3.2.2||3.3.5 ||3.3.4||3.3.3&lt;br /&gt;
|- &lt;br /&gt;
| Gromacs ||colspan=2|4.5.4 ||colspan=2|4.5.5||colspan=2|-||4.6.2&lt;br /&gt;
|- &lt;br /&gt;
| GUILE ||colspan=2|1.8.5 ||colspan=2|-||colspan=2|-||1.8.8&lt;br /&gt;
|- &lt;br /&gt;
| HDF5 ||colspan=2|1.8.7||colspan=2|-||colspan=2|1.8.14||-&lt;br /&gt;
|- &lt;br /&gt;
| LAMMPS ||colspan=4|-||16.05.14 (Phi)||16.05.14 (GPU, Phi)||-&lt;br /&gt;
|-&lt;br /&gt;
| Lapack ||colspan=2|3.2.2 ||colspan=2|3 ||3.6.1 ||colspan=2|-&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.maplesoft.com/ Maple] ||colspan=2| - ||colspan=2|18||-||2015||-&lt;br /&gt;
|- &lt;br /&gt;
| [https://wiki.niif.hu/index.php?title=NIIF_szuperszámítógépek_használata#Matlab_feladatok Matlab] ||colspan=2|7.13.0 (R2011b)|| colspan=2|-||-||R2015b||-&lt;br /&gt;
|- &lt;br /&gt;
| NumPy ||colspan=2|1,3||colspan=2|1.5.1||colspan=2|-||1.7.0&lt;br /&gt;
|- &lt;br /&gt;
| PETSc||colspan=4|-||3.7.4 Intel||colspan=2|-&lt;br /&gt;
|- &lt;br /&gt;
| Octave ||colspan=2|3,4||colspan=2|3.0.5 ||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| Qt||colspan=2|3.3.8b, 4.6.2||colspan=2|3.3.6 ||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| R ||2.8.1, 3.2.0, 3.2.5(Intel) || 2.8.1 ||colspan=2|-||colspan=2|-||3.0.1&lt;br /&gt;
|- &lt;br /&gt;
| Scalapack||colspan=4|1.7.5||colspan=2|2.0.2||-&lt;br /&gt;
|- &lt;br /&gt;
| Scilab ||colspan=2|5.3.2||colspan=2|-||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| Sun JRE ||colspan=2|1,6||colspan=2|-||colspan=1|-||1.8||1.7&lt;br /&gt;
|- &lt;br /&gt;
| Gaussian||colspan=3|09_REV_C.01||-||-||colspan=2|09_REV_D.01&lt;br /&gt;
|- &lt;br /&gt;
| colspan=8|'''Middleware'''&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.nordugrid.org/arc/ ARC grid middleware] ||colspan=7|4.1.0 (A-REX, ARIS, clients))&lt;br /&gt;
|- &lt;br /&gt;
| colspan=8|'''Miscellaneous software'''&lt;br /&gt;
|- &lt;br /&gt;
| Emacs ||colspan=2|22,3||colspan=2|21,4||-||23.1.1||22.3.1&lt;br /&gt;
|- &lt;br /&gt;
| GNU Plot ||colspan=2|4.2.3 ||colspan=2|4||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| ImageMagick ||colspan=2|6.4.3 ||colspan=2|6.2.8||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.virtualgl.org/About/TigerVNC Turbo VNC]||colspan=4|1,1||colspan=3|2&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.blender.org/ Blender]||colspan=3|2,49||-||colspan=2|2.49||-&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.virtualgl.org/ VirtualGL]||colspan=3|2.3.2||-||colspan=2|2.4||-&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.paraview.org/ Paraview]||colspan=3|3.14.1||-||colspan=3|3.14.1&lt;br /&gt;
|- &lt;br /&gt;
| ffmpeg||colspan=2|-||colspan=2|0.4.9||colspan=3|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
[[Kategória: HPC]]&lt;/div&gt;</summary>
		<author><name>Szigeti(AT)niif.hu</name></author>	</entry>

	<entry>
		<id>https://wiki.niif.hu/index.php?title=HPC_software&amp;diff=4019</id>
		<title>HPC software</title>
		<link rel="alternate" type="text/html" href="https://wiki.niif.hu/index.php?title=HPC_software&amp;diff=4019"/>
				<updated>2016-11-15T11:46:11Z</updated>
		
		<summary type="html">&lt;p&gt;Szigeti(AT)niif.hu: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The current version of software on specific machines might be higher than described here. We are encouraging all users to install other tools to be used on our servers if service AUP and license restrictions are respected.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
!Software component&lt;br /&gt;
!Debrecen&lt;br /&gt;
!Pécs&lt;br /&gt;
!Szeged&lt;br /&gt;
!Budapest&lt;br /&gt;
!Budapest2&lt;br /&gt;
!Debrecen2,3 (Leo, Apollo)&lt;br /&gt;
!Miskolc&lt;br /&gt;
|-&lt;br /&gt;
| colspan=8| '''Compiler environment (compiler, debugger, profiler)'''&lt;br /&gt;
|-&lt;br /&gt;
| AMD [http://developer.amd.com/tools-and-sdks/cpu-development/x86-open64-compiler-suite/ compilers ((opencc, openf90, openf95))] || colspan=2|-||colspan=2|Available||colspan=3|-&lt;br /&gt;
|-&lt;br /&gt;
| Math. Library (AMD) [http://developer.amd.com/tools/cpu-development/amd-core-math-library-acml ACML] ||colspan=2|-||colspan=2|4.2.4||colspan=3|-&lt;br /&gt;
|-&lt;br /&gt;
| [http://sources.redhat.com/gdb/ GDB] || colspan=2|7|| colspan=2|7.0.1|| colspan=2|7.2||7.5&lt;br /&gt;
|-&lt;br /&gt;
| [http://gcc.gnu.org/ GNU compilers (gcc, g++, gfortran)]||4,3||4.1.2 || colspan=2|4.1.2, 4.4.4|| colspan=2|4.4.7|| 4.8.5&lt;br /&gt;
|-&lt;br /&gt;
| Gprof|| colspan=2|2,2|| colspan=2|2,17|| colspan=2|2.20|| 2.26.1&lt;br /&gt;
|-&lt;br /&gt;
| Intel [http://software.intel.com/en-us/articles/intel-composer-xe/ compilers (icc, ifort)], [http://software.intel.com/en-us/articles/intel-math-kernel-library-documentation/ MKL],  [http://software.intel.com/en-us/articles/intel-mkl-link-line-advisor/ Linking]  ||colspan=2|12.1.5 (composerxe-2011 Update 11) ||colspan=2|-|| colspan=3|16.0&lt;br /&gt;
|- &lt;br /&gt;
| [http://oprofile.sourceforge.net/ Oprofile] ||  colspan=3|- ||0.9.4||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| colspan=8|'''Libraries supporting parallel development'''&lt;br /&gt;
|- &lt;br /&gt;
| LAM-MPI ||colspan=2|- ||colspan=2|7.1.2||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| MPICH2 ||colspan=2|1,4||colspan=2|1,2||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| [http://openmp.org/ OpenMP]||colspan=2|2.5 (GNU), 3.0 (Intel)||colspan=2|2.5 (GNU), 2.5 (AMD) ||colspan=3|2.5 (GNU), 3.0 (Intel)&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.open-mpi.org/ OpenMPI] ||1.4.2 ||1.3.2 ||colspan=2|1.4.3 || colspan=2|1.8.5||1.6.3&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.csm.ornl.gov/pvm/ PVM] ||colspan=4|3.4.5 ||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| [http://videotorium.hu/hu/recordings/details/12291,Overview_and_usage_of_the_SGI_HPC_softwares_on_the_SGI_ICE_and_SGI_UV_supercomputers SGI Perf. Suite] ||colspan=2|Available||colspan=2|-||colspan=2|-||Available&lt;br /&gt;
|- &lt;br /&gt;
| SGI-MPI (MPT) ||colspan=2|2,04||colspan=2|-||colspan=2|-||2.12&lt;br /&gt;
|- &lt;br /&gt;
| PGI Compiler and Tools ||colspan=5|-|| 15.10 || -&lt;br /&gt;
|- &lt;br /&gt;
| colspan=8|'''Libraries supporting scientific work'''&lt;br /&gt;
|- &lt;br /&gt;
| AmberTool || 16 ||colspan=6|-&lt;br /&gt;
|-&lt;br /&gt;
| Atlas ||colspan=4|3.8.3||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| BLACS ||colspan=4|1.1 (OpenMPI, MPICH2) ||1.1 (OpenMPI Intel)||3.1.3||-&lt;br /&gt;
|- &lt;br /&gt;
| BLAS ||colspan=2|3.2.2 ||colspan=2|3||3.6.1||-||3&lt;br /&gt;
|- &lt;br /&gt;
| Boost||colspan=2|1,36||colspan=2|1,33||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| FFTW ||colspan=2|3.1.2 ||colspan=2|3.2.2||3.3.5 ||3.3.4||3.3.3&lt;br /&gt;
|- &lt;br /&gt;
| Gromacs ||colspan=2|4.5.4 ||colspan=2|4.5.5||colspan=2|-||4.6.2&lt;br /&gt;
|- &lt;br /&gt;
| GUILE ||colspan=2|1.8.5 ||colspan=2|-||colspan=2|-||1.8.8&lt;br /&gt;
|- &lt;br /&gt;
| HDF5 ||colspan=2|1.8.7||colspan=2|-||colspan=2|1.8.14||-&lt;br /&gt;
|- &lt;br /&gt;
| LAMMPS ||colspan=4|-||16.05.14 (Phi)||16.05.14 (GPU, Phi)||-&lt;br /&gt;
|-&lt;br /&gt;
| Lapack ||colspan=2|3.2.2 ||colspan=2|3 ||3.6.1 ||colspan=2|-&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.maplesoft.com/ Maple] ||colspan=2| - ||colspan=2|18||-||2015||-&lt;br /&gt;
|- &lt;br /&gt;
| [https://wiki.niif.hu/index.php?title=NIIF_szuperszámítógépek_használata#Matlab_feladatok Matlab] ||colspan=2|7.13.0 (R2011b)|| colspan=2|-||-||R2015b||-&lt;br /&gt;
|- &lt;br /&gt;
| NumPy ||colspan=2|1,3||colspan=2|1.5.1||colspan=2|-||1.7.0&lt;br /&gt;
|- &lt;br /&gt;
| Octave ||colspan=2|3,4||colspan=2|3.0.5 ||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| Qt||colspan=2|3.3.8b, 4.6.2||colspan=2|3.3.6 ||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| R ||2.8.1, 3.2.0, 3.2.5(Intel) || 2.8.1 ||colspan=2|-||colspan=2|-||3.0.1&lt;br /&gt;
|- &lt;br /&gt;
| Scalapack||colspan=4|1.7.5||colspan=2|2.0.2||-&lt;br /&gt;
|- &lt;br /&gt;
| Scilab ||colspan=2|5.3.2||colspan=2|-||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| Sun JRE ||colspan=2|1,6||colspan=2|-||colspan=1|-||1.8||1.7&lt;br /&gt;
|- &lt;br /&gt;
| Gaussian||colspan=3|09_REV_C.01||-||-||colspan=2|09_REV_D.01&lt;br /&gt;
|- &lt;br /&gt;
| colspan=8|'''Middleware'''&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.nordugrid.org/arc/ ARC grid middleware] ||colspan=7|4.1.0 (A-REX, ARIS, clients))&lt;br /&gt;
|- &lt;br /&gt;
| colspan=8|'''Miscellaneous software'''&lt;br /&gt;
|- &lt;br /&gt;
| Emacs ||colspan=2|22,3||colspan=2|21,4||-||23.1.1||22.3.1&lt;br /&gt;
|- &lt;br /&gt;
| GNU Plot ||colspan=2|4.2.3 ||colspan=2|4||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| ImageMagick ||colspan=2|6.4.3 ||colspan=2|6.2.8||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.virtualgl.org/About/TigerVNC Turbo VNC]||colspan=4|1,1||colspan=3|2&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.blender.org/ Blender]||colspan=3|2,49||-||colspan=2|2.49||-&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.virtualgl.org/ VirtualGL]||colspan=3|2.3.2||-||colspan=2|2.4||-&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.paraview.org/ Paraview]||colspan=3|3.14.1||-||colspan=3|3.14.1&lt;br /&gt;
|- &lt;br /&gt;
| ffmpeg||colspan=2|-||colspan=2|0.4.9||colspan=3|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
[[Kategória: HPC]]&lt;/div&gt;</summary>
		<author><name>Szigeti(AT)niif.hu</name></author>	</entry>

	<entry>
		<id>https://wiki.niif.hu/index.php?title=HPC_software&amp;diff=4018</id>
		<title>HPC software</title>
		<link rel="alternate" type="text/html" href="https://wiki.niif.hu/index.php?title=HPC_software&amp;diff=4018"/>
				<updated>2016-11-15T08:52:23Z</updated>
		
		<summary type="html">&lt;p&gt;Szigeti(AT)niif.hu: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The current version of software on specific machines might be higher than described here. We are encouraging all users to install other tools to be used on our servers if service AUP and license restrictions are respected.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
!Software component&lt;br /&gt;
!Debrecen&lt;br /&gt;
!Pécs&lt;br /&gt;
!Szeged&lt;br /&gt;
!Budapest&lt;br /&gt;
!Budapest2&lt;br /&gt;
!Debrecen2,3 (Leo, Apollo)&lt;br /&gt;
!Miskolc&lt;br /&gt;
|-&lt;br /&gt;
| colspan=8| '''Compiler environment (compiler, debugger, profiler)'''&lt;br /&gt;
|-&lt;br /&gt;
| AMD [http://developer.amd.com/tools-and-sdks/cpu-development/x86-open64-compiler-suite/ compilers ((opencc, openf90, openf95))] || colspan=2|-||colspan=2|Available||colspan=3|-&lt;br /&gt;
|-&lt;br /&gt;
| Math. Library (AMD) [http://developer.amd.com/tools/cpu-development/amd-core-math-library-acml ACML] ||colspan=2|-||colspan=2|4.2.4||colspan=3|-&lt;br /&gt;
|-&lt;br /&gt;
| [http://sources.redhat.com/gdb/ GDB] || colspan=2|7|| colspan=2|7.0.1|| colspan=2|7.2||7.5&lt;br /&gt;
|-&lt;br /&gt;
| [http://gcc.gnu.org/ GNU compilers (gcc, g++, gfortran)]||4,3||4.1.2 || colspan=2|4.1.2, 4.4.4|| colspan=2|4.4.7|| 4.8.5&lt;br /&gt;
|-&lt;br /&gt;
| Gprof|| colspan=2|2,2|| colspan=2|2,17|| colspan=2|2.20|| 2.26.1&lt;br /&gt;
|-&lt;br /&gt;
| Intel [http://software.intel.com/en-us/articles/intel-composer-xe/ compilers (icc, ifort)], [http://software.intel.com/en-us/articles/intel-math-kernel-library-documentation/ MKL],  [http://software.intel.com/en-us/articles/intel-mkl-link-line-advisor/ Linking]  ||colspan=2|12.1.5 (composerxe-2011 Update 11) ||colspan=2|-|| colspan=3|16.0&lt;br /&gt;
|- &lt;br /&gt;
| [http://oprofile.sourceforge.net/ Oprofile] ||  colspan=3|- ||0.9.4||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| colspan=8|'''Libraries supporting parallel development'''&lt;br /&gt;
|- &lt;br /&gt;
| LAM-MPI ||colspan=2|- ||colspan=2|7.1.2||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| MPICH2 ||colspan=2|1,4||colspan=2|1,2||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| [http://openmp.org/ OpenMP]||colspan=2|2.5 (GNU), 3.0 (Intel)||colspan=2|2.5 (GNU), 2.5 (AMD) ||colspan=3|2.5 (GNU), 3.0 (Intel)&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.open-mpi.org/ OpenMPI] ||1.4.2 ||1.3.2 ||colspan=2|1.4.3 || colspan=2|1.8.5||1.6.3&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.csm.ornl.gov/pvm/ PVM] ||colspan=4|3.4.5 ||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| [http://videotorium.hu/hu/recordings/details/12291,Overview_and_usage_of_the_SGI_HPC_softwares_on_the_SGI_ICE_and_SGI_UV_supercomputers SGI Perf. Suite] ||colspan=2|Available||colspan=2|-||colspan=2|-||Available&lt;br /&gt;
|- &lt;br /&gt;
| SGI-MPI (MPT) ||colspan=2|2,04||colspan=2|-||colspan=2|-||2.12&lt;br /&gt;
|- &lt;br /&gt;
| PGI Compiler and Tools ||colspan=5|-|| 15.10 || -&lt;br /&gt;
|- &lt;br /&gt;
| colspan=8|'''Libraries supporting scientific work'''&lt;br /&gt;
|- &lt;br /&gt;
| AmberTool || 16 ||colspan=6|-&lt;br /&gt;
|-&lt;br /&gt;
| Atlas ||colspan=4|3.8.3||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| BLACS ||colspan=4|1.1 (OpenMPI, MPICH2) ||-||3.1.3||-&lt;br /&gt;
|- &lt;br /&gt;
| BLAS ||colspan=2|3.2.2 ||colspan=2|3||3.6.1||-||3&lt;br /&gt;
|- &lt;br /&gt;
| Boost||colspan=2|1,36||colspan=2|1,33||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| FFTW ||colspan=2|3.1.2 ||colspan=2|3.2.2||3.3.5 ||3.3.4||3.3.3&lt;br /&gt;
|- &lt;br /&gt;
| Gromacs ||colspan=2|4.5.4 ||colspan=2|4.5.5||colspan=2|-||4.6.2&lt;br /&gt;
|- &lt;br /&gt;
| GUILE ||colspan=2|1.8.5 ||colspan=2|-||colspan=2|-||1.8.8&lt;br /&gt;
|- &lt;br /&gt;
| HDF5 ||colspan=2|1.8.7||colspan=2|-||colspan=2|1.8.14||-&lt;br /&gt;
|- &lt;br /&gt;
| LAMMPS ||colspan=4|-||16.05.14 (Phi)||16.05.14 (GPU, Phi)||-&lt;br /&gt;
|-&lt;br /&gt;
| Lapack ||colspan=2|3.2.2 ||colspan=2|3 ||3.6.1 ||colspan=2|-&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.maplesoft.com/ Maple] ||colspan=2| - ||colspan=2|18||-||2015||-&lt;br /&gt;
|- &lt;br /&gt;
| [https://wiki.niif.hu/index.php?title=NIIF_szuperszámítógépek_használata#Matlab_feladatok Matlab] ||colspan=2|7.13.0 (R2011b)|| colspan=2|-||-||R2015b||-&lt;br /&gt;
|- &lt;br /&gt;
| NumPy ||colspan=2|1,3||colspan=2|1.5.1||colspan=2|-||1.7.0&lt;br /&gt;
|- &lt;br /&gt;
| Octave ||colspan=2|3,4||colspan=2|3.0.5 ||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| Qt||colspan=2|3.3.8b, 4.6.2||colspan=2|3.3.6 ||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| R ||2.8.1, 3.2.0, 3.2.5(Intel) || 2.8.1 ||colspan=2|-||colspan=2|-||3.0.1&lt;br /&gt;
|- &lt;br /&gt;
| Scalapack||colspan=4|1.7.5||colspan=2|2.0.2||-&lt;br /&gt;
|- &lt;br /&gt;
| Scilab ||colspan=2|5.3.2||colspan=2|-||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| Sun JRE ||colspan=2|1,6||colspan=2|-||colspan=1|-||1.8||1.7&lt;br /&gt;
|- &lt;br /&gt;
| Gaussian||colspan=3|09_REV_C.01||-||-||colspan=2|09_REV_D.01&lt;br /&gt;
|- &lt;br /&gt;
| colspan=8|'''Middleware'''&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.nordugrid.org/arc/ ARC grid middleware] ||colspan=7|4.1.0 (A-REX, ARIS, clients))&lt;br /&gt;
|- &lt;br /&gt;
| colspan=8|'''Miscellaneous software'''&lt;br /&gt;
|- &lt;br /&gt;
| Emacs ||colspan=2|22,3||colspan=2|21,4||-||23.1.1||22.3.1&lt;br /&gt;
|- &lt;br /&gt;
| GNU Plot ||colspan=2|4.2.3 ||colspan=2|4||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| ImageMagick ||colspan=2|6.4.3 ||colspan=2|6.2.8||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.virtualgl.org/About/TigerVNC Turbo VNC]||colspan=4|1,1||colspan=3|2&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.blender.org/ Blender]||colspan=3|2,49||-||colspan=2|2.49||-&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.virtualgl.org/ VirtualGL]||colspan=3|2.3.2||-||colspan=2|2.4||-&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.paraview.org/ Paraview]||colspan=3|3.14.1||-||colspan=3|3.14.1&lt;br /&gt;
|- &lt;br /&gt;
| ffmpeg||colspan=2|-||colspan=2|0.4.9||colspan=3|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
[[Kategória: HPC]]&lt;/div&gt;</summary>
		<author><name>Szigeti(AT)niif.hu</name></author>	</entry>

	<entry>
		<id>https://wiki.niif.hu/index.php?title=HPC_software&amp;diff=4017</id>
		<title>HPC software</title>
		<link rel="alternate" type="text/html" href="https://wiki.niif.hu/index.php?title=HPC_software&amp;diff=4017"/>
				<updated>2016-11-15T08:49:57Z</updated>
		
		<summary type="html">&lt;p&gt;Szigeti(AT)niif.hu: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The current version of software on specific machines might be higher than described here. We are encouraging all users to install other tools to be used on our servers if service AUP and license restrictions are respected.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
!Software component&lt;br /&gt;
!Debrecen&lt;br /&gt;
!Pécs&lt;br /&gt;
!Szeged&lt;br /&gt;
!Budapest&lt;br /&gt;
!Budapest2&lt;br /&gt;
!Debrecen2,3 (Leo, Apollo)&lt;br /&gt;
!Miskolc&lt;br /&gt;
|-&lt;br /&gt;
| colspan=8| '''Compiler environment (compiler, debugger, profiler)'''&lt;br /&gt;
|-&lt;br /&gt;
| AMD [http://developer.amd.com/tools-and-sdks/cpu-development/x86-open64-compiler-suite/ compilers ((opencc, openf90, openf95))] || colspan=2|-||colspan=2|Available||colspan=3|-&lt;br /&gt;
|-&lt;br /&gt;
| Math. Library (AMD) [http://developer.amd.com/tools/cpu-development/amd-core-math-library-acml ACML] ||colspan=2|-||colspan=2|4.2.4||colspan=3|-&lt;br /&gt;
|-&lt;br /&gt;
| [http://sources.redhat.com/gdb/ GDB] || colspan=2|7|| colspan=2|7.0.1|| colspan=2|7.2||7.5&lt;br /&gt;
|-&lt;br /&gt;
| [http://gcc.gnu.org/ GNU compilers (gcc, g++, gfortran)]||4,3||4.1.2 || colspan=2|4.1.2, 4.4.4|| colspan=2|4.4.7|| 4.8.5&lt;br /&gt;
|-&lt;br /&gt;
| Gprof|| colspan=2|2,2|| colspan=2|2,17|| colspan=2|2.20|| 2.26.1&lt;br /&gt;
|-&lt;br /&gt;
| Intel [http://software.intel.com/en-us/articles/intel-composer-xe/ compilers (icc, ifort)], [http://software.intel.com/en-us/articles/intel-math-kernel-library-documentation/ MKL],  [http://software.intel.com/en-us/articles/intel-mkl-link-line-advisor/ Linking]  ||colspan=2|12.1.5 (composerxe-2011 Update 11) ||colspan=2|-|| colspan=3|16.0&lt;br /&gt;
|- &lt;br /&gt;
| [http://oprofile.sourceforge.net/ Oprofile] ||  colspan=3|- ||0.9.4||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| colspan=8|'''Libraries supporting parallel development'''&lt;br /&gt;
|- &lt;br /&gt;
| LAM-MPI ||colspan=2|- ||colspan=2|7.1.2||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| MPICH2 ||colspan=2|1,4||colspan=2|1,2||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| [http://openmp.org/ OpenMP]||colspan=2|2.5 (GNU), 3.0 (Intel)||colspan=2|2.5 (GNU), 2.5 (AMD) ||colspan=3|2.5 (GNU), 3.0 (Intel)&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.open-mpi.org/ OpenMPI] ||1.4.2 ||1.3.2 ||colspan=2|1.4.3 || colspan=2|1.8.5||1.6.3&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.csm.ornl.gov/pvm/ PVM] ||colspan=4|3.4.5 ||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| [http://videotorium.hu/hu/recordings/details/12291,Overview_and_usage_of_the_SGI_HPC_softwares_on_the_SGI_ICE_and_SGI_UV_supercomputers SGI Perf. Suite] ||colspan=2|Available||colspan=2|-||colspan=2|-||Available&lt;br /&gt;
|- &lt;br /&gt;
| SGI-MPI (MPT) ||colspan=2|2,04||colspan=2|-||colspan=2|-||2.12&lt;br /&gt;
|- &lt;br /&gt;
| PGI Compiler and Tools ||colspan=5|-|| 15.10 || -&lt;br /&gt;
|- &lt;br /&gt;
| colspan=8|'''Libraries supporting scientific work'''&lt;br /&gt;
|- &lt;br /&gt;
| AmberTool || 16 ||colspan=6|-&lt;br /&gt;
|-&lt;br /&gt;
| Atlas ||colspan=4|3.8.3||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| BLACS ||colspan=4|1.1 (OpenMPI, MPICH2) ||-||3.1.3||-&lt;br /&gt;
|- &lt;br /&gt;
| BLAS ||colspan=2|3.2.2 ||colspan=2|3||3.6.1||-||3&lt;br /&gt;
|- &lt;br /&gt;
| Boost||colspan=2|1,36||colspan=2|1,33||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| FFTW ||colspan=2|3.1.2 ||colspan=2|3.2.2||3.3.5 ||-||3.3.3&lt;br /&gt;
|- &lt;br /&gt;
| Gromacs ||colspan=2|4.5.4 ||colspan=2|4.5.5||colspan=2|-||4.6.2&lt;br /&gt;
|- &lt;br /&gt;
| GUILE ||colspan=2|1.8.5 ||colspan=2|-||colspan=2|-||1.8.8&lt;br /&gt;
|- &lt;br /&gt;
| HDF5 ||colspan=2|1.8.7||colspan=2|-||colspan=2|1.8.17||-&lt;br /&gt;
|- &lt;br /&gt;
| LAMMPS ||colspan=4|-||16.05.14 (Phi)||16.05.14 (GPU, Phi)||-&lt;br /&gt;
|-&lt;br /&gt;
| Lapack ||colspan=2|3.2.2 ||colspan=2|3 ||3.6.1 ||colspan=2|-&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.maplesoft.com/ Maple] ||colspan=2| - ||colspan=2|18||-||2015||-&lt;br /&gt;
|- &lt;br /&gt;
| [https://wiki.niif.hu/index.php?title=NIIF_szuperszámítógépek_használata#Matlab_feladatok Matlab] ||colspan=2|7.13.0 (R2011b)|| colspan=2|-||-||R2015b||-&lt;br /&gt;
|- &lt;br /&gt;
| NumPy ||colspan=2|1,3||colspan=2|1.5.1||colspan=2|-||1.7.0&lt;br /&gt;
|- &lt;br /&gt;
| Octave ||colspan=2|3,4||colspan=2|3.0.5 ||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| Qt||colspan=2|3.3.8b, 4.6.2||colspan=2|3.3.6 ||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| R ||2.8.1, 3.2.0, 3.2.5(Intel) || 2.8.1 ||colspan=2|-||colspan=2|-||3.0.1&lt;br /&gt;
|- &lt;br /&gt;
| Scalapack||colspan=4|1.7.5||colspan=2|2.0.2||-&lt;br /&gt;
|- &lt;br /&gt;
| Scilab ||colspan=2|5.3.2||colspan=2|-||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| Sun JRE ||colspan=2|1,6||colspan=2|-||colspan=1|-||1.8||1.7&lt;br /&gt;
|- &lt;br /&gt;
| Gaussian||colspan=3|09_REV_C.01||-||-||colspan=2|09_REV_D.01&lt;br /&gt;
|- &lt;br /&gt;
| colspan=8|'''Middleware'''&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.nordugrid.org/arc/ ARC grid middleware] ||colspan=7|4.1.0 (A-REX, ARIS, clients))&lt;br /&gt;
|- &lt;br /&gt;
| colspan=8|'''Miscellaneous software'''&lt;br /&gt;
|- &lt;br /&gt;
| Emacs ||colspan=2|22,3||colspan=2|21,4||-||23.1.1||22.3.1&lt;br /&gt;
|- &lt;br /&gt;
| GNU Plot ||colspan=2|4.2.3 ||colspan=2|4||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| ImageMagick ||colspan=2|6.4.3 ||colspan=2|6.2.8||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.virtualgl.org/About/TigerVNC Turbo VNC]||colspan=4|1,1||colspan=3|2&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.blender.org/ Blender]||colspan=3|2,49||-||colspan=2|2.49||-&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.virtualgl.org/ VirtualGL]||colspan=3|2.3.2||-||colspan=2|2.4||-&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.paraview.org/ Paraview]||colspan=3|3.14.1||-||colspan=3|3.14.1&lt;br /&gt;
|- &lt;br /&gt;
| ffmpeg||colspan=2|-||colspan=2|0.4.9||colspan=3|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
[[Kategória: HPC]]&lt;/div&gt;</summary>
		<author><name>Szigeti(AT)niif.hu</name></author>	</entry>

	<entry>
		<id>https://wiki.niif.hu/index.php?title=HPC_software&amp;diff=4016</id>
		<title>HPC software</title>
		<link rel="alternate" type="text/html" href="https://wiki.niif.hu/index.php?title=HPC_software&amp;diff=4016"/>
				<updated>2016-11-15T08:31:29Z</updated>
		
		<summary type="html">&lt;p&gt;Szigeti(AT)niif.hu: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The current version of software on specific machines might be higher than described here. We are encouraging all users to install other tools to be used on our servers if service AUP and license restrictions are respected.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
!Software component&lt;br /&gt;
!Debrecen&lt;br /&gt;
!Pécs&lt;br /&gt;
!Szeged&lt;br /&gt;
!Budapest&lt;br /&gt;
!Budapest2&lt;br /&gt;
!Debrecen2,3 (Leo, Apollo)&lt;br /&gt;
!Miskolc&lt;br /&gt;
|-&lt;br /&gt;
| colspan=8| '''Compiler environment (compiler, debugger, profiler)'''&lt;br /&gt;
|-&lt;br /&gt;
| AMD [http://developer.amd.com/tools-and-sdks/cpu-development/x86-open64-compiler-suite/ compilers ((opencc, openf90, openf95))] || colspan=2|-||colspan=2|Available||colspan=3|-&lt;br /&gt;
|-&lt;br /&gt;
| Math. Library (AMD) [http://developer.amd.com/tools/cpu-development/amd-core-math-library-acml ACML] ||colspan=2|-||colspan=2|4.2.4||colspan=3|-&lt;br /&gt;
|-&lt;br /&gt;
| [http://sources.redhat.com/gdb/ GDB] || colspan=2|7|| colspan=2|7.0.1|| colspan=2|7.2||7.5&lt;br /&gt;
|-&lt;br /&gt;
| [http://gcc.gnu.org/ GNU compilers (gcc, g++, gfortran)]||4,3||4.1.2 || colspan=2|4.1.2, 4.4.4|| colspan=2|4.4.7|| 4.8.5&lt;br /&gt;
|-&lt;br /&gt;
| Gprof|| colspan=2|2,2|| colspan=2|2,17|| colspan=2|2.20|| 2.26.1&lt;br /&gt;
|-&lt;br /&gt;
| Intel [http://software.intel.com/en-us/articles/intel-composer-xe/ compilers (icc, ifort)], [http://software.intel.com/en-us/articles/intel-math-kernel-library-documentation/ MKL],  [http://software.intel.com/en-us/articles/intel-mkl-link-line-advisor/ Linking]  ||colspan=2|12.1.5 (composerxe-2011 Update 11) ||colspan=2|-|| colspan=3|16.0&lt;br /&gt;
|- &lt;br /&gt;
| [http://oprofile.sourceforge.net/ Oprofile] ||  colspan=3|- ||0.9.4||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| colspan=8|'''Libraries supporting parallel development'''&lt;br /&gt;
|- &lt;br /&gt;
| LAM-MPI ||colspan=2|- ||colspan=2|7.1.2||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| MPICH2 ||colspan=2|1,4||colspan=2|1,2||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| [http://openmp.org/ OpenMP]||colspan=2|2.5 (GNU), 3.0 (Intel)||colspan=2|2.5 (GNU), 2.5 (AMD) ||colspan=3|2.5 (GNU), 3.0 (Intel)&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.open-mpi.org/ OpenMPI] ||1.4.2 ||1.3.2 ||colspan=2|1.4.3 || colspan=2|1.8.5||1.6.3&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.csm.ornl.gov/pvm/ PVM] ||colspan=4|3.4.5 ||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| [http://videotorium.hu/hu/recordings/details/12291,Overview_and_usage_of_the_SGI_HPC_softwares_on_the_SGI_ICE_and_SGI_UV_supercomputers SGI Perf. Suite] ||colspan=2|Available||colspan=2|-||colspan=2|-||Available&lt;br /&gt;
|- &lt;br /&gt;
| SGI-MPI (MPT) ||colspan=2|2,04||colspan=2|-||colspan=2|-||2.12&lt;br /&gt;
|- &lt;br /&gt;
| PGI Compiler and Tools ||colspan=5|-|| 15.10 || -&lt;br /&gt;
|- &lt;br /&gt;
| colspan=8|'''Libraries supporting scientific work'''&lt;br /&gt;
|- &lt;br /&gt;
| AmberTool || 16 ||colspan=6|-&lt;br /&gt;
|-&lt;br /&gt;
| Atlas ||colspan=4|3.8.3||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| BLACS ||colspan=4|1.1 (OpenMPI, MPICH2) ||-||3.1.3||-&lt;br /&gt;
|- &lt;br /&gt;
| BLAS ||colspan=2|3.2.2 ||colspan=2|3||3.6.1||-||3&lt;br /&gt;
|- &lt;br /&gt;
| Boost||colspan=2|1,36||colspan=2|1,33||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| FFTW ||colspan=2|3.1.2 ||colspan=2|3.2.2||colspan=2|-||3.3.3&lt;br /&gt;
|- &lt;br /&gt;
| Gromacs ||colspan=2|4.5.4 ||colspan=2|4.5.5||colspan=2|-||4.6.2&lt;br /&gt;
|- &lt;br /&gt;
| GUILE ||colspan=2|1.8.5 ||colspan=2|-||colspan=2|-||1.8.8&lt;br /&gt;
|- &lt;br /&gt;
| HDF5 ||colspan=2|1.8.7||colspan=2|-||colspan=2|1.8.17||-&lt;br /&gt;
|- &lt;br /&gt;
| LAMMPS ||colspan=4|-||16.05.14 (Phi)||16.05.14 (GPU, Phi)||-&lt;br /&gt;
|-&lt;br /&gt;
| Lapack ||colspan=2|3.2.2 ||colspan=2|3 ||3.6.1 ||colspan=2|-&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.maplesoft.com/ Maple] ||colspan=2| - ||colspan=2|18||-||2015||-&lt;br /&gt;
|- &lt;br /&gt;
| [https://wiki.niif.hu/index.php?title=NIIF_szuperszámítógépek_használata#Matlab_feladatok Matlab] ||colspan=2|7.13.0 (R2011b)|| colspan=2|-||-||R2015b||-&lt;br /&gt;
|- &lt;br /&gt;
| NumPy ||colspan=2|1,3||colspan=2|1.5.1||colspan=2|-||1.7.0&lt;br /&gt;
|- &lt;br /&gt;
| Octave ||colspan=2|3,4||colspan=2|3.0.5 ||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| Qt||colspan=2|3.3.8b, 4.6.2||colspan=2|3.3.6 ||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| R ||2.8.1, 3.2.0, 3.2.5(Intel) || 2.8.1 ||colspan=2|-||colspan=2|-||3.0.1&lt;br /&gt;
|- &lt;br /&gt;
| Scalapack||colspan=4|1.7.5||colspan=2|2.0.2||-&lt;br /&gt;
|- &lt;br /&gt;
| Scilab ||colspan=2|5.3.2||colspan=2|-||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| Sun JRE ||colspan=2|1,6||colspan=2|-||colspan=1|-||1.8||1.7&lt;br /&gt;
|- &lt;br /&gt;
| Gaussian||colspan=3|09_REV_C.01||-||-||colspan=2|09_REV_D.01&lt;br /&gt;
|- &lt;br /&gt;
| colspan=8|'''Middleware'''&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.nordugrid.org/arc/ ARC grid middleware] ||colspan=7|4.1.0 (A-REX, ARIS, clients))&lt;br /&gt;
|- &lt;br /&gt;
| colspan=8|'''Miscellaneous software'''&lt;br /&gt;
|- &lt;br /&gt;
| Emacs ||colspan=2|22,3||colspan=2|21,4||-||23.1.1||22.3.1&lt;br /&gt;
|- &lt;br /&gt;
| GNU Plot ||colspan=2|4.2.3 ||colspan=2|4||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| ImageMagick ||colspan=2|6.4.3 ||colspan=2|6.2.8||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.virtualgl.org/About/TigerVNC Turbo VNC]||colspan=4|1,1||colspan=3|2&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.blender.org/ Blender]||colspan=3|2,49||-||colspan=2|2.49||-&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.virtualgl.org/ VirtualGL]||colspan=3|2.3.2||-||colspan=2|2.4||-&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.paraview.org/ Paraview]||colspan=3|3.14.1||-||colspan=3|3.14.1&lt;br /&gt;
|- &lt;br /&gt;
| ffmpeg||colspan=2|-||colspan=2|0.4.9||colspan=3|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
[[Kategória: HPC]]&lt;/div&gt;</summary>
		<author><name>Szigeti(AT)niif.hu</name></author>	</entry>

	<entry>
		<id>https://wiki.niif.hu/index.php?title=HPC_software&amp;diff=3980</id>
		<title>HPC software</title>
		<link rel="alternate" type="text/html" href="https://wiki.niif.hu/index.php?title=HPC_software&amp;diff=3980"/>
				<updated>2016-11-07T15:02:56Z</updated>
		
		<summary type="html">&lt;p&gt;Szigeti(AT)niif.hu: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The current version of software on specific machines might be higher than described here. We are encouraging all users to install other tools to be used on our servers if service AUP and license restrictions are respected.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
!Software component&lt;br /&gt;
!Debrecen&lt;br /&gt;
!Pécs&lt;br /&gt;
!Szeged&lt;br /&gt;
!Budapest&lt;br /&gt;
!Budapest2&lt;br /&gt;
!Debrecen2,3 (Leo, Apollo)&lt;br /&gt;
!Miskolc&lt;br /&gt;
|-&lt;br /&gt;
| colspan=8| '''Compiler environment (compiler, debugger, profiler)'''&lt;br /&gt;
|-&lt;br /&gt;
| AMD [http://developer.amd.com/tools-and-sdks/cpu-development/x86-open64-compiler-suite/ compilers ((opencc, openf90, openf95))] || colspan=2|-||colspan=2|Available||colspan=3|-&lt;br /&gt;
|-&lt;br /&gt;
| Math. Library (AMD) [http://developer.amd.com/tools/cpu-development/amd-core-math-library-acml ACML] ||colspan=2|-||colspan=2|4.2.4||colspan=3|-&lt;br /&gt;
|-&lt;br /&gt;
| [http://sources.redhat.com/gdb/ GDB] || colspan=2|7|| colspan=2|7.0.1|| colspan=2|7.2||7.5&lt;br /&gt;
|-&lt;br /&gt;
| [http://gcc.gnu.org/ GNU compilers (gcc, g++, gfortran)]||4,3||4.1.2 || colspan=2|4.1.2, 4.4.4|| colspan=2|4.4.7|| 4.8.5&lt;br /&gt;
|-&lt;br /&gt;
| Gprof|| colspan=2|2,2|| colspan=2|2,17|| colspan=2|2.20|| 2.26.1&lt;br /&gt;
|-&lt;br /&gt;
| Intel [http://software.intel.com/en-us/articles/intel-composer-xe/ compilers (icc, ifort)], [http://software.intel.com/en-us/articles/intel-math-kernel-library-documentation/ MKL],  [http://software.intel.com/en-us/articles/intel-mkl-link-line-advisor/ Linking]  ||colspan=2|12.1.5 (composerxe-2011 Update 11) ||colspan=2|-|| colspan=3|16.0&lt;br /&gt;
|- &lt;br /&gt;
| [http://oprofile.sourceforge.net/ Oprofile] ||  colspan=3|- ||0.9.4||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| colspan=8|'''Libraries supporting parallel development'''&lt;br /&gt;
|- &lt;br /&gt;
| LAM-MPI ||colspan=2|- ||colspan=2|7.1.2||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| MPICH2 ||colspan=2|1,4||colspan=2|1,2||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| [http://openmp.org/ OpenMP]||colspan=2|2.5 (GNU), 3.0 (Intel)||colspan=2|2.5 (GNU), 2.5 (AMD) ||colspan=3|2.5 (GNU), 3.0 (Intel)&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.open-mpi.org/ OpenMPI] ||1.4.2 ||1.3.2 ||colspan=2|1.4.3 || colspan=2|1.8.5||1.6.3&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.csm.ornl.gov/pvm/ PVM] ||colspan=4|3.4.5 ||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| [http://videotorium.hu/hu/recordings/details/12291,Overview_and_usage_of_the_SGI_HPC_softwares_on_the_SGI_ICE_and_SGI_UV_supercomputers SGI Perf. Suite] ||colspan=2|Available||colspan=2|-||colspan=2|-||Available&lt;br /&gt;
|- &lt;br /&gt;
| SGI-MPI (MPT) ||colspan=2|2,04||colspan=2|-||colspan=2|-||2.12&lt;br /&gt;
|- &lt;br /&gt;
| PGI Compiler and Tools ||colspan=5|-|| 15.10 || -&lt;br /&gt;
|- &lt;br /&gt;
| colspan=8|'''Libraries supporting scientific work'''&lt;br /&gt;
|- &lt;br /&gt;
| AmberTool || 16 ||colspan=6|-&lt;br /&gt;
|-&lt;br /&gt;
| Atlas ||colspan=4|3.8.3||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| BLACS ||colspan=4|1.1 (OpenMPI, MPICH2) ||-||3.1.3||-&lt;br /&gt;
|- &lt;br /&gt;
| BLAS ||colspan=2|3.2.2 ||colspan=2|3||colspan=2|-||3&lt;br /&gt;
|- &lt;br /&gt;
| Boost||colspan=2|1,36||colspan=2|1,33||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| FFTW ||colspan=2|3.1.2 ||colspan=2|3.2.2||colspan=2|-||3.3.3&lt;br /&gt;
|- &lt;br /&gt;
| Gromacs ||colspan=2|4.5.4 ||colspan=2|4.5.5||colspan=2|-||4.6.2&lt;br /&gt;
|- &lt;br /&gt;
| GUILE ||colspan=2|1.8.5 ||colspan=2|-||colspan=2|-||1.8.8&lt;br /&gt;
|- &lt;br /&gt;
| HDF5 ||colspan=2|1.8.7||colspan=2|-||colspan=2|1.8.17||-&lt;br /&gt;
|- &lt;br /&gt;
| LAMMPS ||colspan=4|-||16.05.14 (Phi)||16.05.14 (GPU, Phi)||-&lt;br /&gt;
|-&lt;br /&gt;
| Lapack ||colspan=2|3.2.2 ||colspan=2|3 ||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.maplesoft.com/ Maple] ||colspan=2| - ||colspan=2|18||-||2015||-&lt;br /&gt;
|- &lt;br /&gt;
| [https://wiki.niif.hu/index.php?title=NIIF_szuperszámítógépek_használata#Matlab_feladatok Matlab] ||colspan=2|7.13.0 (R2011b)|| colspan=2|-||-||R2015b||-&lt;br /&gt;
|- &lt;br /&gt;
| NumPy ||colspan=2|1,3||colspan=2|1.5.1||colspan=2|-||1.7.0&lt;br /&gt;
|- &lt;br /&gt;
| Octave ||colspan=2|3,4||colspan=2|3.0.5 ||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| Qt||colspan=2|3.3.8b, 4.6.2||colspan=2|3.3.6 ||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| R ||2.8.1, 3.2.0, 3.2.5(Intel) || 2.8.1 ||colspan=2|-||colspan=2|-||3.0.1&lt;br /&gt;
|- &lt;br /&gt;
| Scalapack||colspan=4|1.7.5||-||2.0.2||-&lt;br /&gt;
|- &lt;br /&gt;
| Scilab ||colspan=2|5.3.2||colspan=2|-||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| Sun JRE ||colspan=2|1,6||colspan=2|-||colspan=1|-||1.8||1.7&lt;br /&gt;
|- &lt;br /&gt;
| Gaussian||colspan=3|09_REV_C.01||-||-||colspan=2|09_REV_D.01&lt;br /&gt;
|- &lt;br /&gt;
| colspan=8|'''Middleware'''&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.nordugrid.org/arc/ ARC grid middleware] ||colspan=7|4.1.0 (A-REX, ARIS, clients))&lt;br /&gt;
|- &lt;br /&gt;
| colspan=8|'''Miscellaneous software'''&lt;br /&gt;
|- &lt;br /&gt;
| Emacs ||colspan=2|22,3||colspan=2|21,4||-||23.1.1||22.3.1&lt;br /&gt;
|- &lt;br /&gt;
| GNU Plot ||colspan=2|4.2.3 ||colspan=2|4||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| ImageMagick ||colspan=2|6.4.3 ||colspan=2|6.2.8||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.virtualgl.org/About/TigerVNC Turbo VNC]||colspan=4|1,1||colspan=3|2&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.blender.org/ Blender]||colspan=3|2,49||-||colspan=2|2.49||-&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.virtualgl.org/ VirtualGL]||colspan=3|2.3.2||-||colspan=2|2.4||-&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.paraview.org/ Paraview]||colspan=3|3.14.1||-||colspan=3|3.14.1&lt;br /&gt;
|- &lt;br /&gt;
| ffmpeg||colspan=2|-||colspan=2|0.4.9||colspan=3|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
[[Kategória: HPC]]&lt;/div&gt;</summary>
		<author><name>Szigeti(AT)niif.hu</name></author>	</entry>

	<entry>
		<id>https://wiki.niif.hu/index.php?title=HPC_software&amp;diff=3979</id>
		<title>HPC software</title>
		<link rel="alternate" type="text/html" href="https://wiki.niif.hu/index.php?title=HPC_software&amp;diff=3979"/>
				<updated>2016-11-07T14:59:46Z</updated>
		
		<summary type="html">&lt;p&gt;Szigeti(AT)niif.hu: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The current version of software on specific machines might be higher than described here. We are encouraging all users to install other tools to be used on our servers if service AUP and license restrictions are respected.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
!Software component&lt;br /&gt;
!Debrecen&lt;br /&gt;
!Pécs&lt;br /&gt;
!Szeged&lt;br /&gt;
!Budapest&lt;br /&gt;
!Budapest2&lt;br /&gt;
!Debrecen2,3 (Leo, Apollo)&lt;br /&gt;
!Miskolc&lt;br /&gt;
|-&lt;br /&gt;
| colspan=8| '''Compiler environment (compiler, debugger, profiler)'''&lt;br /&gt;
|-&lt;br /&gt;
| AMD [http://developer.amd.com/tools-and-sdks/cpu-development/x86-open64-compiler-suite/ compilers ((opencc, openf90, openf95))] || colspan=2|-||colspan=2|Available||colspan=3|-&lt;br /&gt;
|-&lt;br /&gt;
| Math. Library (AMD) [http://developer.amd.com/tools/cpu-development/amd-core-math-library-acml ACML] ||colspan=2|-||colspan=2|4.2.4||colspan=3|-&lt;br /&gt;
|-&lt;br /&gt;
| [http://sources.redhat.com/gdb/ GDB] || colspan=2|7|| colspan=2|7.0.1|| colspan=2|7.2||7.5&lt;br /&gt;
|-&lt;br /&gt;
| [http://gcc.gnu.org/ GNU compilers (gcc, g++, gfortran)]||4,3||4.1.2 || colspan=2|4.1.2, 4.4.4|| colspan=2|4.4.7|| 4.3.4&lt;br /&gt;
|-&lt;br /&gt;
| Gprof|| colspan=2|2,2|| colspan=2|2,17|| colspan=2|2.20|| 2.23&lt;br /&gt;
|-&lt;br /&gt;
| Intel [http://software.intel.com/en-us/articles/intel-composer-xe/ compilers (icc, ifort)], [http://software.intel.com/en-us/articles/intel-math-kernel-library-documentation/ MKL],  [http://software.intel.com/en-us/articles/intel-mkl-link-line-advisor/ Linking]  ||colspan=2|12.1.5 (composerxe-2011 Update 11) ||colspan=2|-|| colspan=3|16.0&lt;br /&gt;
|- &lt;br /&gt;
| [http://oprofile.sourceforge.net/ Oprofile] ||  colspan=3|- ||0.9.4||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| colspan=8|'''Libraries supporting parallel development'''&lt;br /&gt;
|- &lt;br /&gt;
| LAM-MPI ||colspan=2|- ||colspan=2|7.1.2||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| MPICH2 ||colspan=2|1,4||colspan=2|1,2||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| [http://openmp.org/ OpenMP]||colspan=2|2.5 (GNU), 3.0 (Intel)||colspan=2|2.5 (GNU), 2.5 (AMD) ||colspan=3|2.5 (GNU), 3.0 (Intel)&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.open-mpi.org/ OpenMPI] ||1.4.2 ||1.3.2 ||colspan=2|1.4.3 || colspan=2|1.8.5||1.6.3&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.csm.ornl.gov/pvm/ PVM] ||colspan=4|3.4.5 ||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| [http://videotorium.hu/hu/recordings/details/12291,Overview_and_usage_of_the_SGI_HPC_softwares_on_the_SGI_ICE_and_SGI_UV_supercomputers SGI Perf. Suite] ||colspan=2|Available||colspan=2|-||colspan=2|-||Available&lt;br /&gt;
|- &lt;br /&gt;
| SGI-MPI (MPT) ||colspan=2|2,04||colspan=2|-||colspan=2|-||2.12&lt;br /&gt;
|- &lt;br /&gt;
| PGI Compiler and Tools ||colspan=5|-|| 15.10 || -&lt;br /&gt;
|- &lt;br /&gt;
| colspan=8|'''Libraries supporting scientific work'''&lt;br /&gt;
|- &lt;br /&gt;
| AmberTool || 16 ||colspan=6|-&lt;br /&gt;
|-&lt;br /&gt;
| Atlas ||colspan=4|3.8.3||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| BLACS ||colspan=4|1.1 (OpenMPI, MPICH2) ||-||3.1.3||-&lt;br /&gt;
|- &lt;br /&gt;
| BLAS ||colspan=2|3.2.2 ||colspan=2|3||colspan=2|-||3&lt;br /&gt;
|- &lt;br /&gt;
| Boost||colspan=2|1,36||colspan=2|1,33||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| FFTW ||colspan=2|3.1.2 ||colspan=2|3.2.2||colspan=2|-||3.3.3&lt;br /&gt;
|- &lt;br /&gt;
| Gromacs ||colspan=2|4.5.4 ||colspan=2|4.5.5||colspan=2|-||4.6.2&lt;br /&gt;
|- &lt;br /&gt;
| GUILE ||colspan=2|1.8.5 ||colspan=2|-||colspan=2|-||1.8.8&lt;br /&gt;
|- &lt;br /&gt;
| HDF5 ||colspan=2|1.8.7||colspan=2|-||colspan=2|1.8.17||-&lt;br /&gt;
|- &lt;br /&gt;
| LAMMPS ||colspan=4|-||16.05.14 (Phi)||16.05.14 (GPU, Phi)||-&lt;br /&gt;
|-&lt;br /&gt;
| Lapack ||colspan=2|3.2.2 ||colspan=2|3 ||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.maplesoft.com/ Maple] ||colspan=2| - ||colspan=2|18||-||2015||-&lt;br /&gt;
|- &lt;br /&gt;
| [https://wiki.niif.hu/index.php?title=NIIF_szuperszámítógépek_használata#Matlab_feladatok Matlab] ||colspan=2|7.13.0 (R2011b)|| colspan=2|-||-||R2015b||-&lt;br /&gt;
|- &lt;br /&gt;
| NumPy ||colspan=2|1,3||colspan=2|1.5.1||colspan=2|-||1.7.0&lt;br /&gt;
|- &lt;br /&gt;
| Octave ||colspan=2|3,4||colspan=2|3.0.5 ||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| Qt||colspan=2|3.3.8b, 4.6.2||colspan=2|3.3.6 ||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| R ||2.8.1, 3.2.0, 3.2.5(Intel) || 2.8.1 ||colspan=2|-||colspan=2|-||3.0.1&lt;br /&gt;
|- &lt;br /&gt;
| Scalapack||colspan=4|1.7.5||-||2.0.2||-&lt;br /&gt;
|- &lt;br /&gt;
| Scilab ||colspan=2|5.3.2||colspan=2|-||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| Sun JRE ||colspan=2|1,6||colspan=2|-||colspan=1|-||1.8||1.7&lt;br /&gt;
|- &lt;br /&gt;
| Gaussian||colspan=3|09_REV_C.01||-||-||colspan=2|09_REV_D.01&lt;br /&gt;
|- &lt;br /&gt;
| colspan=8|'''Middleware'''&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.nordugrid.org/arc/ ARC grid middleware] ||colspan=7|4.1.0 (A-REX, ARIS, clients))&lt;br /&gt;
|- &lt;br /&gt;
| colspan=8|'''Miscellaneous software'''&lt;br /&gt;
|- &lt;br /&gt;
| Emacs ||colspan=2|22,3||colspan=2|21,4||-||23.1.1||22.3.1&lt;br /&gt;
|- &lt;br /&gt;
| GNU Plot ||colspan=2|4.2.3 ||colspan=2|4||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| ImageMagick ||colspan=2|6.4.3 ||colspan=2|6.2.8||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.virtualgl.org/About/TigerVNC Turbo VNC]||colspan=4|1,1||colspan=3|2&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.blender.org/ Blender]||colspan=3|2,49||-||colspan=2|2.49||-&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.virtualgl.org/ VirtualGL]||colspan=3|2.3.2||-||colspan=2|2.4||-&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.paraview.org/ Paraview]||colspan=3|3.14.1||-||colspan=3|3.14.1&lt;br /&gt;
|- &lt;br /&gt;
| ffmpeg||colspan=2|-||colspan=2|0.4.9||colspan=3|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
[[Kategória: HPC]]&lt;/div&gt;</summary>
		<author><name>Szigeti(AT)niif.hu</name></author>	</entry>

	<entry>
		<id>https://wiki.niif.hu/index.php?title=HPC_software&amp;diff=3978</id>
		<title>HPC software</title>
		<link rel="alternate" type="text/html" href="https://wiki.niif.hu/index.php?title=HPC_software&amp;diff=3978"/>
				<updated>2016-11-07T14:59:14Z</updated>
		
		<summary type="html">&lt;p&gt;Szigeti(AT)niif.hu: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The current version of software on specific machines might be higher than described here. We are encouraging all users to install other tools to be used on our servers if service AUP and license restrictions are respected.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
!Software component&lt;br /&gt;
!Debrecen&lt;br /&gt;
!Pécs&lt;br /&gt;
!Szeged&lt;br /&gt;
!Budapest&lt;br /&gt;
!Budapest2&lt;br /&gt;
!Debrecen2,3 (Leo, Apollo)&lt;br /&gt;
!Miskolc&lt;br /&gt;
|-&lt;br /&gt;
| colspan=8| '''Compiler environment (compiler, debugger, profiler)'''&lt;br /&gt;
|-&lt;br /&gt;
| AMD [http://developer.amd.com/tools-and-sdks/cpu-development/x86-open64-compiler-suite/ compilers ((opencc, openf90, openf95))] || colspan=2|-||colspan=2|Available||colspan=3|-&lt;br /&gt;
|-&lt;br /&gt;
| Math. Library (AMD) [http://developer.amd.com/tools/cpu-development/amd-core-math-library-acml ACML] ||colspan=2|-||colspan=2|4.2.4||colspan=3|-&lt;br /&gt;
|-&lt;br /&gt;
| [http://sources.redhat.com/gdb/ GDB] || colspan=2|7|| colspan=2|7.0.1|| colspan=2|7.2||7.5&lt;br /&gt;
|-&lt;br /&gt;
| [http://gcc.gnu.org/ GNU compilers (gcc, g++, gfortran)]||4,3||4.1.2 || colspan=2|4.1.2, 4.4.4|| colspan=2|4.4.7|| 4.3.4&lt;br /&gt;
|-&lt;br /&gt;
| Gprof|| colspan=2|2,2|| colspan=2|2,17|| colspan=2|2.20|| 2.23&lt;br /&gt;
|-&lt;br /&gt;
| Intel [http://software.intel.com/en-us/articles/intel-composer-xe/ compilers (icc, ifort)], [http://software.intel.com/en-us/articles/intel-math-kernel-library-documentation/ MKL],  [http://software.intel.com/en-us/articles/intel-mkl-link-line-advisor/ Linking]  ||colspan=2|12.1.5 (composerxe-2011 Update 11) ||colspan=2|-|| colspan=3|16.0&lt;br /&gt;
|- &lt;br /&gt;
| [http://oprofile.sourceforge.net/ Oprofile] ||  colspan=3|- ||0.9.4||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| colspan=8|'''Libraries supporting parallel development'''&lt;br /&gt;
|- &lt;br /&gt;
| LAM-MPI ||colspan=2|- ||colspan=2|7.1.2||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| MPICH2 ||colspan=2|1,4||colspan=2|1,2||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| [http://openmp.org/ OpenMP]||colspan=2|2.5 (GNU), 3.0 (Intel)||colspan=2|2.5 (GNU), 2.5 (AMD) ||colspan=3|2.5 (GNU), 3.0 (Intel)&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.open-mpi.org/ OpenMPI] ||1.4.2 ||1.3.2 ||colspan=2|1.4.3 || colspan=2|1.8.5||1.6.3&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.csm.ornl.gov/pvm/ PVM] ||colspan=4|3.4.5 ||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| [http://videotorium.hu/hu/recordings/details/12291,Overview_and_usage_of_the_SGI_HPC_softwares_on_the_SGI_ICE_and_SGI_UV_supercomputers SGI Perf. Suite] ||colspan=2|Available||colspan=2|-||colspan=2|-||Available&lt;br /&gt;
|- &lt;br /&gt;
| SGI-MPI (MPT) ||colspan=2|2,04||colspan=2|-||colspan=2|-||2.12&lt;br /&gt;
|- &lt;br /&gt;
| PGI Compiler and Tools ||colspan=5|-|| 15.10 || -&lt;br /&gt;
|- &lt;br /&gt;
| colspan=8|'''Libraries supporting scientific work'''&lt;br /&gt;
|- &lt;br /&gt;
| AmberTool || 16 ||colspan=6|-&lt;br /&gt;
|-&lt;br /&gt;
| Atlas ||colspan=4|3.8.3||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| BLACS ||colspan=4|1.1 (OpenMPI, MPICH2) ||-||3.1.3||-&lt;br /&gt;
|- &lt;br /&gt;
| BLAS ||colspan=2|3.2.2 ||colspan=2|3||colspan=2|-||3&lt;br /&gt;
|- &lt;br /&gt;
| Boost||colspan=2|1,36||colspan=2|1,33||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| FFTW ||colspan=2|3.1.2 ||colspan=2|3.2.2||colspan=2|-||3.3.3&lt;br /&gt;
|- &lt;br /&gt;
| Gromacs ||colspan=2|4.5.4 ||colspan=2|4.5.5||colspan=2|-||4.6.2&lt;br /&gt;
|- &lt;br /&gt;
| GUILE ||colspan=2|1.8.5 ||colspan=2|-||colspan=2|-||1.8.8&lt;br /&gt;
|- &lt;br /&gt;
| HDF5 ||colspan=2|1.8.7||colspan=2|-||colspan=2|1.8.17||-&lt;br /&gt;
|- &lt;br /&gt;
| LAMMPS ||colspan=4|-||16.05.14 (Phi)||16.05.14 (GPU, Phi)||-&lt;br /&gt;
|-&lt;br /&gt;
| Lapack ||colspan=2|3.2.2 ||colspan=2|3 ||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.maplesoft.com/ Maple] ||colspan=2| - ||colspan=2|18||-||2015||-&lt;br /&gt;
|- &lt;br /&gt;
| [https://wiki.niif.hu/index.php?title=NIIF_szuperszámítógépek_használata#Matlab_feladatok Matlab] ||colspan=2|7.13.0 (R2011b)|| colspan=2|-||-||R2015b||-&lt;br /&gt;
|- &lt;br /&gt;
| NumPy ||colspan=2|1,3||colspan=2|1.5.1||colspan=2|-||1.7.0&lt;br /&gt;
|- &lt;br /&gt;
| Octave ||colspan=2|3,4||colspan=2|3.0.5 ||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| Qt||colspan=2|3.3.8b, 4.6.2||colspan=2|3.3.6 ||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| R ||2.8.1, 3.2.0, 3.2.5(intel) || 2.8.1 ||colspan=2|-||colspan=2|-||3.0.1&lt;br /&gt;
|- &lt;br /&gt;
| Scalapack||colspan=4|1.7.5||-||2.0.2||-&lt;br /&gt;
|- &lt;br /&gt;
| Scilab ||colspan=2|5.3.2||colspan=2|-||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| Sun JRE ||colspan=2|1,6||colspan=2|-||colspan=1|-||1.8||1.7&lt;br /&gt;
|- &lt;br /&gt;
| Gaussian||colspan=3|09_REV_C.01||-||-||colspan=2|09_REV_D.01&lt;br /&gt;
|- &lt;br /&gt;
| colspan=8|'''Middleware'''&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.nordugrid.org/arc/ ARC grid middleware] ||colspan=7|4.1.0 (A-REX, ARIS, clients))&lt;br /&gt;
|- &lt;br /&gt;
| colspan=8|'''Miscellaneous software'''&lt;br /&gt;
|- &lt;br /&gt;
| Emacs ||colspan=2|22,3||colspan=2|21,4||-||23.1.1||22.3.1&lt;br /&gt;
|- &lt;br /&gt;
| GNU Plot ||colspan=2|4.2.3 ||colspan=2|4||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| ImageMagick ||colspan=2|6.4.3 ||colspan=2|6.2.8||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.virtualgl.org/About/TigerVNC Turbo VNC]||colspan=4|1,1||colspan=3|2&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.blender.org/ Blender]||colspan=3|2,49||-||colspan=2|2.49||-&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.virtualgl.org/ VirtualGL]||colspan=3|2.3.2||-||colspan=2|2.4||-&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.paraview.org/ Paraview]||colspan=3|3.14.1||-||colspan=3|3.14.1&lt;br /&gt;
|- &lt;br /&gt;
| ffmpeg||colspan=2|-||colspan=2|0.4.9||colspan=3|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
[[Kategória: HPC]]&lt;/div&gt;</summary>
		<author><name>Szigeti(AT)niif.hu</name></author>	</entry>

	<entry>
		<id>https://wiki.niif.hu/index.php?title=Intel_Xeon_Phi&amp;diff=3962</id>
		<title>Intel Xeon Phi</title>
		<link rel="alternate" type="text/html" href="https://wiki.niif.hu/index.php?title=Intel_Xeon_Phi&amp;diff=3962"/>
				<updated>2016-09-27T19:17:01Z</updated>
		
		<summary type="html">&lt;p&gt;Szigeti(AT)niif.hu: /* Offload */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Az oldalon található információk segítséget nyújtanak a HPC felhasználóinknak, hogy alkalmazásaikat miként portolhatják valamint optimalizálhatják a hatékonyabb futást szem előtt tartva Intel Xeon Phi koprocesszor kártyákon.&lt;br /&gt;
&lt;br /&gt;
{{INFO|Egy 10 alkalmas, angol nyelvű Xeon Phi programozás tutoriál videóanyaggal és példaprogrammal elérhető a [[http://colfaxresearch.com/how-16-04/ Colfax oldalán]]}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Hardver bemutatása==&lt;br /&gt;
Intel® Xeon Phi™ Coprocessor 7120P&lt;br /&gt;
&amp;lt;gallery&amp;gt;&lt;br /&gt;
Fájl:Xeon-phi-family-rwd.png.rendition.intel.web.720.405.png|&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-  align=&amp;quot;center&amp;quot;&lt;br /&gt;
! CPU&lt;br /&gt;
! threads/Core&lt;br /&gt;
!max threads&lt;br /&gt;
! CPU Freq&lt;br /&gt;
! Memória&lt;br /&gt;
! L1 cache&lt;br /&gt;
! L2 cache&lt;br /&gt;
! Interfész&lt;br /&gt;
! Ptot&lt;br /&gt;
! Számítási kapacitás&lt;br /&gt;
|-  align=&amp;quot;center&amp;quot;&lt;br /&gt;
| 61&lt;br /&gt;
| 4 Hardver&lt;br /&gt;
| 244&lt;br /&gt;
| 1238 GHz&lt;br /&gt;
| 16GB GDDR5&lt;br /&gt;
| 32KB/core&lt;br /&gt;
| 512KB/core&lt;br /&gt;
| PCIe&lt;br /&gt;
| 300W&lt;br /&gt;
| 1,2 TFlop/s dupla pontosság&lt;br /&gt;
|-  align=&amp;quot;center&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
| OpenMP-nél csak '''240'''&lt;br /&gt;
|&lt;br /&gt;
| ~100 cycles&lt;br /&gt;
~174 GB/s&lt;br /&gt;
| ~3 cycles&lt;br /&gt;
| ~10 cycles&lt;br /&gt;
| ~7 GB/s&lt;br /&gt;
~1 microsecond&lt;br /&gt;
|&lt;br /&gt;
| 2,4 TFlop/s egyszeres pontosság&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Az Intel® 1. generációs Xeon Phi™ koprocesszorában található 61 processzor ugyanazt a 16GB memóriát éri el, de semmilyen módon se fér hozzá közvetlenül a hoszt gép fizikai memóriájához.&amp;lt;br /&amp;gt;&lt;br /&gt;
[[Budapest2_klaszter|Budapest2]] és [[HPC#Szupersz.C3.A1m.C3.ADt.C3.B3g.C3.A9peink_.C3.B6sszehasonl.C3.ADt.C3.A1sa|Debrecen3-Phi]] gépeinkben számítási nodonként 2-2 Phi™ koprocesszor található. &amp;lt;br/&amp;gt;&lt;br /&gt;
A kártyákon speciális célú Linux kernel fut melyre a belépés limitáltan csak a rendszergazdák számára lehetséges SSH-n keresztül a hoszt gépről. Természetesen a kártyára belépve olyan érzésünk lehet, mintha egy sok processzoros gépen dolgoznánk, ami rendelkezik rengeteg memóriával. Ez így is van, de a hatékonyabb kihasználás érdekében az SSH-n keresztüli elérés nem lehetséges, csak a Slurm segítségével valamint a programozási nyelvek által támogatott csatornákon keresztül. Ennek részletezése lentebb megtalálható.&lt;br /&gt;
&lt;br /&gt;
==Használati módok==&lt;br /&gt;
A koprocesszor használatai módjait két csoportba lehet sorolni. A '''natív''' és az '''offload''' használatot.  &amp;lt;br/&amp;gt;&lt;br /&gt;
Ezen használati módokról a lenti fejezetekben több részletet is megtudhat. Az itt megfogalmazott &amp;quot;támogatás&amp;quot; csak a mi infrastruktúránkra érvényes! &amp;lt;br /&amp;gt;&lt;br /&gt;
A bemutatott példák ''c++'' nyelven vannak, de természetesen ''c'' és ''fortran'' esetén is megtalálható az ekvivalens módszer.&lt;br /&gt;
&lt;br /&gt;
===a) nem támogatott mód===&lt;br /&gt;
====közvetlen natív használat====&lt;br /&gt;
A natív mód arra szolgál, hogy a MIC kártyára  lefordított alkalmazásunkat felmásoljuk a kártyára, majd belépünk SSH-n a kártyára és azon futtatjuk a bináris kódunkat. &amp;lt;br /&amp;gt;&lt;br /&gt;
Biztonsági okokból a közvetlen natív használat az NIIF HPC infrastruktúrán nem elérhető. &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===b) támogatott, de nem preferált használat===&lt;br /&gt;
====automatikus kódátvitel====&lt;br /&gt;
Ebben az esetben nem kell átmásolnunk a bináris alkalmazásunkat a kártyára sőt még csak be se kell lépnünk, mert ezt a két lépést egy beépített tool végzi el. &amp;lt;br/&amp;gt;&lt;br /&gt;
A tool eléréséhez szükséges parancs neve: '''micnativeloadex'''&lt;br /&gt;
&lt;br /&gt;
Ebben az esetben a workflow a következőképpen módosul:&lt;br /&gt;
# Alkalmazás megírása. például [[Média:hello.cc|''hello.cc'']]&lt;br /&gt;
# Alkalmazás fordítása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;icpc -o hello-MIC -mmic hello.cc&amp;lt;/pre&amp;gt;&lt;br /&gt;
# A tool futásához szükséges library-k megadása &amp;lt;br&amp;gt; &amp;lt;pre&amp;gt;export SINK_LD_LIBRARY_PATH=/opt/intel/composerxe/compiler/lib/mic&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Alkalmazás futtatása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;micnativeloadex hello-MIC&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| BUDAPEST2[cn10] phi (0)$ export SINK_LD_LIBRARY_PATH=/opt/intel/composerxe/compiler/lib/mic &amp;lt;br/&amp;gt;BUDAPEST2[cn10] phi (0)$ micnativeloadex hello-MIC &amp;lt;br/&amp;gt;Hello world! I have 244 logical cores.&amp;lt;br/&amp;gt;BUDAPEST2[cn10] phi (0)$&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===c) támogatott módok===&lt;br /&gt;
====Offload====&lt;br /&gt;
Az Offload modell esetén szükséges a forráskód módosítása, mégpedig úgy, hogy azon kódrészletet, amit a koprocesszoron szeretnénk futtatni, ki kell egészíteni egy fordítási direktívával. Az alkalmazás fordításakor elegendő csak a CPU-ra való binárist készíteni mivel a modell végzi a megfelelő forrás részlet mozgatását és futtatását.&amp;lt;br /&amp;gt;&lt;br /&gt;
[[Fájl:offload.png|Offload modell]] &amp;lt;br /&amp;gt;&lt;br /&gt;
Példánkban jól látható, hogy a második kiíratás már a koprocesszoron fut.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
int main(int argc, char * argv[]) {&lt;br /&gt;
  printf(&amp;quot;Hello world from host!\n&amp;quot;);&lt;br /&gt;
#pragma offload target(mic)&lt;br /&gt;
  {&lt;br /&gt;
    printf(&amp;quot;Hello world from coprocessor!\n&amp;quot;); fflush(stdout);&lt;br /&gt;
  }&lt;br /&gt;
  printf(&amp;quot;Bye\n&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ebben a példában az operációs rendszer választja ki, hogy melyik Phi kártyán fusson le a kódrészlet. Amennyiben mi szeretnénk explicite megmondani, hogy melyik kártyán fusson, akkor a fordítási direktívában a mic kulcsszó után kell tenni egy &amp;quot;:&amp;quot;-t és a kártya sorszámát [mi esetünkben 0 vagy 1]. Ekkor így nézne ki a direktíva: ''#pragma offload target(mic:1)''&lt;br /&gt;
&lt;br /&gt;
Amennyiben az offload szekcióban függvény hívás is szerepel, akkor szükségeltetik az függvény futásához szükséges változók adatterületének átmásolása a Phi kártyára, majd a lefutás után visszamásolni a hoszt gépre. Ezen másolást az ''offload_attribute'' ''push'' és ''pop'' paraméterei segítségével határozhatjuk meg. &amp;lt;br /&amp;gt;&lt;br /&gt;
A következő mátrix-vektor szorzás példánál az A mátrix globális változó, míg a b és c vektorok a függvény paraméterei.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//A változók felmásolásra kerülnek a Xeon Phi-re&lt;br /&gt;
#pragma offload_attribute(push, target(mic))&lt;br /&gt;
  double* A;&lt;br /&gt;
  void MatrixVector(double *b, double *c, double m, double n) {&lt;br /&gt;
    ...&lt;br /&gt;
    c[] = A[..]*b[]&lt;br /&gt;
  }&lt;br /&gt;
// felmásolt objektumok visszamásolása&lt;br /&gt;
#pragma offload_attribute(pop)&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
  ...&lt;br /&gt;
#pragma offload target(mic) in(A[0:n*m])&lt;br /&gt;
// így is meg lehetne adni: #pragma offload target(mic:0) in(A : length(n*m))	        &lt;br /&gt;
  {&lt;br /&gt;
    MatrixVector(b, c, m, n);&lt;br /&gt;
  }&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ekkor minden memória területet 2x kell másolni, egyszer fel a kártyára, majd másodszor vissza a hoszt gépre, pedig elegendő lenne csak az eredmény vektort visszamásolni. Ezen optimalizálással egy későbbi fejezetben foglalkozunk. &amp;lt;p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Futás során kaphatunk részletes riportot is, hogy pontosan milyen adatmozgatások történnek az alkalmazás futása során. Ehhez nincs szükség másra csak az '''OFFLOAD_REPORT''' környezeti változó megfelelő beállítása. A beállított értékek Automatic Offload esetén 1, 2, míg Compiler Assisted Offload esetén 1, 2 és 3 lehetnek, attól függően mennyire részletes riportot szeretnénk kapni. &amp;lt;br /&amp;gt;&lt;br /&gt;
beállítás:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export OFFLOAD_REPORT=3&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
példa kimenet a hello alkalmazásra:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Hello world from host!&lt;br /&gt;
[Offload] [MIC 0] [File]                    hello_offload.cc&lt;br /&gt;
[Offload] [MIC 0] [Line]                    5&lt;br /&gt;
[Offload] [MIC 0] [Tag]                     Tag 0&lt;br /&gt;
Hello world from coprocessor!&lt;br /&gt;
[Offload] [HOST]  [Tag 0] [CPU Time]        0.554653(seconds)&lt;br /&gt;
[Offload] [MIC 0] [Tag 0] [CPU-&amp;gt;MIC Data]   0 (bytes)&lt;br /&gt;
[Offload] [MIC 0] [Tag 0] [MIC Time]        0.000140(seconds)&lt;br /&gt;
[Offload] [MIC 0] [Tag 0] [MIC-&amp;gt;CPU Data]   0 (bytes)&lt;br /&gt;
Bye&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Offload + OpenMP====&lt;br /&gt;
Amennyiben nem csak 1 processzort szeretnénk használni a Phi kártyánkon és az alkalmazásunkban használt számítás megengedi a párhuzamosítást, akkor az OpenMP segítségével párhuzamosíthatjuk a megfelelő számítást. &amp;lt;br /&amp;gt;&lt;br /&gt;
[[Fájl:Offload+openmp.png|Offload modell és OpenMP]] &amp;lt;br /&amp;gt;&lt;br /&gt;
Példánk maradjon ugyanúgy a mátrix-vektor szorzás. Amennyiben a teljes kártya adta kapacitást ki szeretnénk használni, akkor az OMP threadek számát 240-re kell beállítani. Azért nem 244-re, mivel 1 CPU-t dedikáltan csak a kommunikációra használ a rendszer és 1 CPU 4 hardver threaddel rendelkezik.&lt;br /&gt;
Ekkor a példánk így változik:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;omp.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#pragma offload_attribute(push, target(mic))&lt;br /&gt;
void multiply(int n, int m, double* A, double* b, double* c){&lt;br /&gt;
  const int nCPUs = omp_get_max_threads();   //240&lt;br /&gt;
  omp_set_num_threads(nCPUs);&lt;br /&gt;
#pragma omp parallel for schedule(dynamic, 1)&lt;br /&gt;
  for ( int i = 0 ; i &amp;lt; m ; i++)&lt;br /&gt;
     for ( int j = 0 ; j &amp;lt; n ; j++)&lt;br /&gt;
       c[i] += A[i*n+j] * b[j];&lt;br /&gt;
}&lt;br /&gt;
#pragma offload_attribute(pop)&lt;br /&gt;
&lt;br /&gt;
int main(){&lt;br /&gt;
  double * A = (double*) malloc(sizeof(double)*n*m);&lt;br /&gt;
  ...&lt;br /&gt;
#pragma offload target(mic) in(A:length(n*m))&lt;br /&gt;
     multiply(n, m, A, b, c);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Az alkalmazás fordításánál a fordítónak meg kell adni, hogy használhja az openmp library-ket is. Ezt a '''-qopenmp''' kapcsolóval tudjuk elérni.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
icpc -o matrix-CPU -qopenmp matrix.cc&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====MPI + Offload + OpenMP====&lt;br /&gt;
Abban az esetben, ha 1 számítási node nem elég, MPI segítségével össze tudunk kötni több nodet, heterogén rendszert alkotva, és azokon alkalmazható a fenti Offload + OpenMP modell. &amp;lt;br /&amp;gt;&lt;br /&gt;
[[Fájl:Mpi+offload+openmp.png|MPI elosztás valamint Offload modell és OpenMP]] &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Phi használat slurm-mel==&lt;br /&gt;
Slurm esetén a mic opciót kell megadni a gres-knél. Amennyiben egynél több kártyára van szükség, akkor azt a ''mic:2''-vel lehetséges megadni. Ekkor két kártyát foglal le az ütemező a futtatandó feladatnak. &amp;lt;br /&amp;gt;&lt;br /&gt;
Egy lehetséges feladat, amit srun-nal lehet a slurmben beküldeni. debrecen3 esetén a ''prod-phi'' particiót kell használni&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ cat slurm_job_openmp_phi&lt;br /&gt;
&lt;br /&gt;
#!/bin/bash&lt;br /&gt;
#SBATCH -A &amp;lt;PROJECT NEVE&amp;gt;&lt;br /&gt;
#SBATCH --job-name=&amp;lt;JOB NEVE&amp;gt;&lt;br /&gt;
#SBATCH --gres mic:2&lt;br /&gt;
#SBATCH --time=1:00:00&lt;br /&gt;
#SBATCH --partition=prod&lt;br /&gt;
#SBATCH -o slurm-%A.out&lt;br /&gt;
export OMP_NUM_THREADS=2&lt;br /&gt;
./runme_openmp_2phi&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Ekkor a feladat beküldése a következő paranccsal lehetséges.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
srun ./slurm_job_openmp_phi&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Alkalmazások optimalizálása a kártyára==&lt;br /&gt;
Ebben a fejezetben optimalizálási lehetőségeket mutatunk be, melyek segítségül szolgálhatnak az egyes alkalmazások hatékonyabb futása érdekében ezzel jobban kihasználni a rendelkezésre álló erőforrások adta számítási kapacitás.&lt;br /&gt;
&lt;br /&gt;
===Környezeti változók===&lt;br /&gt;
Lehetséges csak a Phi kártyákon használt környezeti változók beállítása, amit az alkalmazásunk használni is tud. &amp;lt;br /&amp;gt;&lt;br /&gt;
Példánkban az OpenMP esetén használható szálak számát állíthatjuk be:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export MIC_ENV_PREFIX=XEONPHI&lt;br /&gt;
export OMP_NUM_THREADS=12&lt;br /&gt;
export XEONPHI_OMP_NUM_THREADS=240&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
A beállított prefix tetszőleges karaktersorozat lehet, a lényeg, hogy ezt a beállított prefixet használjuk a környezeti változók esetén is!&lt;br /&gt;
 &lt;br /&gt;
===Hosszredukció===&lt;br /&gt;
Többnyire a fordítók, ha nem mondjuk meg explicite, a dupla pontosságú változatot választják a lehetséges változó- és függvény típusok közül. Amennyiben a számításunk nem igényli a dupla pontosságot, akkor használjuk a forrásban mindenhol az adott típus vagy függvény egyszeres pontosságú változatát, mellyel jelentős teljesítmény növekedést tudunk elérni. Továbbá az egyes műveletek futási ideje nem egyforma, így a műveletet helyettesíthetjük más művelettel, ami gyorsabb, akkor ismét javíthatunk a hatékonyságon. &amp;lt;br /&amp;gt;&lt;br /&gt;
Íme pár példa, amit alkalmazni lehet:&lt;br /&gt;
* &amp;quot;''/''&amp;quot; művelet helyett &amp;quot;''*''&amp;quot; -t használjunk, mert azt gyorsabban végre tudja hajtani a processzor&lt;br /&gt;
* ''pow'' =&amp;gt; ''1.0 / sqrt()''    , tetszőleges hatványozás, pl.: 3/2-ik hatvány&lt;br /&gt;
* hardver áltatl támogatott műveletek:&lt;br /&gt;
** ''exp'' =&amp;gt; ''exp2''&lt;br /&gt;
** ''log'' =&amp;gt; ''log2''&lt;br /&gt;
* dupla pontosság -&amp;gt; egyszeres pontosság lebegőpontos számok és műveletek esetén &lt;br /&gt;
** ''1.0'' =&amp;gt; ''1.0f''&lt;br /&gt;
** ''pow'' =&amp;gt; ''powf''&lt;br /&gt;
&lt;br /&gt;
===Vektorizáció===&lt;br /&gt;
A vektorizáció azon fordítási időben eldönthető optimalizációs faktor, amely nagy mértékben függ attól a hardver felépítésétől, amire éppen a fordítás történni fog. Legfontosabb befolyásoló tényezők a processzor cache méretei illetve a vektor műveletekért felelős aritmetikai egységének jellemzői mivel ezeket használja fel a fordító az optimalizálási folyamata során. &amp;lt;br /&amp;gt;&lt;br /&gt;
Az automatikus vektorizáció minimum -O2 szinttől érvényes, de mivel általában ez a szint az alapértelmezett, ezért minden fordításnál alkalmazásra kerül. &amp;lt;br /&amp;gt;&lt;br /&gt;
A fordító programnak a következő kapcsolókat lehet megadni, melyek segítenek a minél jobb vektorizációban:&lt;br /&gt;
* ''-qopt-report=&amp;lt;szám&amp;gt;'' ; az optimalizációs jelentés részletességét lehet megadni 0-tól 5-ig, ahol az 5-s szint a legrészletesebbet jelenti&lt;br /&gt;
* ''-qopt-report-phase:vec'' ; a vektorizációról szeretnék jelentést kapni&lt;br /&gt;
* ''-opt-report-file=&amp;lt;fájlnév&amp;gt;'' ; megadható a riportfájl neve&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
icpc autovec.cc -mmic -qopt-report=5 -opt-report-file=vectorization.oMIC.optrpt -qopt-report-phase:vec&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====SIMD====&lt;br /&gt;
Az adatok hatékonyabb feldolgozására az egyik megvalósítás a SIMD (Single Instruction Multiple Data), amikor is a különálló adatokat úgy kezeli, mintha az vektorok volnának és azon végzi el a vektor-műveletet. Ezzel egy a hardver által támogatott műveletet végzünk egy lépésben egy nagyobb adatmennyiségen, ezzel párhuzamosítva a feldolgozási folyamatot. Ezzel a módszerrel nagyon jó adat szintű párhuzamosítást lehet elérni.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Fájl:Simd.png|SIMD]]&lt;br /&gt;
&lt;br /&gt;
Főbb támogatott műveletek:&lt;br /&gt;
* aritmetikai műveletek, bináris operátorok&lt;br /&gt;
* összehasonlítások&lt;br /&gt;
* konverziók és típus kasztolások&lt;br /&gt;
* bit-műveletek: NOT, AND, OR, XOR, XAND&lt;br /&gt;
&lt;br /&gt;
Használata: '''simd''' fordítási direktívával lehetséges &amp;lt;br /&amp;gt;&lt;br /&gt;
Például:&lt;br /&gt;
* egyszerű vektorizáció&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma simd&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
  A[i] += B[i];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* adat párhuzamosítás OpenMP segítségével&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for simd&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
  DoSomeWork(A[i]);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====AoS -&amp;gt; SoA====&lt;br /&gt;
A kiindulás probléma az, hogy a struktúrákat tartalmazó vektort nem lehet jól vektorizálni, mivel a cache-ben tárolt adatok nagy többsége feleslegesen van bent, mivel azon adatokat csak egy későbbi ciklus futás során fogja az alkalmazás feldolgozni. Tehát ezen adatok jelenleg feleslegesen foglalják a cache területet, ahelyett, hogy hasznos adatot tárolnánk ott.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
struct MyStructure {&lt;br /&gt;
  float x, y, z;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void My_Function(cons int nItem, MyStructure* const Items, const float delta) {&lt;br /&gt;
  for (int i = 0; i &amp;lt; nItem; i++) {&lt;br /&gt;
    ...&lt;br /&gt;
    for (int j = 0; j &amp;lt; nItem; j++) {&lt;br /&gt;
      const float dx = Items[j].x - Items[i].x;&lt;br /&gt;
      const float dy = Items[j].y - Items[i].y;&lt;br /&gt;
      const float dz = Items[j].z - Items[i].z;&lt;br /&gt;
      ...  &lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  ... &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A vektorban a struktúrák tárolása helyett minden elemnek egy dedikált vektort allokálunk és így könnyen tudja a fordító vektorizálni azt. Természetesen segítségként a vektor feldolgozását &amp;quot;tömbösítettük&amp;quot; a tileSize méret megadásával illetve a ''unroll'' fordítási direktíva segítségével.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
struct MyStructureSet {&lt;br /&gt;
  float *x, *y, *z;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void My_Function(cons int nItem, MyStructure* const Items, const float delta) {&lt;br /&gt;
  const int tileSize = 16;&lt;br /&gt;
#pragma omp parallel for schedule(guided)&lt;br /&gt;
  for (int ii = 0; ii &amp;lt; nItem; ii+= tileSize) {&lt;br /&gt;
    ...&lt;br /&gt;
#pragma unroll(tileSize)&lt;br /&gt;
    for (int j = 0; j &amp;lt; nItem; j++) {&lt;br /&gt;
#pragma unroll(tileSize)&lt;br /&gt;
      for (int i = ii; i &amp;lt; ii + tileSize; i++) {&lt;br /&gt;
        const float dx = Items.x[j] - Items.x[i];&lt;br /&gt;
        const float dy = Items.y[j] - Items.y[i];&lt;br /&gt;
        const float dz = Items.z[j] - Items.z[i];&lt;br /&gt;
        ...&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  ... &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Hatékony hossz választás====&lt;br /&gt;
Olyan esetben jelent előnyt, ha például az n*n-es mátrix esetén n nem osztható 16-val, mert akkor a vektorizáció nem teljesen hajtható végre. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// n%16 != 0&lt;br /&gt;
float* A = _mm_malloc(sizeof(float)*n*n, 64);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Ilyen esetben mindig marad egy kis vektor részlet, amire elemenként kell elvégezni a műveletet.&amp;lt;br /&amp;gt;&lt;br /&gt;
A megoldást az jelenti, hogy a memória allokálást kiterjesztjük és nagyobb memória területet foglalunk a változónak, így már alkalmazható lesz a vektorizáció, mivel a maradvány területen is el tudjuk végezni a műveletet egy lépésben.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int lda=n;&lt;br /&gt;
if (n % 16 != 0) lda += (16 - n%16); // most lda%16==0&lt;br /&gt;
float* A = _mm_malloc(sizeof(float)*n*lda, 64);&lt;br /&gt;
&lt;br /&gt;
// használat&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
  for (int j = 0; j &amp;lt; n; j++)&lt;br /&gt;
    A[i*lda + j] = ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====&amp;quot;Héj ciklusok&amp;quot; kiküszöbölése====&lt;br /&gt;
Ciklusok esetén lehetséges, hogy a vektorizációt nem teljes mértékben ismeri fel a fordító program. &lt;br /&gt;
Ekkor segíthetünk a fordítónak egy fordítási direktívával ('''#pragma vector aligned'''), ha teljes mértékben biztosak vagyunk, hogy a ciklusunk vektorizálható!&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma vector aligned&lt;br /&gt;
  for (int j = 0; j &amp;lt; n; j++)&lt;br /&gt;
    A[i*lda + j] -= ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Ettől jelentős futás idejű javulást érhetünk el, mivel ezzel a #pragma-val kikapcsoltuk a futás idejű ellenőrzést, de előfordulhatnak olyan esetek, főleg amikor nem jól mértük fel a ciklusunkat és nem sikerült a vektorizálás, hogy ''Segmentation fault'' hibára fogunk futni.&lt;br /&gt;
&lt;br /&gt;
====Regularizáció====&lt;br /&gt;
Van egy ciklusunk, ami egy vektor elemeit módosítja. A processzor az adatokat &amp;quot;csokorba szedve&amp;quot; emeli be a belső kis méretű cache-be. Egyszerre természetesen több vektor elem is bekerül a a cache-be ezzel gyorsítva a feldolgozást. Ezen vektor iterációk valahogyan ráilleszkedik a vektorra.&lt;br /&gt;
&lt;br /&gt;
[[Fájl:regularization.png|Vektor regularizáció]]&lt;br /&gt;
&lt;br /&gt;
Abban az esetben, amikor ez az illesztés nem teljesen tökéletes (lsd. piros elemek), keletkeznem héj ciklusok, melyeket a regularizációval is eliminálni lehet. &amp;lt;br /&amp;gt;&lt;br /&gt;
A feladat eredetileg valahogyan így nézhet ki:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
for (int i = 0; i&amp;lt;n; i++)&lt;br /&gt;
  A[i]=...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ez a következő átalakítással már alkalmas vektorizációra és nem lesz &amp;quot;peel loop&amp;quot; probléma.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
for (int b=0; b&amp;lt;n; b++) {&lt;br /&gt;
  const int jStart = b- b%16; // mindig osztható 16-val&lt;br /&gt;
  ...&lt;br /&gt;
#pragma simd&lt;br /&gt;
  for (int j = jStart; j&amp;lt;n; j++)&lt;br /&gt;
    A[j]=...&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Párhuzamosítás===&lt;br /&gt;
Több féle mód létezik a párhuzamosításra. Korábban említettük a SIMD technikát, ami adatpárhuzamosításra való 1 processzoron belül, de létezik még processzorok közti párhuzamosítás, amit az OpenMP segítségével tudunk hatékonyan megvalósítani. Amennyiben gépek közti párhuzamosításra, üzenek küldésre van szükség, akkor az MPI-t kell segítségül hívni. Általános megoldás nem létezik minden feladatra, mert MPI is alkalmas lehet gépen belüli párhuzamosításra, nem csak az OpenMP. Amit mérlegelni kell, hogy mi az amire az alkalmazásnak szükséges van illetve mekkora kommunikációs overhead keletkezik, ha lecseréljük az OpenMP-t MPI-ra. Általában az egymástól független számításokat OpenMP-vel, míg a kommunikációt igénylő feladatokra MPI-t szoktak használni.&lt;br /&gt;
&lt;br /&gt;
====Szálak párhuzamosítása====&lt;br /&gt;
Az OpenMP-nek környezeti változók segítségével megadható a párhuzamosan futtatható szálak száma. Ezt a következő paranccsal tudjuk megtenni, ahol 5-re állítjuk.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export OMP_NUM_THREADS=5&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Forrásban a párhuzamos szálakat a ''#pragma omp parallel'' segítségével adhatjuk meg.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for              	//külső for szálak párhuzamosítása&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
#pragma simd				// vektorizáció a belső cikluson&lt;br /&gt;
  for (int j = 0; j &amp;lt; m; j++)&lt;br /&gt;
    My_Function(A[i][j]);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A fordítási direktívák össze is vonhatóak, ilyenkor 1 ciklus is elég.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for simd&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
    My_Function(A[i]);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=====Változók láthatósága, megosztása=====&lt;br /&gt;
Változók hatókörét a programozási nyelven kívül pragma-k segítségével is beállíthatjuk, hogy megosztott legyen a változó vagy a szálra nézve saját példány. Amennyiben nem definiáljuk felül, akkor minden változó megosztott. A felül definiálást a private és a shared paraméterekkel lehet megadni az OpenMP-nek.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int A, B, C;&lt;br /&gt;
&lt;br /&gt;
#pragma omp parallel private(A) shared(B)&lt;br /&gt;
{...}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Az itteni példában az B és C megosztott változó a szálak között, míg A-t minden szálnak lemásolja a futás kezdetén.&lt;br /&gt;
&lt;br /&gt;
====Párhuzamos szálak ütemezése====&lt;br /&gt;
OpenMp a szálak ütemezésére három féle módod kínál fel.&lt;br /&gt;
Módok:&lt;br /&gt;
* '''static''' (alapértelmezett): egyenletesen arányban egymás után elosztja a processzeket az indított szálakon, ezért a futási idő tetszőlegesen elhúzódhat, ha egyes processzek lassabban futnak le.&lt;br /&gt;
* '''dynamic''': minden esetben az első szabad szálra kerül a következő processz ütemezése, folyamatos kihasználtságot elérve, de &amp;quot;költséges&amp;quot; a sok-sok ütemezés&lt;br /&gt;
* '''guided''': Az előző két mód ötvözése. Nagyobb procesz számot ütemez az éppen szabad szálakra, melyek számát folyamatosan csökkenti a futás előre haladtával, hogy körülbelül egyszerre fejeződjön be minden szál futása, ezzel csökkentve a dynamic esetén magad ütemezési időráfordítást.&lt;br /&gt;
&lt;br /&gt;
Mindhárom esetben megadható az egyszerre ütemezett processzek száma, amit a ''chunk'' értéke határoz meg. Ha megvan adva a chunk értéke, akkor az minden esetben annak megfelelő processzámot ütemez a szálakra.&lt;br /&gt;
Használata:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel [schedule (&amp;lt;mode&amp;gt;,&amp;lt;chunk&amp;gt;) {...}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Szálak szinkronizációja====&lt;br /&gt;
Szála szinkronizálását többféle módon lehet megvalósítani, de ezen megoldások sajno jelentősen lelassítják a program futását.&lt;br /&gt;
* mutexxel: nagyon lassú, állandóan várnak egymásra a párhuzamos szálak&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++) {&lt;br /&gt;
#pragma omp critical&lt;br /&gt;
  { // kritikus szekció, csak egy szál hajthatja végre&lt;br /&gt;
    total = total + i;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* atomi művelet definiálása: sajnos még ez is lassú, de itt már nem olyan &amp;quot;erős&amp;quot; a szinkronizáció, ezért rendkívül sok limitációja van az itt használható atomi műveleteknek.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++) {&lt;br /&gt;
#pragma omp atomic&lt;br /&gt;
   total += i;&lt;br /&gt;
}	&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=====Párhuzamos redukció=====&lt;br /&gt;
Az itt bemutatott eljárás gyorsabb mint a fenti szinkronizációs megoldás, de sajnos nem alkalmazható vektorokra! A redukciót a ''reduction(operation: scalar)'' formában kell megadni.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int sum = 0;&lt;br /&gt;
#pragma omp parallel for reduction(+: sum)&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++) {&lt;br /&gt;
  sum = sum + i;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Tulajdonképpen a fenti problémakörre a következő minta ad megoldást. Ekkor egy külön változóban akkumuláltatjuk a szál általi összeget és ezen értékeke összegyűjtését végezzük csak atomi műveletként.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int sum = 0;&lt;br /&gt;
#pragma omp parallel&lt;br /&gt;
{&lt;br /&gt;
  int sum_thr = 0;&lt;br /&gt;
#pragma omp for&lt;br /&gt;
  for (int i=0; i&amp;lt;n; i++)&lt;br /&gt;
    sum_thr += i;&lt;br /&gt;
&lt;br /&gt;
#pragma omp atomic&lt;br /&gt;
  sum += sum_thr;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Ciklusok párhuzamosítása====&lt;br /&gt;
Ciklus párhuzamosítására a következő mintát szokás használni.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel&lt;br /&gt;
{&lt;br /&gt;
  // Az itt definiált forrást minden szál esetén végre fog hajtani&lt;br /&gt;
  ...&lt;br /&gt;
#pragma omp for [schedule (&amp;lt;mode&amp;gt;,&amp;lt;chunk&amp;gt;)&lt;br /&gt;
  for (int i = 0; i &amp;lt; n; i++) {&lt;br /&gt;
    // Ez a kódrész lesz párhuzamosítva&lt;br /&gt;
    ...&lt;br /&gt;
  }&lt;br /&gt;
  // ... és ez a köd is minden szál esetén végrehajtódik&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Ciklus kifejtés====&lt;br /&gt;
A technika már egy példa erejéig a vektorizációnál is látható, de ott nem került részletezésre. A megoldás lényege, hogy a kellően nagy ciklust szétdaraboljuk kisebb darabokra, amit már külön feldolgozhatóak, ezzel párhuzamosítva a folyamatokat. Az egy darab kiindulási ciklusból két darab egymásba ágyazott ciklust hozunk létre, ami ugyanazon a feladattéren dolgozik. &lt;br /&gt;
&lt;br /&gt;
Eredeti állapot:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++) {...}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Átírt változat:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int STRIP=1024;&lt;br /&gt;
for (int ii = 0; ii &amp;lt; n; ii += STRIP)&lt;br /&gt;
 for (int i = ii; i &amp;lt; ii+STRIP; i++) {...}&lt;br /&gt;
&lt;br /&gt;
// ha még van maradék elem, akkor azokon is végrehajtjuk a számításokat&lt;br /&gt;
if (n%STRIP != 0) {&lt;br /&gt;
  for (i=n-(n%STRIP)+1; i&amp;lt;n; i++) {...}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A darabok mérete (STRIP értéke) az adott rendszer és feladat függvényében változhat, tehát ez egyfajta &amp;quot;tuning&amp;quot; paraméter.&lt;br /&gt;
&lt;br /&gt;
====Automatikus ciklusösszevonás====&lt;br /&gt;
Az OpenMP támogatást nyújt több egymásba ágyazott ciklus &amp;quot;összevonására&amp;quot;. Ehhez csak a '''collaps''' fordítási direktívát és az összevonandó ciklusok számát kell megadni. &amp;lt;br /&amp;gt;&lt;br /&gt;
FONTOS: ekkor az automatikus vektorizáció kikapcsolásra kerül!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for collaps(2)&lt;br /&gt;
for (int i=0; i&amp;lt;n; i++)&lt;br /&gt;
  for( int j=0; j&amp;lt;m; j++){...}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A fneti példában a 2db for ciklis kerül párhuzamosításra úgy, hogy a fordítás során 1db ciklus generálódik belőle, ami a következő paraméterezéssel ekvivalens:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for&lt;br /&gt;
for (int c=0; c &amp;lt; n*m; c++) {&lt;br /&gt;
  int i = c/n;&lt;br /&gt;
  int j = c%n;&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
[[Kategória: HPC]]&lt;/div&gt;</summary>
		<author><name>Szigeti(AT)niif.hu</name></author>	</entry>

	<entry>
		<id>https://wiki.niif.hu/index.php?title=HPC_software&amp;diff=3802</id>
		<title>HPC software</title>
		<link rel="alternate" type="text/html" href="https://wiki.niif.hu/index.php?title=HPC_software&amp;diff=3802"/>
				<updated>2016-07-13T13:53:17Z</updated>
		
		<summary type="html">&lt;p&gt;Szigeti(AT)niif.hu: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The current version of software on specific machines might be higher than described here. We are encouraging all users to install other tools to be used on our servers if service AUP and license restrictions are respected.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
!Software component&lt;br /&gt;
!Debrecen&lt;br /&gt;
!Pécs&lt;br /&gt;
!Szeged&lt;br /&gt;
!Budapest&lt;br /&gt;
!Budapest2&lt;br /&gt;
!Debrecen2,3 (Leo, Apollo)&lt;br /&gt;
!Miskolc&lt;br /&gt;
|-&lt;br /&gt;
| colspan=8| '''Compiler environment (compiler, debugger, profiler)'''&lt;br /&gt;
|-&lt;br /&gt;
| AMD [http://developer.amd.com/tools-and-sdks/cpu-development/x86-open64-compiler-suite/ compilers ((opencc, openf90, openf95))] || colspan=2|-||colspan=2|Available||colspan=3|-&lt;br /&gt;
|-&lt;br /&gt;
| Math. Library (AMD) [http://developer.amd.com/tools/cpu-development/amd-core-math-library-acml ACML] ||colspan=2|-||colspan=2|4.2.4||colspan=3|-&lt;br /&gt;
|-&lt;br /&gt;
| [http://sources.redhat.com/gdb/ GDB] || colspan=2|7|| colspan=2|7.0.1|| colspan=2|7.2||7.5&lt;br /&gt;
|-&lt;br /&gt;
| [http://gcc.gnu.org/ GNU compilers (gcc, g++, gfortran)]||4,3||4.1.2 || colspan=2|4.1.2, 4.4.4|| colspan=2|4.4.7|| 4.3.4&lt;br /&gt;
|-&lt;br /&gt;
| Gprof|| colspan=2|2,2|| colspan=2|2,17|| colspan=2|2.20|| 2.23&lt;br /&gt;
|-&lt;br /&gt;
| Intel [http://software.intel.com/en-us/articles/intel-composer-xe/ compilers (icc, ifort)], [http://software.intel.com/en-us/articles/intel-math-kernel-library-documentation/ MKL],  [http://software.intel.com/en-us/articles/intel-mkl-link-line-advisor/ Linking]  ||colspan=2|12.1.5 (composerxe-2011 Update 11) ||colspan=2|-|| colspan=3|16.0&lt;br /&gt;
|- &lt;br /&gt;
| [http://oprofile.sourceforge.net/ Oprofile] ||  colspan=3|- ||0.9.4||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| colspan=8|'''Libraries supporting parallel development'''&lt;br /&gt;
|- &lt;br /&gt;
| LAM-MPI ||colspan=2|- ||colspan=2|7.1.2||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| MPICH2 ||colspan=2|1,4||colspan=2|1,2||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| [http://openmp.org/ OpenMP]||colspan=2|2.5 (GNU), 3.0 (Intel)||colspan=2|2.5 (GNU), 2.5 (AMD) ||colspan=3|2.5 (GNU), 3.0 (Intel)&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.open-mpi.org/ OpenMPI] ||1.4.2 ||1.3.2 ||colspan=2|1.4.3 || colspan=2|1.8.5||1.6.3&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.csm.ornl.gov/pvm/ PVM] ||colspan=4|3.4.5 ||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| [http://videotorium.hu/hu/recordings/details/12291,Overview_and_usage_of_the_SGI_HPC_softwares_on_the_SGI_ICE_and_SGI_UV_supercomputers SGI Perf. Suite] ||colspan=2|Available||colspan=2|-||colspan=2|-||Available&lt;br /&gt;
|- &lt;br /&gt;
| SGI-MPI (MPT) ||colspan=2|2,04||colspan=2|-||colspan=2|-||2.12&lt;br /&gt;
|- &lt;br /&gt;
| PGI Compiler and Tools ||colspan=5|-|| 15.10 || -&lt;br /&gt;
|- &lt;br /&gt;
| colspan=8|'''Libraries supporting scientific work'''&lt;br /&gt;
|- &lt;br /&gt;
| AmberTool || 16 ||colspan=6|-&lt;br /&gt;
|-&lt;br /&gt;
| Atlas ||colspan=4|3.8.3||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| BLACS ||colspan=4|1.1 (OpenMPI, MPICH2) ||-||3.1.3||-&lt;br /&gt;
|- &lt;br /&gt;
| BLAS ||colspan=2|3.2.2 ||colspan=2|3||colspan=2|-||3&lt;br /&gt;
|- &lt;br /&gt;
| Boost||colspan=2|1,36||colspan=2|1,33||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| FFTW ||colspan=2|3.1.2 ||colspan=2|3.2.2||colspan=2|-||3.3.3&lt;br /&gt;
|- &lt;br /&gt;
| Gromacs ||colspan=2|4.5.4 ||colspan=2|4.5.5||colspan=2|-||4.6.2&lt;br /&gt;
|- &lt;br /&gt;
| GUILE ||colspan=2|1.8.5 ||colspan=2|-||colspan=2|-||1.8.8&lt;br /&gt;
|- &lt;br /&gt;
| HDF5 ||colspan=2|1.8.7||colspan=2|-||colspan=2|1.8.17||-&lt;br /&gt;
|- &lt;br /&gt;
| LAMMPS ||colspan=4|-||16.05.14 (Phi)||16.05.14 (GPU, Phi)||-&lt;br /&gt;
|-&lt;br /&gt;
| Lapack ||colspan=2|3.2.2 ||colspan=2|3 ||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.maplesoft.com/ Maple] ||colspan=2| - ||colspan=2|18||-||2015||-&lt;br /&gt;
|- &lt;br /&gt;
| [https://wiki.niif.hu/index.php?title=NIIF_szuperszámítógépek_használata#Matlab_feladatok Matlab] ||colspan=2|7.13.0 (R2011b)|| colspan=2|-||-||R2015b||-&lt;br /&gt;
|- &lt;br /&gt;
| NumPy ||colspan=2|1,3||colspan=2|1.5.1||colspan=2|-||1.7.0&lt;br /&gt;
|- &lt;br /&gt;
| Octave ||colspan=2|3,4||colspan=2|3.0.5 ||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| Qt||colspan=2|3.3.8b, 4.6.2||colspan=2|3.3.6 ||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| R ||colspan=2|2.8.1 ||colspan=2|-||colspan=2|-||3.0.1&lt;br /&gt;
|- &lt;br /&gt;
| Scalapack||colspan=4|1.7.5||-||2.0.2||-&lt;br /&gt;
|- &lt;br /&gt;
| Scilab ||colspan=2|5.3.2||colspan=2|-||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| Sun JRE ||colspan=2|1,6||colspan=2|-||colspan=1|-||1.8||1.7&lt;br /&gt;
|- &lt;br /&gt;
| Gaussian||colspan=3|09_REV_C.01||-||-||colspan=2|09_REV_D.01&lt;br /&gt;
|- &lt;br /&gt;
| colspan=8|'''Middleware'''&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.nordugrid.org/arc/ ARC grid middleware] ||colspan=7|4.1.0 (A-REX, ARIS, clients))&lt;br /&gt;
|- &lt;br /&gt;
| colspan=8|'''Miscellaneous software'''&lt;br /&gt;
|- &lt;br /&gt;
| Emacs ||colspan=2|22,3||colspan=2|21,4||-||23.1.1||22.3.1&lt;br /&gt;
|- &lt;br /&gt;
| GNU Plot ||colspan=2|4.2.3 ||colspan=2|4||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| ImageMagick ||colspan=2|6.4.3 ||colspan=2|6.2.8||colspan=3|-&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.virtualgl.org/About/TigerVNC Turbo VNC]||colspan=4|1,1||colspan=3|2&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.blender.org/ Blender]||colspan=3|2,49||-||colspan=2|2.49||-&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.virtualgl.org/ VirtualGL]||colspan=3|2.3.2||-||colspan=2|2.4||-&lt;br /&gt;
|- &lt;br /&gt;
| [http://www.paraview.org/ Paraview]||colspan=3|3.14.1||-||colspan=3|3.14.1&lt;br /&gt;
|- &lt;br /&gt;
| ffmpeg||colspan=2|-||colspan=2|0.4.9||colspan=3|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
[[Kategória: HPC]]&lt;/div&gt;</summary>
		<author><name>Szigeti(AT)niif.hu</name></author>	</entry>

	<entry>
		<id>https://wiki.niif.hu/index.php?title=Intel_Xeon_Phi&amp;diff=3794</id>
		<title>Intel Xeon Phi</title>
		<link rel="alternate" type="text/html" href="https://wiki.niif.hu/index.php?title=Intel_Xeon_Phi&amp;diff=3794"/>
				<updated>2016-05-26T10:03:42Z</updated>
		
		<summary type="html">&lt;p&gt;Szigeti(AT)niif.hu: /* Offload */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Az oldalon található információk segítséget nyújtanak a HPC felhasználóinknak, hogy alkalmazásaikat miként portolhatják valamint optimalizálhatják a hatékonyabb futást szem előtt tartva Intel Xeon Phi koprocesszor kártyákon.&lt;br /&gt;
&lt;br /&gt;
{{INFO|Egy 10 alkalmas, angol nyelvű Xeon Phi programozás tutoriál videóanyaggal és példaprogrammal elérhető a [[http://colfaxresearch.com/how-16-04/ Colfax oldalán]]}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Hardver bemutatása==&lt;br /&gt;
Intel® Xeon Phi™ Coprocessor 7120P&lt;br /&gt;
&amp;lt;gallery&amp;gt;&lt;br /&gt;
Fájl:Xeon-phi-family-rwd.png.rendition.intel.web.720.405.png|&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-  align=&amp;quot;center&amp;quot;&lt;br /&gt;
! CPU&lt;br /&gt;
! threads/Core&lt;br /&gt;
!max threads&lt;br /&gt;
! CPU Freq&lt;br /&gt;
! Memória&lt;br /&gt;
! L1 cache&lt;br /&gt;
! L2 cache&lt;br /&gt;
! Interfész&lt;br /&gt;
! Ptot&lt;br /&gt;
! Számítási kapacitás&lt;br /&gt;
|-  align=&amp;quot;center&amp;quot;&lt;br /&gt;
| 61&lt;br /&gt;
| 4 Hardver&lt;br /&gt;
| 244&lt;br /&gt;
| 1238 GHz&lt;br /&gt;
| 16GB GDDR5&lt;br /&gt;
| 32KB/core&lt;br /&gt;
| 512KB/core&lt;br /&gt;
| PCIe&lt;br /&gt;
| 300W&lt;br /&gt;
| 1,2 TFlop/s dupla pontosság&lt;br /&gt;
|-  align=&amp;quot;center&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
| OpenMP-nél csak '''240'''&lt;br /&gt;
|&lt;br /&gt;
| ~100 cycles&lt;br /&gt;
~174 GB/s&lt;br /&gt;
| ~3 cycles&lt;br /&gt;
| ~10 cycles&lt;br /&gt;
| ~7 GB/s&lt;br /&gt;
~1 microsecond&lt;br /&gt;
|&lt;br /&gt;
| 2,4 TFlop/s egyszeres pontosság&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Az Intel® 1. generációs Xeon Phi™ koprocesszorában található 61 processzor ugyanazt a 16GB memóriát éri el, de semmilyen módon se fér hozzá közvetlenül a hoszt gép fizikai memóriájához.&amp;lt;br /&amp;gt;&lt;br /&gt;
[[Budapest2_klaszter|Budapest2]] és [[HPC#Szupersz.C3.A1m.C3.ADt.C3.B3g.C3.A9peink_.C3.B6sszehasonl.C3.ADt.C3.A1sa|Debrecen3-Phi]] gépeinkben számítási nodonként 2-2 Phi™ koprocesszor található. &amp;lt;br/&amp;gt;&lt;br /&gt;
A kártyákon speciális célú Linux kernel fut melyre a belépés limitáltan csak a rendszergazdák számára lehetséges SSH-n keresztül a hoszt gépről. Természetesen a kártyára belépve olyan érzésünk lehet, mintha egy sok processzoros gépen dolgoznánk, ami rendelkezik rengeteg memóriával. Ez így is van, de a hatékonyabb kihasználás érdekében az SSH-n keresztüli elérés nem lehetséges, csak a Slurm segítségével valamint a programozási nyelvek által támogatott csatornákon keresztül. Ennek részletezése lentebb megtalálható.&lt;br /&gt;
&lt;br /&gt;
==Használati módok==&lt;br /&gt;
A koprocesszor használatai módjait két csoportba lehet sorolni. A '''natív''' és az '''offload''' használatot.  &amp;lt;br/&amp;gt;&lt;br /&gt;
Ezen használati módokról a lenti fejezetekben több részletet is megtudhat. Az itt megfogalmazott &amp;quot;támogatás&amp;quot; csak a mi infrastruktúránkra érvényes! &amp;lt;br /&amp;gt;&lt;br /&gt;
A bemutatott példák ''c++'' nyelven vannak, de természetesen ''c'' és ''fortran'' esetén is megtalálható az ekvivalens módszer.&lt;br /&gt;
&lt;br /&gt;
===a) nem támogatott mód===&lt;br /&gt;
====közvetlen natív használat====&lt;br /&gt;
A natív mód arra szolgál, hogy a MIC kártyára  lefordított alkalmazásunkat felmásoljuk a kártyára, majd belépünk SSH-n a kártyára és azon futtatjuk a bináris kódunkat. &amp;lt;br /&amp;gt;&lt;br /&gt;
Biztonsági okokból a közvetlen natív használat az NIIF HPC infrastruktúrán nem elérhető. &amp;lt;br/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===b) támogatott, de nem preferált használat===&lt;br /&gt;
====automatikus kódátvitel====&lt;br /&gt;
Ebben az esetben nem kell átmásolnunk a bináris alkalmazásunkat a kártyára sőt még csak be se kell lépnünk, mert ezt a két lépést egy beépített tool végzi el. &amp;lt;br/&amp;gt;&lt;br /&gt;
A tool eléréséhez szükséges parancs neve: '''micnativeloadex'''&lt;br /&gt;
&lt;br /&gt;
Ebben az esetben a workflow a következőképpen módosul:&lt;br /&gt;
# Alkalmazás megírása. például [[Média:hello.cc|''hello.cc'']]&lt;br /&gt;
# Alkalmazás fordítása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;icpc -o hello-MIC -mmic hello.cc&amp;lt;/pre&amp;gt;&lt;br /&gt;
# A tool futásához szükséges library-k megadása &amp;lt;br&amp;gt; &amp;lt;pre&amp;gt;export SINK_LD_LIBRARY_PATH=/opt/intel/composerxe/compiler/lib/mic&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Alkalmazás futtatása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;micnativeloadex hello-MIC&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| BUDAPEST2[cn10] phi (0)$ export SINK_LD_LIBRARY_PATH=/opt/intel/composerxe/compiler/lib/mic &amp;lt;br/&amp;gt;BUDAPEST2[cn10] phi (0)$ micnativeloadex hello-MIC &amp;lt;br/&amp;gt;Hello world! I have 244 logical cores.&amp;lt;br/&amp;gt;BUDAPEST2[cn10] phi (0)$&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===c) támogatott módok===&lt;br /&gt;
====Offload====&lt;br /&gt;
Az Offload modell esetén szükséges a forráskód módosítása, mégpedig úgy, hogy azon kódrészletet, amit a koprocesszoron szeretnénk futtatni, ki kell egészíteni egy fordítási direktívával. Az alkalmazás fordításakor elegendő csak a CPU-ra való binárist készíteni mivel a modell végzi a megfelelő forrás részlet mozgatását és futtatását.&amp;lt;br /&amp;gt;&lt;br /&gt;
[[Fájl:offload.png|Offload modell]] &amp;lt;br /&amp;gt;&lt;br /&gt;
Példánkban jól látható, hogy a második kiíratás már a koprocesszoron fut.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
int main(int argc, char * argv[]) {&lt;br /&gt;
  printf(&amp;quot;Hello world from host!\n&amp;quot;);&lt;br /&gt;
#pragma offload target(mic)&lt;br /&gt;
  {&lt;br /&gt;
    printf(&amp;quot;Hello world from coprocessor!\n&amp;quot;); fflush(stdout);&lt;br /&gt;
  }&lt;br /&gt;
  printf(&amp;quot;Bye\n&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ebben a példában az operációs rendszer választja ki, hogy melyik Phi kártyán fusson le a kódrészlet. Amennyiben mi szeretnénk explicite megmondani, hogy melyik kártyán fusson, akkor a fordítási direktívában a mic kulcsszó után kell tenni egy &amp;quot;:&amp;quot;-t és a kártya sorszámát [mi esetünkben 0 vagy 1]. Ekkor így nézne ki a direktíva: ''#pragma offload target(mic:1)''&lt;br /&gt;
&lt;br /&gt;
Amennyiben az offload szekcióban függvény hívás is szerepel, akkor szükségeltetik az függvény futásához szükséges változók adatterületének átmásolása a Phi kártyára, majd a lefutás után visszamásolni a hoszt gépre. Ezen másolást az ''offload_attribute'' ''push'' és ''pop'' paraméterei segítségével határozhatjuk meg. &amp;lt;br /&amp;gt;&lt;br /&gt;
A következő mátrix-vektor szorzás példánál az A mátrix globális változó, míg a b és c vektorok a függvény paraméterei.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//A változók felmásolásra kerülnek a Xeon Phi-re&lt;br /&gt;
#pragma offload_attribute(push, target(mic))&lt;br /&gt;
  double* A;&lt;br /&gt;
  void MatrixVector(double *b, double *c, double m, double n) {&lt;br /&gt;
    ...&lt;br /&gt;
    c[] = A[..]*b[]&lt;br /&gt;
  }&lt;br /&gt;
// felmásolt objektumok visszamásolása&lt;br /&gt;
#pragma offload_attribute(pop)&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
  ...&lt;br /&gt;
#pragma offload target(mic) in(A[0:n*m])&lt;br /&gt;
// így is meg lehetne adni: #pragma offload target(mic:0) in(A : length(n*m))	        &lt;br /&gt;
  {&lt;br /&gt;
    MatrixVector(b, c, m, n);&lt;br /&gt;
  }&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ekkor minden memória területet 2x kell másolni, egyszer fel a kártyára, majd másodszor vissza a hoszt gépre, pedig elegendő lenne csak az eredmény vektort visszamásolni. Ezen optimalizálással egy későbbi fejezetben foglalkozunk. &amp;lt;p /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Futás során kaphatunk részletes riportot is, hogy pontosan milyen adatmozgatások történnek az alkalmazás futása során. Ehhez nincs szükség másra csak az '''OFFLOAD_REPORT''' környezeti változó megfelelő beállítása. A beállított értékek Automatic Offload esetén 1, 2, míg Compiler Assisted Offload esetén 1, 2 és 3 lehetnek, attól függően mennyire részletes riportot szeretnénk kapni. &amp;lt;br /&amp;gt;&lt;br /&gt;
beállítás:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export OFFLOAD_REPORT=3&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
példa kimenet a hello alkalmazásra:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Hello world from host!&lt;br /&gt;
[Offload] [MIC 0] [File]                    hello_offload.cc&lt;br /&gt;
[Offload] [MIC 0] [Line]                    5&lt;br /&gt;
[Offload] [MIC 0] [Tag]                     Tag 0&lt;br /&gt;
Hello world from coprocessor!&lt;br /&gt;
[Offload] [HOST]  [Tag 0] [CPU Time]        0.554653(seconds)&lt;br /&gt;
[Offload] [MIC 0] [Tag 0] [CPU-&amp;gt;MIC Data]   0 (bytes)&lt;br /&gt;
[Offload] [MIC 0] [Tag 0] [MIC Time]        0.000140(seconds)&lt;br /&gt;
[Offload] [MIC 0] [Tag 0] [MIC-&amp;gt;CPU Data]   0 (bytes)&lt;br /&gt;
Bye&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Offload + OpenMP====&lt;br /&gt;
Amennyiben nem csak 1 processzort szeretnénk használni a Phi kártyánkon és az alkalmazásunkban használt számítás megengedi a párhuzamosítást, akkor az OpenMP segítségével párhuzamosíthatjuk a megfelelő számítást. &amp;lt;br /&amp;gt;&lt;br /&gt;
[[Fájl:Offload+openmp.png|Offload modell és OpenMP]] &amp;lt;br /&amp;gt;&lt;br /&gt;
Példánk maradjon ugyanúgy a mátrix-vektor szorzás. Amennyiben a teljes kártya adta kapacitást ki szeretnénk használni, akkor az OMP threadek számát 240-re kell beállítani. Azért nem 244-re, mivel 1 CPU-t dedikáltan csak a kommunikációra használ a rendszer és 1 CPU 4 hardver threaddel rendelkezik.&lt;br /&gt;
Ekkor a példánk így változik:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;omp.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#pragma offload_attribute(push, target(mic))&lt;br /&gt;
void multiply(int n, int m, double* A, double* b, double* c){&lt;br /&gt;
  const int nCPUs = omp_get_max_threads();   //240&lt;br /&gt;
  omp_set_num_threads(nCPUs);&lt;br /&gt;
#pragma omp parallel for schedule(dynamic, 1)&lt;br /&gt;
  for ( int i = 0 ; i &amp;lt; m ; i++)&lt;br /&gt;
     for ( int j = 0 ; j &amp;lt; n ; j++)&lt;br /&gt;
       c[i] += A[i*n+j] * b[j];&lt;br /&gt;
}&lt;br /&gt;
#pragma offload_attribute(pop)&lt;br /&gt;
&lt;br /&gt;
int main(){&lt;br /&gt;
  double * A = (double*) malloc(sizeof(double)*n*m);&lt;br /&gt;
  ...&lt;br /&gt;
#pragma offload target(mic) in(A:length(n*m))&lt;br /&gt;
     multiply(n, m, A, b, c);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Az alkalmazás fordításánál a fordítónak meg kell adni, hogy használhja az openmp library-ket is. Ezt a '''-qopenmp''' kapcsolóval tudjuk elérni.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
icpc -o matrix-CPU -qopenmp matrix.cc&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====MPI + Offload + OpenMP====&lt;br /&gt;
Abban az esetben, ha 1 számítási node nem elég, MPI segítségével össze tudunk kötni több nodet, heterogén rendszert alkotva, és azokon alkalmazható a fenti Offload + OpenMP modell. &amp;lt;br /&amp;gt;&lt;br /&gt;
[[Fájl:Mpi+offload+openmp.png|MPI elosztás valamint Offload modell és OpenMP]] &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Phi használat slurm-mel==&lt;br /&gt;
Slurm esetén a mic opciót kell megadni a gres-knél. Amennyiben egynél több kártyára van szükség, akkor azt a ''mic:2''-vel lehetséges megadni. Ekkor két kártyát foglal le az ütemező a futtatandó feladatnak. &amp;lt;br /&amp;gt;&lt;br /&gt;
Egy lehetséges feladat, amit srun-nal lehet a slurmben beküldeni. debrecen3 esetén a ''prod-phi'' particiót kell használni&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ cat slurm_job_openmp_phi&lt;br /&gt;
&lt;br /&gt;
#!/bin/bash&lt;br /&gt;
#SBATCH -A &amp;lt;PROJECT NEVE&amp;gt;&lt;br /&gt;
#SBATCH --job-name=&amp;lt;JOB NEVE&amp;gt;&lt;br /&gt;
#SBATCH --gres mic:2&lt;br /&gt;
#SBATCH --time=1:00:00&lt;br /&gt;
#SBATCH --partition=prod&lt;br /&gt;
#SBATCH -o slurm-%A.out&lt;br /&gt;
export OMP_NUM_THREADS=2&lt;br /&gt;
./runme_openmp_2phi&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Ekkor a feladat beküldése a következő paranccsal lehetséges.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
srun ./slurm_job_openmp_phi&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Alkalmazások optimalizálása a kártyára==&lt;br /&gt;
Ebben a fejezetben optimalizálási lehetőségeket mutatunk be, melyek segítségül szolgálhatnak az egyes alkalmazások hatékonyabb futása érdekében ezzel jobban kihasználni a rendelkezésre álló erőforrások adta számítási kapacitás.&lt;br /&gt;
&lt;br /&gt;
===Környezeti változók===&lt;br /&gt;
Lehetséges csak a Phi kártyákon használt környezeti változók beállítása, amit az alkalmazásunk használni is tud. &amp;lt;br /&amp;gt;&lt;br /&gt;
Példánkban az OpenMP esetén használható szálak számát állíthatjuk be:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export MIC_ENV_PREFIX=XEONPHI&lt;br /&gt;
export OMP_NUM_THREADS=12&lt;br /&gt;
export XEONPHI_OMP_NUM_THREADS=240&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
A beállított prefix tetszőleges karaktersorozat lehet, a lényeg, hogy ezt a beállított prefixet használjuk a környezeti változók esetén is!&lt;br /&gt;
 &lt;br /&gt;
===Hosszredukció===&lt;br /&gt;
Többnyire a fordítók, ha nem mondjuk meg explicite, a dupla pontosságú változatot választják a lehetséges változó- és függvény típusok közül. Amennyiben a számításunk nem igényli a dupla pontosságot, akkor használjuk a forrásban mindenhol az adott típus vagy függvény egyszeres pontosságú változatát, mellyel jelentős teljesítmény növekedést tudunk elérni. Továbbá az egyes műveletek futási ideje nem egyforma, így a műveletet helyettesíthetjük más művelettel, ami gyorsabb, akkor ismét javíthatunk a hatékonyságon. &amp;lt;br /&amp;gt;&lt;br /&gt;
Íme pár példa, amit alkalmazni lehet:&lt;br /&gt;
* &amp;quot;''/''&amp;quot; művelet helyett &amp;quot;''*''&amp;quot; -t használjunk, mert azt gyorsabban végre tudja hajtani a processzor&lt;br /&gt;
* ''pow'' =&amp;gt; ''1.0 / sqrt()''    , tetszőleges hatványozás, pl.: 3/2-ik hatvány&lt;br /&gt;
* hardver áltatl támogatott műveletek:&lt;br /&gt;
** ''exp'' =&amp;gt; ''exp2''&lt;br /&gt;
** ''log'' =&amp;gt; ''log2''&lt;br /&gt;
* dupla pontosság -&amp;gt; egyszeres pontosság lebegőpontos számok és műveletek esetén &lt;br /&gt;
** ''1.0'' =&amp;gt; ''1.0f''&lt;br /&gt;
** ''pow'' =&amp;gt; ''powf''&lt;br /&gt;
&lt;br /&gt;
===Vektorizáció===&lt;br /&gt;
A vektorizáció azon fordítási időben eldönthető optimalizációs faktor, amely nagy mértékben függ attól a hardver felépítésétől, amire éppen a fordítás történni fog. Legfontosabb befolyásoló tényezők a processzor cache méretei illetve a vektor műveletekért felelős aritmetikai egységének jellemzői mivel ezeket használja fel a fordító az optimalizálási folyamata során. &amp;lt;br /&amp;gt;&lt;br /&gt;
Az automatikus vektorizáció minimum -O2 szinttől érvényes, de mivel általában ez a szint az alapértelmezett, ezért minden fordításnál alkalmazásra kerül. &amp;lt;br /&amp;gt;&lt;br /&gt;
A fordító programnak a következő kapcsolókat lehet megadni, melyek segítenek a minél jobb vektorizációban:&lt;br /&gt;
* ''-qopt-report=&amp;lt;szám&amp;gt;'' ; az optimalizációs jelentés részletességét lehet megadni 0-tól 5-ig, ahol az 5-s szint a legrészletesebbet jelenti&lt;br /&gt;
* ''-qopt-report-phase:vec'' ; a vektorizációról szeretnék jelentést kapni&lt;br /&gt;
* ''-opt-report-file=&amp;lt;fájlnév&amp;gt;'' ; megadható a riportfájl neve&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
icpc autovec.cc -mmic -qopt-report=5 -opt-report-file=vectorization.oMIC.optrpt -qopt-report-phase:vec&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====SIMD====&lt;br /&gt;
Az adatok hatékonyabb feldolgozására az egyik megvalósítás a SIMD (Single Instruction Multiple Data), amikor is a különálló adatokat úgy kezeli, mintha az vektorok volnának és azon végzi el a vektor-műveletet. Ezzel egy a hardver által támogatott műveletet végzünk egy lépésben egy nagyobb adatmennyiségen, ezzel párhuzamosítva a feldolgozási folyamatot. Ezzel a módszerrel nagyon jó adat szintű párhuzamosítást lehet elérni.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Fájl:Simd.png|SIMD]]&lt;br /&gt;
&lt;br /&gt;
Főbb támogatott műveletek:&lt;br /&gt;
* aritmetikai műveletek, bináris operátorok&lt;br /&gt;
* összehasonlítások&lt;br /&gt;
* konverziók és típus kasztolások&lt;br /&gt;
* bit-műveletek: NOT, AND, OR, XOR, XAND&lt;br /&gt;
&lt;br /&gt;
Használata: '''simd''' fordítási direktívával lehetséges &amp;lt;br /&amp;gt;&lt;br /&gt;
Például:&lt;br /&gt;
* egyszerű vektorizáció&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma simd&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
  A[i] += B[i];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* adat párhuzamosítás OpenMP segítségével&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for simd&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
  DoSomeWork(A[i]);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====AoS -&amp;gt; SoA====&lt;br /&gt;
A kiindulás probléma az, hogy a struktúrákat tartalmazó vektort nem lehet jól vektorizálni, mivel a cache-ben tárolt adatok nagy többsége feleslegesen van bent, mivel azon adatokat csak egy későbbi ciklus futás során fogja az alkalmazás feldolgozni. Tehát ezen adatok jelenleg feleslegesen foglalják a cache területet, ahelyett, hogy hasznos adatot tárolnánk ott.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
struct MyStructure {&lt;br /&gt;
  float x, y, z;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void My_Function(cons int nItem, MyStructure* const Items, const float delta) {&lt;br /&gt;
  for (int i = 0; i &amp;lt; nItem; i++) {&lt;br /&gt;
    ...&lt;br /&gt;
    for (int j = 0; j &amp;lt; nItem; j++) {&lt;br /&gt;
      const float dx = Items[j].x - Items[i].x;&lt;br /&gt;
      const float dy = Items[j].y - Items[i].y;&lt;br /&gt;
      const float dz = Items[j].z - Items[i].z;&lt;br /&gt;
      ...  &lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  ... &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A vektorban a struktúrák tárolása helyett minden elemnek egy dedikált vektort allokálunk és így könnyen tudja a fordító vektorizálni azt. Természetesen segítségként a vektor feldolgozását &amp;quot;tömbösítettük&amp;quot; a tileSize méret megadásával illetve a ''unroll'' fordítási direktíva segítségével.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
struct MyStructureSet {&lt;br /&gt;
  float *x, *y, *z;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void My_Function(cons int nItem, MyStructure* const Items, const float delta) {&lt;br /&gt;
  const int tileSize = 16;&lt;br /&gt;
#pragma omp parallel for schedule(guided)&lt;br /&gt;
  for (int ii = 0; ii &amp;lt; nItem; ii+= tileSize) {&lt;br /&gt;
    ...&lt;br /&gt;
#pragma unroll(tileSize)&lt;br /&gt;
    for (int j = 0; j &amp;lt; nItem; j++) {&lt;br /&gt;
#pragma unroll(tileSize)&lt;br /&gt;
      for (int i = ii; i &amp;lt; ii + tileSize; i++) {&lt;br /&gt;
        const float dx = Items.x[j] - Items.x[i];&lt;br /&gt;
        const float dy = Items.y[j] - Items.y[i];&lt;br /&gt;
        const float dz = Items.z[j] - Items.z[i];&lt;br /&gt;
        ...&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  ... &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Hatékony hossz választás====&lt;br /&gt;
Olyan esetben jelent előnyt, ha például az n*n-es mátrix esetén n nem osztható 16-val, mert akkor a vektorizáció nem teljesen hajtható végre. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// n%16 != 0&lt;br /&gt;
float* A = _mm_malloc(sizeof(float)*n*n, 64);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Ilyen esetben mindig marad egy kis vektor részlet, amire elemenként kell elvégezni a műveletet.&amp;lt;br /&amp;gt;&lt;br /&gt;
A megoldást az jelenti, hogy a memória allokálást kiterjesztjük és nagyobb memória területet foglalunk a változónak, így már alkalmazható lesz a vektorizáció, mivel a maradvány területen is el tudjuk végezni a műveletet egy lépésben.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int lda=n;&lt;br /&gt;
if (n % 16 != 0) lda += (16 - n%16); // most lda%16==0&lt;br /&gt;
float* A = _mm_malloc(sizeof(float)*n*lda, 64);&lt;br /&gt;
&lt;br /&gt;
// használat&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
  for (int j = 0; j &amp;lt; n; j++)&lt;br /&gt;
    A[i*lda + j] = ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====&amp;quot;Héj ciklusok&amp;quot; kiküszöbölése====&lt;br /&gt;
Ciklusok esetén lehetséges, hogy a vektorizációt nem teljes mértékben ismeri fel a fordító program. &lt;br /&gt;
Ekkor segíthetünk a fordítónak egy fordítási direktívával ('''#pragma vector aligned'''), ha teljes mértékben biztosak vagyunk, hogy a ciklusunk vektorizálható!&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma vector aligned&lt;br /&gt;
  for (int j = 0; j &amp;lt; n; j++)&lt;br /&gt;
    A[i*lda + j] -= ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Ettől jelentős futás idejű javulást érhetünk el, mivel ezzel a #pragma-val kikapcsoltuk a futás idejű ellenőrzést, de előfordulhatnak olyan esetek, főleg amikor nem jól mértük fel a ciklusunkat és nem sikerült a vektorizálás, hogy ''Segmentation fault'' hibára fogunk futni.&lt;br /&gt;
&lt;br /&gt;
====Regularizáció====&lt;br /&gt;
Van egy ciklusunk, ami egy vektor elemeit módosítja. A processzor az adatokat &amp;quot;csokorba szedve&amp;quot; emeli be a belső kis méretű cache-be. Egyszerre természetesen több vektor elem is bekerül a a cache-be ezzel gyorsítva a feldolgozást. Ezen vektor iterációk valahogyan ráilleszkedik a vektorra.&lt;br /&gt;
&lt;br /&gt;
[[Fájl:regularization.png|Vektor regularizáció]]&lt;br /&gt;
&lt;br /&gt;
Abban az esetben, amikor ez az illesztés nem teljesen tökéletes (lsd. piros elemek), keletkeznem héj ciklusok, melyeket a regularizációval is eliminálni lehet. &amp;lt;br /&amp;gt;&lt;br /&gt;
A feladat eredetileg valahogyan így nézhet ki:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
for (int i = 0; i&amp;lt;n; i++)&lt;br /&gt;
  A[i]=...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ez a következő átalakítással már alkalmas vektorizációra és nem lesz &amp;quot;peel loop&amp;quot; probléma.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
for (int b=0; b&amp;lt;n; b++) {&lt;br /&gt;
  const int jStart = b- b%16; // mindig osztható 16-val&lt;br /&gt;
  ...&lt;br /&gt;
#pragma simd&lt;br /&gt;
  for (int j = jStart; j&amp;lt;n; j++)&lt;br /&gt;
    A[j]=...&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Párhuzamosítás===&lt;br /&gt;
Több féle mód létezik a párhuzamosításra. Korábban említettük a SIMD technikát, ami adatpárhuzamosításra való 1 processzoron belül, de létezik még processzorok közti párhuzamosítás, amit az OpenMP segítségével tudunk hatékonyan megvalósítani. Amennyiben gépek közti párhuzamosításra, üzenek küldésre van szükség, akkor az MPI-t kell segítségül hívni. Általános megoldás nem létezik minden feladatra, mert MPI is alkalmas lehet gépen belüli párhuzamosításra, nem csak az OpenMP. Amit mérlegelni kell, hogy mi az amire az alkalmazásnak szükséges van illetve mekkora kommunikációs overhead keletkezik, ha lecseréljük az OpenMP-t MPI-ra. Általában az egymástól független számításokat OpenMP-vel, míg a kommunikációt igénylő feladatokra MPI-t szoktak használni.&lt;br /&gt;
&lt;br /&gt;
====Szálak párhuzamosítása====&lt;br /&gt;
Az OpenMP-nek környezeti változók segítségével megadható a párhuzamosan futtatható szálak száma. Ezt a következő paranccsal tudjuk megtenni, ahol 5-re állítjuk.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export OMP_NUM_THREADS=5&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Forrásban a párhuzamos szálakat a ''#pragma omp parallel'' segítségével adhatjuk meg.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for              	//külső for szálak párhuzamosítása&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
#pragma simd				// vektorizáció a belső cikluson&lt;br /&gt;
  for (int j = 0; j &amp;lt; m; j++)&lt;br /&gt;
    My_Function(A[i][j]);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A fordítási direktívák össze is vonhatóak, ilyenkor 1 ciklus is elég.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for simd&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
    My_Function(A[i]);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=====Változók láthatósága, megosztása=====&lt;br /&gt;
Változók hatókörét a programozási nyelven kívül pragma-k segítségével is beállíthatjuk, hogy megosztott legyen a változó vagy a szálra nézve saját példány. Amennyiben nem definiáljuk felül, akkor minden változó megosztott. A felül definiálást a private és a shared paraméterekkel lehet megadni az OpenMP-nek.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int A, B, C;&lt;br /&gt;
&lt;br /&gt;
#pragma omp parallel private(A) shared(B)&lt;br /&gt;
{...}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Az itteni példában az B és C megosztott változó a szálak között, míg A-t minden szálnak lemásolja a futás kezdetén.&lt;br /&gt;
&lt;br /&gt;
====Párhuzamos szálak ütemezése====&lt;br /&gt;
OpenMp a szálak ütemezésére három féle módod kínál fel.&lt;br /&gt;
Módok:&lt;br /&gt;
* '''static''' (alapértelmezett): egyenletesen arányban egymás után elosztja a processzeket az indított szálakon, ezért a futási idő tetszőlegesen elhúzódhat, ha egyes processzek lassabban futnak le.&lt;br /&gt;
* '''dynamic''': minden esetben az első szabad szálra kerül a következő processz ütemezése, folyamatos kihasználtságot elérve, de &amp;quot;költséges&amp;quot; a sok-sok ütemezés&lt;br /&gt;
* '''guided''': Az előző két mód ötvözése. Nagyobb procesz számot ütemez az éppen szabad szálakra, melyek számát folyamatosan csökkenti a futás előre haladtával, hogy körülbelül egyszerre fejeződjön be minden szál futása, ezzel csökkentve a dynamic esetén magad ütemezési időráfordítást.&lt;br /&gt;
&lt;br /&gt;
Mindhárom esetben megadható az egyszerre ütemezett processzek száma, amit a ''chunk'' értéke határoz meg. Ha megvan adva a chunk értéke, akkor az minden esetben annak megfelelő processzámot ütemez a szálakra.&lt;br /&gt;
Használata:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel [schedule (&amp;lt;mode&amp;gt;,&amp;lt;chunk&amp;gt;) {...}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Szálak szinkronizációja====&lt;br /&gt;
Szála szinkronizálását többféle módon lehet megvalósítani, de ezen megoldások sajno jelentősen lelassítják a program futását.&lt;br /&gt;
* mutexxel: nagyon lassú, állandóan várnak egymásra a párhuzamos szálak&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++) {&lt;br /&gt;
#pragma omp critical&lt;br /&gt;
  { // kritikus szekció, csak egy szál hajthatja végre&lt;br /&gt;
    total = total + i;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* atomi művelet definiálása: sajnos még ez is lassú, de itt már nem olyan &amp;quot;erős&amp;quot; a szinkronizáció, ezért rendkívül sok limitációja van az itt használható atomi műveleteknek.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++) {&lt;br /&gt;
#pragma omp atomic&lt;br /&gt;
   total += i;&lt;br /&gt;
}	&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=====Párhuzamos redukció=====&lt;br /&gt;
Az itt bemutatott eljárás gyorsabb mint a fenti szinkronizációs megoldás, de sajnos nem alkalmazható vektorokra! A redukciót a ''reduction(operation: scalar)'' formában kell megadni.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int sum = 0;&lt;br /&gt;
#pragma omp parallel for reduction(+: sum)&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++) {&lt;br /&gt;
  sum = sum + i;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Tulajdonképpen a fenti problémakörre a következő minta ad megoldást. Ekkor egy külön változóban akkumuláltatjuk a szál általi összeget és ezen értékeke összegyűjtését végezzük csak atomi műveletként.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int sum = 0;&lt;br /&gt;
#pragma omp parallel&lt;br /&gt;
{&lt;br /&gt;
  int sum_thr = 0;&lt;br /&gt;
#pragma omp for&lt;br /&gt;
  for (int i=0; i&amp;lt;n; i++)&lt;br /&gt;
    sum_thr += i;&lt;br /&gt;
&lt;br /&gt;
#pragma omp atomic&lt;br /&gt;
  sum += sum_thr;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Ciklusok párhuzamosítása====&lt;br /&gt;
Ciklus párhuzamosítására a következő mintát szokás használni.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel&lt;br /&gt;
{&lt;br /&gt;
  // Az itt definiált forrást minden szál esetén végre fog hajtani&lt;br /&gt;
  ...&lt;br /&gt;
#pragma omp for [schedule (&amp;lt;mode&amp;gt;,&amp;lt;chunk&amp;gt;)&lt;br /&gt;
  for (int i = 0; i &amp;lt; n; i++) {&lt;br /&gt;
    // Ez a kódrész lesz párhuzamosítva&lt;br /&gt;
    ...&lt;br /&gt;
  }&lt;br /&gt;
  // ... és ez a köd is minden szál esetén végrehajtódik&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Ciklus kifejtés====&lt;br /&gt;
A technika már egy példa erejéig a vektorizációnál is látható, de ott nem került részletezésre. A megoldás lényege, hogy a kellően nagy ciklust szétdaraboljuk kisebb darabokra, amit már külön feldolgozhatóak, ezzel párhuzamosítva a folyamatokat. Az egy darab kiindulási ciklusból két darab egymásba ágyazott ciklust hozunk létre, ami ugyanazon a feladattéren dolgozik. &lt;br /&gt;
&lt;br /&gt;
Eredeti állapot:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++) {...}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Átírt változat:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int STRIP=1024;&lt;br /&gt;
for (int ii = 0; ii &amp;lt; n; ii += STRIP)&lt;br /&gt;
 for (int i = ii; i &amp;lt; ii+STRIP; i++) {...}&lt;br /&gt;
&lt;br /&gt;
// ha még van maradék elem, akkor azokon is végrehajtjuk a számításokat&lt;br /&gt;
if (n%STRIP != 0) {&lt;br /&gt;
  for (i=n-(n%STRIP)+1; i&amp;lt;n; i++) {...}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A darabok mérete (STRIP értéke) az adott rendszer és feladat függvényében változhat, tehát ez egyfajta &amp;quot;tuning&amp;quot; paraméter.&lt;br /&gt;
&lt;br /&gt;
====Automatikus ciklusösszevonás====&lt;br /&gt;
Az OpenMP támogatást nyújt több egymásba ágyazott ciklus &amp;quot;összevonására&amp;quot;. Ehhez csak a '''collaps''' fordítási direktívát és az összevonandó ciklusok számát kell megadni. &amp;lt;br /&amp;gt;&lt;br /&gt;
FONTOS: ekkor az automatikus vektorizáció kikapcsolásra kerül!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for collaps(2)&lt;br /&gt;
for (int i=0; i&amp;lt;n; i++)&lt;br /&gt;
  for( int j=0; j&amp;lt;m; j++){...}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A fneti példában a 2db for ciklis kerül párhuzamosításra úgy, hogy a fordítás során 1db ciklus generálódik belőle, ami a következő paraméterezéssel ekvivalens:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for&lt;br /&gt;
for (int c=0; c &amp;lt; n*m; c++) {&lt;br /&gt;
  int i = c/n;&lt;br /&gt;
  int j = c%n;&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
[[Kategória: HPC]]&lt;/div&gt;</summary>
		<author><name>Szigeti(AT)niif.hu</name></author>	</entry>

	<entry>
		<id>https://wiki.niif.hu/index.php?title=Intel_Xeon_Phi&amp;diff=3764</id>
		<title>Intel Xeon Phi</title>
		<link rel="alternate" type="text/html" href="https://wiki.niif.hu/index.php?title=Intel_Xeon_Phi&amp;diff=3764"/>
				<updated>2016-05-06T07:16:37Z</updated>
		
		<summary type="html">&lt;p&gt;Szigeti(AT)niif.hu: /* Phi használat slurm-mel */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Az oldalon található információk segítséget nyújtanak a HPC felhasználóinknak, hogy alkalmazásaikat miként portolhatják valamint optimalizálhatják a hatékonyabb futást szem előtt tartva Intel Xeon Phi koprocesszor kártyákon.&lt;br /&gt;
&lt;br /&gt;
==Hardver bemutatása==&lt;br /&gt;
Intel® Xeon Phi™ Coprocessor 7120P&lt;br /&gt;
&amp;lt;gallery&amp;gt;&lt;br /&gt;
Fájl:Xeon-phi-family-rwd.png.rendition.intel.web.720.405.png|&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-  align=&amp;quot;center&amp;quot;&lt;br /&gt;
! CPU&lt;br /&gt;
! threads/Core&lt;br /&gt;
!max threads&lt;br /&gt;
! CPU Freq&lt;br /&gt;
! Memória&lt;br /&gt;
! L1 cache&lt;br /&gt;
! L2 cache&lt;br /&gt;
! Interfész&lt;br /&gt;
! Ptot&lt;br /&gt;
! Számítási kapacitás&lt;br /&gt;
|-  align=&amp;quot;center&amp;quot;&lt;br /&gt;
| 61&lt;br /&gt;
| 4 Hardver&lt;br /&gt;
| 244&lt;br /&gt;
| 1238 GHz&lt;br /&gt;
| 16GB GDDR5&lt;br /&gt;
| 32KB/core&lt;br /&gt;
| 512KB/core&lt;br /&gt;
| PCIe&lt;br /&gt;
| 300W&lt;br /&gt;
| 1,2 TFlop/s dupla pontosság&lt;br /&gt;
|-  align=&amp;quot;center&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
| OpenMP-nél csak '''240'''&lt;br /&gt;
|&lt;br /&gt;
| ~100 cycles&lt;br /&gt;
~174 GB/s&lt;br /&gt;
| ~3 cycles&lt;br /&gt;
| ~10 cycles&lt;br /&gt;
| ~7 GB/s&lt;br /&gt;
~1 microsecond&lt;br /&gt;
|&lt;br /&gt;
| 2,4 TFlop/s egyszeres pontosság&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Az Intel® 1. generációs Xeon Phi™ koprocesszorában található 61 processzor ugyanazt a 16GB memóriát éri el, de semmilyen módon se fér hozzá közvetlenül a hoszt gép fizikai memóriájához.&amp;lt;br /&amp;gt;&lt;br /&gt;
[[Budapest2_klaszter|Budapest2]] és [[HPC#Szupersz.C3.A1m.C3.ADt.C3.B3g.C3.A9peink_.C3.B6sszehasonl.C3.ADt.C3.A1sa|Debrecen3-Phi]] gépeinkben számítási nodonként 2-2 Phi™ koprocesszor található. &amp;lt;br/&amp;gt;&lt;br /&gt;
A kártyákon speciális célú Linux kernel fut melyre a belépés limitáltan csak a rendszergazdák számára lehetséges SSH-n keresztül a hoszt gépről. Természetesen a kártyára belépve olyan érzésünk lehet, mintha egy sok processzoros gépen dolgoznánk, ami rendelkezik rengeteg memóriával. Ez így is van, de a hatékonyabb kihasználás érdekében az SSH-n keresztüli elérés nem lehetséges, csak a Slurm segítségével valamint a programozási nyelvek által támogatott csatornákon keresztül. Ennek részletezése lentebb megtalálható.&lt;br /&gt;
&lt;br /&gt;
==Használati módok==&lt;br /&gt;
A koprocesszor használatai módjait két csoportba lehet sorolni. A '''natív''' és az '''offload''' használatot.  &amp;lt;br/&amp;gt;&lt;br /&gt;
Ezen használati módokról a lenti fejezetekben több részletet is megtudhat. Az itt megfogalmazott &amp;quot;támogatás&amp;quot; csak a mi infrastruktúránkra érvényes! &amp;lt;br /&amp;gt;&lt;br /&gt;
A bemutatott példák ''c++'' nyelven vannak, de természetesen ''c'' és ''fortran'' esetén is megtalálható az ekvivalens módszer.&lt;br /&gt;
&lt;br /&gt;
===a) nem támogatott mód===&lt;br /&gt;
====közvetlen natív használat====&lt;br /&gt;
A natív mód arra szolgál, hogy a MIC kártyára  lefordított alkalmazásunkat felmásoljuk a kártyára, majd belépünk SSH-n a kártyára és azon futtatjuk a bináris kódunkat. &amp;lt;br /&amp;gt;&lt;br /&gt;
Ez a legegyszerűbb használati mód, ugyanis nincs más dolgunk, csak a '''-mmic''' kapcsolót használni a fordításnál és a fordító egy máris a MIC architektúrára kész bináris készít nekünk. &amp;lt;br/&amp;gt;&lt;br /&gt;
Workflow a következő lenne:&lt;br /&gt;
# Alkalmazás megírása. például [[Média:hello.cc|''hello.cc'']]&lt;br /&gt;
# Alkalmazás fordítása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;icpc -o hello-MIC -mmic hello.cc&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Másolás a MIC kártyára &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;scp hello-MIC mic0:&amp;lt;/pre&amp;gt;&lt;br /&gt;
# belépés a kártyára &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;ssh mic0&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Alkalmazás futtatása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;./hello-MIC&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===b) támogatott, de nem preferált használat===&lt;br /&gt;
====automatikus kódátvitel====&lt;br /&gt;
Ebben az esetben nem kell átmásolnunk a bináris alkalmazásunkat a kártyára sőt még csak be se kell lépnünk, mert ezt a két lépést egy beépített tool végzi el. &amp;lt;br/&amp;gt;&lt;br /&gt;
A tool eléréséhez szükséges parancs neve: '''micnativeloadex'''&lt;br /&gt;
&lt;br /&gt;
Ebben az esetben a workflow a következőképpen módosul:&lt;br /&gt;
# Alkalmazás megírása. például [[Média:hello.cc|''hello.cc'']]&lt;br /&gt;
# Alkalmazás fordítása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;icpc -o hello-MIC -mmic hello.cc&amp;lt;/pre&amp;gt;&lt;br /&gt;
# A tool futásához szükséges library-k megadása &amp;lt;br&amp;gt; &amp;lt;pre&amp;gt;export SINK_LD_LIBRARY_PATH=/opt/intel/composerxe/compiler/lib/mic&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Alkalmazás futtatása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;micnativeloadex hello-MIC&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| BUDAPEST2[cn10] phi (0)$ export SINK_LD_LIBRARY_PATH=/opt/intel/composerxe/compiler/lib/mic &amp;lt;br/&amp;gt;BUDAPEST2[cn10] phi (0)$ micnativeloadex hello-MIC &amp;lt;br/&amp;gt;Hello world! I have 244 logical cores.&amp;lt;br/&amp;gt;BUDAPEST2[cn10] phi (0)$&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===c) támogatott módok===&lt;br /&gt;
====Offload====&lt;br /&gt;
Az Offload modell esetén szükséges a forráskód módosítása, mégpedig úgy, hogy azon kódrészletet, amit a koprocesszoron szeretnénk futtatni, ki kell egészíteni egy fordítási direktívával. Az alkalmazás fordításakor elegendő csak a CPU-ra való binárist készíteni mivel a modell végzi a megfelelő forrás részlet mozgatását és futtatását.&amp;lt;br /&amp;gt;&lt;br /&gt;
[[Fájl:offload.png|Offload modell]] &amp;lt;br /&amp;gt;&lt;br /&gt;
Példánkban jól látható, hogy a második kiíratás már a koprocesszoron fut.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
int main(int argc, char * argv[]) {&lt;br /&gt;
  printf(&amp;quot;Hello world from host!\n&amp;quot;);&lt;br /&gt;
#pragma offload target(mic)&lt;br /&gt;
  {&lt;br /&gt;
    printf(&amp;quot;Hello world from coprocessor!\n&amp;quot;); fflush(stdout);&lt;br /&gt;
  }&lt;br /&gt;
  printf(&amp;quot;Bye\n&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ebben a példában az operációs rendszer választja ki, hogy melyik Phi kártyán fusson le a kódrészlet. Amennyiben mi szeretnénk explicite megmondani, hogy melyik kártyán fusson, akkor a fordítási direktívában a mic kulcsszó után kell tenni egy &amp;quot;:&amp;quot;-t és a kártya sorszámát [mi esetünkben 0 vagy 1]. Ekkor így nézne ki a direktíva: ''#pragma offload target(mic:1)''&lt;br /&gt;
&lt;br /&gt;
Amennyiben az offload szekcióban függvény hívás is szerepel, akkor szükségeltetik az függvény futásához szükséges változók adatterületének átmásolása a Phi kártyára, majd a lefutás után visszamásolni a hoszt gépre. Ezen másolást az ''offload_attribute'' ''push'' és ''pop'' paraméterei segítségével határozhatjuk meg. &amp;lt;br /&amp;gt;&lt;br /&gt;
A következő mátrix-vektor szorzás példánál az A mátrix globális változó, míg a b és c vektorok a függvény paraméterei.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//A változók felmásolásra kerülnek a Xeon Phi-re&lt;br /&gt;
#pragma offload_attribute(push, target(mic))&lt;br /&gt;
  double* A;&lt;br /&gt;
  void MatrixVector(double *b, double *c, double m, double n) {&lt;br /&gt;
    ...&lt;br /&gt;
    c[] = A[..]*b[]&lt;br /&gt;
  }&lt;br /&gt;
// felmásolt objektumok visszamásolása&lt;br /&gt;
#pragma offload_attribute(pop)&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
  ...&lt;br /&gt;
#pragma offload target(mic) in(A[0:n*m])&lt;br /&gt;
// így is meg lehetne adni: #pragma offload target(mic:0) in(A : length(n*m))	        &lt;br /&gt;
  {&lt;br /&gt;
    MatrixVector(b, c, m, n);&lt;br /&gt;
  }&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ekkor minden memória területet 2x kell másolni, egyszer fel a kártyára, majd másodszor vissza a hoszt gépre, pedig elegendő lenne csak az eredmény vektort visszamásolni. Ezen optimalizálással egy későbbi fejezetben foglalkozunk. &amp;lt;p /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Futás során kaphatunk részletes riportot is, hogy pontosan milyen adatmozgatások történnek az alkalmazás futása során. Ehhez nincs szükség másra csak az '''OFFLOAD_REPORT''' környezeti változó megfelelő beállítása. A beállított értékek 1, 2 vagy 3 lehetnek, attól függően mennyire részletes riportot szeretnénk kapni. &amp;lt;br /&amp;gt;&lt;br /&gt;
beállítás:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export OFFLOAD_REPORT=3&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
példa kimenet a hello alkalmazásra:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Hello world from host!&lt;br /&gt;
[Offload] [MIC 0] [File]                    hello_offload.cc&lt;br /&gt;
[Offload] [MIC 0] [Line]                    5&lt;br /&gt;
[Offload] [MIC 0] [Tag]                     Tag 0&lt;br /&gt;
Hello world from coprocessor!&lt;br /&gt;
[Offload] [HOST]  [Tag 0] [CPU Time]        0.554653(seconds)&lt;br /&gt;
[Offload] [MIC 0] [Tag 0] [CPU-&amp;gt;MIC Data]   0 (bytes)&lt;br /&gt;
[Offload] [MIC 0] [Tag 0] [MIC Time]        0.000140(seconds)&lt;br /&gt;
[Offload] [MIC 0] [Tag 0] [MIC-&amp;gt;CPU Data]   0 (bytes)&lt;br /&gt;
Bye&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Offload + OpenMP====&lt;br /&gt;
Amennyiben nem csak 1 processzort szeretnénk használni a Phi kártyánkon és az alkalmazásunkban használt számítás megengedi a párhuzamosítást, akkor az OpenMP segítségével párhuzamosíthatjuk a megfelelő számítást. &amp;lt;br /&amp;gt;&lt;br /&gt;
[[Fájl:Offload+openmp.png|Offload modell és OpenMP]] &amp;lt;br /&amp;gt;&lt;br /&gt;
Példánk maradjon ugyanúgy a mátrix-vektor szorzás. Amennyiben a teljes kártya adta kapacitást ki szeretnénk használni, akkor az OMP threadek számát 240-re kell beállítani. Azért nem 244-re, mivel 1 CPU-t dedikáltan csak a kommunikációra használ a rendszer és 1 CPU 4 hardver threaddel rendelkezik.&lt;br /&gt;
Ekkor a példánk így változik:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;omp.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#pragma offload_attribute(push, target(mic))&lt;br /&gt;
void multiply(int n, int m, double* A, double* b, double* c){&lt;br /&gt;
  const int nCPUs = omp_get_max_threads();   //240&lt;br /&gt;
  omp_set_num_threads(nCPUs);&lt;br /&gt;
#pragma omp parallel for schedule(dynamic, 1)&lt;br /&gt;
  for ( int i = 0 ; i &amp;lt; m ; i++)&lt;br /&gt;
     for ( int j = 0 ; j &amp;lt; n ; j++)&lt;br /&gt;
       c[i] += A[i*n+j] * b[j];&lt;br /&gt;
}&lt;br /&gt;
#pragma offload_attribute(pop)&lt;br /&gt;
&lt;br /&gt;
int main(){&lt;br /&gt;
  double * A = (double*) malloc(sizeof(double)*n*m);&lt;br /&gt;
  ...&lt;br /&gt;
#pragma offload target(mic) in(A:length(n*m))&lt;br /&gt;
     multiply(n, m, A, b, c);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Az alkalmazás fordításánál a fordítónak meg kell adni, hogy használhja az openmp library-ket is. Ezt a '''-qopenmp''' kapcsolóval tudjuk elérni.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
icpc -o matrix-CPU -qopenmp matrix.cc&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====MPI + Offload + OpenMP====&lt;br /&gt;
Abban az esetben, ha 1 számítási node nem elég, MPI segítségével össze tudunk kötni több nodet, heterogén rendszert alkotva, és azokon alkalmazható a fenti Offload + OpenMP modell. &amp;lt;br /&amp;gt;&lt;br /&gt;
[[Fájl:Mpi+offload+openmp.png|MPI elosztás valamint Offload modell és OpenMP]] &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Phi használat slurm-mel==&lt;br /&gt;
Slurm esetén a mic opciót kell megadni a gres-knél. Amennyiben egynél több kártyára van szükség, akkor azt a ''mic:2''-vel lehetséges megadni. Ekkor két kártyát foglal le az ütemező a futtatandó feladatnak. &amp;lt;br /&amp;gt;&lt;br /&gt;
Egy lehetséges feladat, amit srun-nal lehet a slurmben beküldeni. debrecen3 esetén a ''prod-phi'' particiót kell használni&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ cat slurm_job_openmp_phi&lt;br /&gt;
&lt;br /&gt;
#!/bin/bash&lt;br /&gt;
#SBATCH -A &amp;lt;PROJECT NEVE&amp;gt;&lt;br /&gt;
#SBATCH --job-name=&amp;lt;JOB NEVE&amp;gt;&lt;br /&gt;
#SBATCH --gres mic:2&lt;br /&gt;
#SBATCH --time=1:00:00&lt;br /&gt;
#SBATCH --partition=prod&lt;br /&gt;
#SBATCH -o slurm-%A.out&lt;br /&gt;
export OMP_NUM_THREADS=2&lt;br /&gt;
./runme_openmp_2phi&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Ekkor a feladat beküldése a következő paranccsal lehetséges.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
srun ./slurm_job_openmp_phi&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Alkalmazások optimalizálása a kártyára==&lt;br /&gt;
Ebben a fejezetben optimalizálási lehetőségeket mutatunk be, melyek segítségül szolgálhatnak az egyes alkalmazások hatékonyabb futása érdekében ezzel jobban kihasználni a rendelkezésre álló erőforrások adta számítási kapacitás.&lt;br /&gt;
&lt;br /&gt;
===Környezeti változók===&lt;br /&gt;
Lehetséges csak a Phi kártyákon használt környezeti változók beállítása, amit az alkalmazásunk használni is tud. &amp;lt;br /&amp;gt;&lt;br /&gt;
Példánkban az OpenMP esetén használható szálak számát állíthatjuk be:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export MIC_ENV_PREFIX=XEONPHI&lt;br /&gt;
export OMP_NUM_THREADS=12&lt;br /&gt;
export XEONPHI_OMP_NUM_THREADS=240&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
A beállított prefix tetszőleges karaktersorozat lehet, a lényeg, hogy ezt a beállított prefixet használjuk a környezeti változók esetén is!&lt;br /&gt;
 &lt;br /&gt;
===Hosszredukció===&lt;br /&gt;
Többnyire a fordítók, ha nem mondjuk meg explicite, a dupla pontosságú változatot választják a lehetséges változó- és függvény típusok közül. Amennyiben a számításunk nem igényli a dupla pontosságot, akkor használjuk a forrásban mindenhol az adott típus vagy függvény egyszeres pontosságú változatát, mellyel jelentős teljesítmény növekedést tudunk elérni. Továbbá az egyes műveletek futási ideje nem egyforma, így a műveletet helyettesíthetjük más művelettel, ami gyorsabb, akkor ismét javíthatunk a hatékonyságon. &amp;lt;br /&amp;gt;&lt;br /&gt;
Íme pár példa, amit alkalmazni lehet:&lt;br /&gt;
* &amp;quot;''/''&amp;quot; művelet helyett &amp;quot;''*''&amp;quot; -t használjunk, mert azt gyorsabban végre tudja hajtani a processzor&lt;br /&gt;
* ''pow'' =&amp;gt; ''1.0 / sqrt()''    , tetszőleges hatványozás, pl.: 3/2-ik hatvány&lt;br /&gt;
* hardver áltatl támogatott műveletek:&lt;br /&gt;
** ''exp'' =&amp;gt; ''exp2''&lt;br /&gt;
** ''log'' =&amp;gt; ''log2''&lt;br /&gt;
* dupla pontosság -&amp;gt; egyszeres pontosság lebegőpontos számok és műveletek esetén &lt;br /&gt;
** ''1.0'' =&amp;gt; ''1.0f''&lt;br /&gt;
** ''pow'' =&amp;gt; ''powf''&lt;br /&gt;
&lt;br /&gt;
===Vektorizáció===&lt;br /&gt;
A vektorizáció azon fordítási időben eldönthető optimalizációs faktor, amely nagy mértékben függ attól a hardver felépítésétől, amire éppen a fordítás történni fog. Legfontosabb befolyásoló tényezők a processzor cache méretei illetve a vektor műveletekért felelős aritmetikai egységének jellemzői mivel ezeket használja fel a fordító az optimalizálási folyamata során. &amp;lt;br /&amp;gt;&lt;br /&gt;
Az automatikus vektorizáció minimum -O2 szinttől érvényes, de mivel általában ez a szint az alapértelmezett, ezért minden fordításnál alkalmazásra kerül. &amp;lt;br /&amp;gt;&lt;br /&gt;
A fordító programnak a következő kapcsolókat lehet megadni, melyek segítenek a minél jobb vektorizációban:&lt;br /&gt;
* ''-qopt-report=&amp;lt;szám&amp;gt;'' ; az optimalizációs jelentés részletességét lehet megadni 0-tól 5-ig, ahol az 5-s szint a legrészletesebbet jelenti&lt;br /&gt;
* ''-qopt-report-phase:vec'' ; a vektorizációról szeretnék jelentést kapni&lt;br /&gt;
* ''-opt-report-file=&amp;lt;fájlnév&amp;gt;'' ; megadható a riportfájl neve&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
icpc autovec.cc -mmic -qopt-report=5 -opt-report-file=vectorization.oMIC.optrpt -qopt-report-phase:vec&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====SIMD====&lt;br /&gt;
Az adatok hatékonyabb feldolgozására az egyik megvalósítás a SIMD (Single Instruction Multiple Data), amikor is a különálló adatokat úgy kezeli, mintha az vektorok volnának és azon végzi el a vektor-műveletet. Ezzel egy a hardver által támogatott műveletet végzünk egy lépésben egy nagyobb adatmennyiségen, ezzel párhuzamosítva a feldolgozási folyamatot. Ezzel a módszerrel nagyon jó adat szintű párhuzamosítást lehet elérni.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Fájl:Simd.png|SIMD]]&lt;br /&gt;
&lt;br /&gt;
Főbb támogatott műveletek:&lt;br /&gt;
* aritmetikai műveletek, bináris operátorok&lt;br /&gt;
* összehasonlítások&lt;br /&gt;
* konverziók és típus kasztolások&lt;br /&gt;
* bit-műveletek: NOT, AND, OR, XOR, XAND&lt;br /&gt;
&lt;br /&gt;
Használata: '''simd''' fordítási direktívával lehetséges &amp;lt;br /&amp;gt;&lt;br /&gt;
Például:&lt;br /&gt;
* egyszerű vektorizáció&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma simd&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
  A[i] += B[i];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* adat párhuzamosítás OpenMP segítségével&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for simd&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
  DoSomeWork(A[i]);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====AoS -&amp;gt; SoA====&lt;br /&gt;
A kiindulás probléma az, hogy a struktúrákat tartalmazó vektort nem lehet jól vektorizálni, mivel a cache-ben tárolt adatok nagy többsége feleslegesen van bent, mivel azon adatokat csak egy későbbi ciklus futás során fogja az alkalmazás feldolgozni. Tehát ezen adatok jelenleg feleslegesen foglalják a cache területet, ahelyett, hogy hasznos adatot tárolnánk ott.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
struct MyStructure {&lt;br /&gt;
  float x, y, z;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void My_Function(cons int nItem, MyStructure* const Items, const float delta) {&lt;br /&gt;
  for (int i = 0; i &amp;lt; nItem; i++) {&lt;br /&gt;
    ...&lt;br /&gt;
    for (int j = 0; j &amp;lt; nItem; j++) {&lt;br /&gt;
      const float dx = Items[j].x - Items[i].x;&lt;br /&gt;
      const float dy = Items[j].y - Items[i].y;&lt;br /&gt;
      const float dz = Items[j].z - Items[i].z;&lt;br /&gt;
      ...  &lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  ... &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A vektorban a struktúrák tárolása helyett minden elemnek egy dedikált vektort allokálunk és így könnyen tudja a fordító vektorizálni azt. Természetesen segítségként a vektor feldolgozását &amp;quot;tömbösítettük&amp;quot; a tileSize méret megadásával illetve a ''unroll'' fordítási direktíva segítségével.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
struct MyStructureSet {&lt;br /&gt;
  float *x, *y, *z;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void My_Function(cons int nItem, MyStructure* const Items, const float delta) {&lt;br /&gt;
  const int tileSize = 16;&lt;br /&gt;
#pragma omp parallel for schedule(guided)&lt;br /&gt;
  for (int ii = 0; ii &amp;lt; nItem; ii+= tileSize) {&lt;br /&gt;
    ...&lt;br /&gt;
#pragma unroll(tileSize)&lt;br /&gt;
    for (int j = 0; j &amp;lt; nItem; j++) {&lt;br /&gt;
#pragma unroll(tileSize)&lt;br /&gt;
      for (int i = ii; i &amp;lt; ii + tileSize; i++) {&lt;br /&gt;
        const float dx = Items.x[j] - Items.x[i];&lt;br /&gt;
        const float dy = Items.y[j] - Items.y[i];&lt;br /&gt;
        const float dz = Items.z[j] - Items.z[i];&lt;br /&gt;
        ...&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  ... &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Hatékony hossz választás====&lt;br /&gt;
Olyan esetben jelent előnyt, ha például az n*n-es mátrix esetén n nem osztható 16-val, mert akkor a vektorizáció nem teljesen hajtható végre. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// n%16 != 0&lt;br /&gt;
float* A = _mm_malloc(sizeof(float)*n*n, 64);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Ilyen esetben mindig marad egy kis vektor részlet, amire elemenként kell elvégezni a műveletet.&amp;lt;br /&amp;gt;&lt;br /&gt;
A megoldást az jelenti, hogy a memória allokálást kiterjesztjük és nagyobb memória területet foglalunk a változónak, így már alkalmazható lesz a vektorizáció, mivel a maradvány területen is el tudjuk végezni a műveletet egy lépésben.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int lda=n;&lt;br /&gt;
if (n % 16 != 0) lda += (16 - n%16); // most lda%16==0&lt;br /&gt;
float* A = _mm_malloc(sizeof(float)*n*lda, 64);&lt;br /&gt;
&lt;br /&gt;
// használat&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
  for (int j = 0; j &amp;lt; n; j++)&lt;br /&gt;
    A[i*lda + j] = ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====&amp;quot;Héj ciklusok&amp;quot; kiküszöbölése====&lt;br /&gt;
Ciklusok esetén lehetséges, hogy a vektorizációt nem teljes mértékben ismeri fel a fordító program. &lt;br /&gt;
Ekkor segíthetünk a fordítónak egy fordítási direktívával ('''#pragma vector aligned'''), ha teljes mértékben biztosak vagyunk, hogy a ciklusunk vektorizálható!&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma vector aligned&lt;br /&gt;
  for (int j = 0; j &amp;lt; n; j++)&lt;br /&gt;
    A[i*lda + j] -= ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Ettől jelentős futás idejű javulást érhetünk el, mivel ezzel a #pragma-val kikapcsoltuk a futás idejű ellenőrzést, de előfordulhatnak olyan esetek, főleg amikor nem jól mértük fel a ciklusunkat és nem sikerült a vektorizálás, hogy ''Segmentation fault'' hibára fogunk futni.&lt;br /&gt;
&lt;br /&gt;
====Regularizáció====&lt;br /&gt;
Van egy ciklusunk, ami egy vektor elemeit módosítja. A processzor az adatokat &amp;quot;csokorba szedve&amp;quot; emeli be a belső kis méretű cache-be. Egyszerre természetesen több vektor elem is bekerül a a cache-be ezzel gyorsítva a feldolgozást. Ezen vektor iterációk valahogyan ráilleszkedik a vektorra.&lt;br /&gt;
&lt;br /&gt;
[[Fájl:regularization.png|Vektor regularizáció]]&lt;br /&gt;
&lt;br /&gt;
Abban az esetben, amikor ez az illesztés nem teljesen tökéletes (lsd. piros elemek), keletkeznem héj ciklusok, melyeket a regularizációval is eliminálni lehet. &amp;lt;br /&amp;gt;&lt;br /&gt;
A feladat eredetileg valahogyan így nézhet ki:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
for (int i = 0; i&amp;lt;n; i++)&lt;br /&gt;
  A[i]=...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ez a következő átalakítással már alkalmas vektorizációra és nem lesz &amp;quot;peel loop&amp;quot; probléma.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
for (int b=0; b&amp;lt;n; b++) {&lt;br /&gt;
  const int jStart = b- b%16; // mindig osztható 16-val&lt;br /&gt;
  ...&lt;br /&gt;
#pragma simd&lt;br /&gt;
  for (int j = jStart; j&amp;lt;n; j++)&lt;br /&gt;
    A[j]=...&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Párhuzamosítás===&lt;br /&gt;
Több féle mód létezik a párhuzamosításra. Korábban említettük a SIMD technikát, ami adatpárhuzamosításra való 1 processzoron belül, de létezik még processzorok közti párhuzamosítás, amit az OpenMP segítségével tudunk hatékonyan megvalósítani. Amennyiben gépek közti párhuzamosításra, üzenek küldésre van szükség, akkor az MPI-t kell segítségül hívni. Általános megoldás nem létezik minden feladatra, mert MPI is alkalmas lehet gépen belüli párhuzamosításra, nem csak az OpenMP. Amit mérlegelni kell, hogy mi az amire az alkalmazásnak szükséges van illetve mekkora kommunikációs overhead keletkezik, ha lecseréljük az OpenMP-t MPI-ra. Általában az egymástól független számításokat OpenMP-vel, míg a kommunikációt igénylő feladatokra MPI-t szoktak használni.&lt;br /&gt;
&lt;br /&gt;
====Szálak párhuzamosítása====&lt;br /&gt;
Az OpenMP-nek környezeti változók segítségével megadható a párhuzamosan futtatható szálak száma. Ezt a következő paranccsal tudjuk megtenni, ahol 5-re állítjuk.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export OMP_NUM_THREADS=5&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Forrásban a párhuzamos szálakat a ''#pragma omp parallel'' segítségével adhatjuk meg.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for              	//külső for szálak párhuzamosítása&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
#pragma simd				// vektorizáció a belső cikluson&lt;br /&gt;
  for (int j = 0; j &amp;lt; m; j++)&lt;br /&gt;
    My_Function(A[i][j]);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A fordítási direktívák össze is vonhatóak, ilyenkor 1 ciklus is elég.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for simd&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
    My_Function(A[i]);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=====Változók láthatósága, megosztása=====&lt;br /&gt;
Változók hatókörét a programozási nyelven kívül pragma-k segítségével is beállíthatjuk, hogy megosztott legyen a változó vagy a szálra nézve saját példány. Amennyiben nem definiáljuk felül, akkor minden változó megosztott. A felül definiálást a private és a shared paraméterekkel lehet megadni az OpenMP-nek.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int A, B, C;&lt;br /&gt;
&lt;br /&gt;
#pragma omp parallel private(A) shared(B)&lt;br /&gt;
{...}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Az itteni példában az B és C megosztott változó a szálak között, míg A-t minden szálnak lemásolja a futás kezdetén.&lt;br /&gt;
&lt;br /&gt;
====Párhuzamos szálak ütemezése====&lt;br /&gt;
OpenMp a szálak ütemezésére három féle módod kínál fel.&lt;br /&gt;
Módok:&lt;br /&gt;
* '''static''' (alapértelmezett): egyenletesen arányban egymás után elosztja a processzeket az indított szálakon, ezért a futási idő tetszőlegesen elhúzódhat, ha egyes processzek lassabban futnak le.&lt;br /&gt;
* '''dynamic''': minden esetben az első szabad szálra kerül a következő processz ütemezése, folyamatos kihasználtságot elérve, de &amp;quot;költséges&amp;quot; a sok-sok ütemezés&lt;br /&gt;
* '''guided''': Az előző két mód ötvözése. Nagyobb procesz számot ütemez az éppen szabad szálakra, melyek számát folyamatosan csökkenti a futás előre haladtával, hogy körülbelül egyszerre fejeződjön be minden szál futása, ezzel csökkentve a dynamic esetén magad ütemezési időráfordítást.&lt;br /&gt;
&lt;br /&gt;
Mindhárom esetben megadható az egyszerre ütemezett processzek száma, amit a ''chunk'' értéke határoz meg. Ha megvan adva a chunk értéke, akkor az minden esetben annak megfelelő processzámot ütemez a szálakra.&lt;br /&gt;
Használata:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel [schedule (&amp;lt;mode&amp;gt;,&amp;lt;chunk&amp;gt;) {...}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Szálak szinkronizációja====&lt;br /&gt;
Szála szinkronizálását többféle módon lehet megvalósítani, de ezen megoldások sajno jelentősen lelassítják a program futását.&lt;br /&gt;
* mutexxel: nagyon lassú, állandóan várnak egymásra a párhuzamos szálak&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++) {&lt;br /&gt;
#pragma omp critical&lt;br /&gt;
  { // kritikus szekció, csak egy szál hajthatja végre&lt;br /&gt;
    total = total + i;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* atomi művelet definiálása: sajnos még ez is lassú, de itt már nem olyan &amp;quot;erős&amp;quot; a szinkronizáció, ezért rendkívül sok limitációja van az itt használható atomi műveleteknek.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++) {&lt;br /&gt;
#pragma omp atomic&lt;br /&gt;
   total += i;&lt;br /&gt;
}	&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=====Párhuzamos redukció=====&lt;br /&gt;
Az itt bemutatott eljárás gyorsabb mint a fenti szinkronizációs megoldás, de sajnos nem alkalmazható vektorokra! A redukciót a ''reduction(operation: scalar)'' formában kell megadni.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int sum = 0;&lt;br /&gt;
#pragma omp parallel for reduction(+: sum)&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++) {&lt;br /&gt;
  sum = sum + i;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Tulajdonképpen a fenti problémakörre a következő minta ad megoldást. Ekkor egy külön változóban akkumuláltatjuk a szál általi összeget és ezen értékeke összegyűjtését végezzük csak atomi műveletként.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int sum = 0;&lt;br /&gt;
#pragma omp parallel&lt;br /&gt;
{&lt;br /&gt;
  int sum_thr = 0;&lt;br /&gt;
#pragma omp for&lt;br /&gt;
  for (int i=0; i&amp;lt;n; i++)&lt;br /&gt;
    sum_thr += i;&lt;br /&gt;
&lt;br /&gt;
#pragma omp atomic&lt;br /&gt;
  sum += sum_thr;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Ciklusok párhuzamosítása====&lt;br /&gt;
Ciklus párhuzamosítására a következő mintát szokás használni.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel&lt;br /&gt;
{&lt;br /&gt;
  // Az itt definiált forrást minden szál esetén végre fog hajtani&lt;br /&gt;
  ...&lt;br /&gt;
#pragma omp for [schedule (&amp;lt;mode&amp;gt;,&amp;lt;chunk&amp;gt;)&lt;br /&gt;
  for (int i = 0; i &amp;lt; n; i++) {&lt;br /&gt;
    // Ez a kódrész lesz párhuzamosítva&lt;br /&gt;
    ...&lt;br /&gt;
  }&lt;br /&gt;
  // ... és ez a köd is minden szál esetén végrehajtódik&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Ciklus kifejtés====&lt;br /&gt;
A technika már egy példa erejéig a vektorizációnál is látható, de ott nem került részletezésre. A megoldás lényege, hogy a kellően nagy ciklust szétdaraboljuk kisebb darabokra, amit már külön feldolgozhatóak, ezzel párhuzamosítva a folyamatokat. Az egy darab kiindulási ciklusból két darab egymásba ágyazott ciklust hozunk létre, ami ugyanazon a feladattéren dolgozik. &lt;br /&gt;
&lt;br /&gt;
Eredeti állapot:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++) {...}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Átírt változat:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int STRIP=1024;&lt;br /&gt;
for (int ii = 0; ii &amp;lt; n; ii += STRIP)&lt;br /&gt;
 for (int i = ii; i &amp;lt; ii+STRIP; i++) {...}&lt;br /&gt;
&lt;br /&gt;
// ha még van maradék elem, akkor azokon is végrehajtjuk a számításokat&lt;br /&gt;
if (n%STRIP != 0) {&lt;br /&gt;
  for (i=n-(n%STRIP)+1; i&amp;lt;n; i++) {...}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A darabok mérete (STRIP értéke) az adott rendszer és feladat függvényében változhat, tehát ez egyfajta &amp;quot;tuning&amp;quot; paraméter.&lt;br /&gt;
&lt;br /&gt;
====Automatikus ciklusösszevonás====&lt;br /&gt;
Az OpenMP támogatást nyújt több egymásba ágyazott ciklus &amp;quot;összevonására&amp;quot;. Ehhez csak a '''collaps''' fordítási direktívát és az összevonandó ciklusok számát kell megadni. &amp;lt;br /&amp;gt;&lt;br /&gt;
FONTOS: ekkor az automatikus vektorizáció kikapcsolásra kerül!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for collaps(2)&lt;br /&gt;
for (int i=0; i&amp;lt;n; i++)&lt;br /&gt;
  for( int j=0; j&amp;lt;m; j++){...}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A fneti példában a 2db for ciklis kerül párhuzamosításra úgy, hogy a fordítás során 1db ciklus generálódik belőle, ami a következő paraméterezéssel ekvivalens:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for&lt;br /&gt;
for (int c=0; c &amp;lt; n*m; c++) {&lt;br /&gt;
  int i = c/n;&lt;br /&gt;
  int j = c%n;&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Szigeti(AT)niif.hu</name></author>	</entry>

	<entry>
		<id>https://wiki.niif.hu/index.php?title=HPC&amp;diff=3763</id>
		<title>HPC</title>
		<link rel="alternate" type="text/html" href="https://wiki.niif.hu/index.php?title=HPC&amp;diff=3763"/>
				<updated>2016-05-06T07:12:08Z</updated>
		
		<summary type="html">&lt;p&gt;Szigeti(AT)niif.hu: /* Saját HPC oktató tartalmaink */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
[[HPC_en|NIIF HPC in ENGLISH]]&lt;br /&gt;
&lt;br /&gt;
== A szuperszámítógépekről ==&lt;br /&gt;
Az NIIF Intézet szuperszámítógép szolgáltatása tudományos számítási feladatok futtatására, valamint tudományos célú adattárolásra szolgál. A jelenleg integrált szuperszámítógép-rendszer komponensei négy helyszínen találhatók:&lt;br /&gt;
&lt;br /&gt;
* Debreceni Egyetem&lt;br /&gt;
* NIIFI központ&lt;br /&gt;
* Pécsi Tudományegyetem&lt;br /&gt;
* Szegedi Tudományegyetem&lt;br /&gt;
* Miskolci Egyetem&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Fájl:Orszagos-attekinto_hpc.png|center|700px|alt=Az NIIF szuperszámítógépei térképen]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
A tudományos számítási feladatok különböző típusainak minél szélesebb körű lefedettsége érdekében az egyes helyszíneken különböző felépítésű gépek találhatók: két helyszínen ccNUMA, három helyszínen pedig &amp;quot;fat-node&amp;quot; fürtözött megoldás. Az alrendszereket az NIIFI nagy sávszélességű, alacsony késleltetésű HBONE+ adathálózatán keresztül, ARC grid köztesréteg, valamint harmonizált felhasználói azonosítás segítségével integráljuk egységes elvek mentén elérhető számítási egységgé. Az erőforrás jelenleg Magyarország legnagyobb tudományos számítás céljára felhasználható erőforrása, amely összesen 50 billió lebegőpontos művelet elvégzését teszi lehetővé másodpercenként.&lt;br /&gt;
A szuperszámítógép-rendszert az NIIFI üzemelteti és fejleszti. A rendszerhez regisztrációt követően, minden olyan személy vagy kutatócsoport hozzáférhet, amely tagintézményi szerződéses kapcsolatban áll az NIIF Intézettel.&lt;br /&gt;
A gépen megtalálhatók és futtathatók a legkorszerűbb fejlesztőeszközök és tudományos számításra szolgáló alkalmazások, valamint, az NIIF adatközpontban elhelyezett alrendszer kivételével, valamennyi alrendszer kiegészül a számítási feladatok eredményeit megjelenítő vizualizációs eszközökkel.&lt;br /&gt;
&lt;br /&gt;
==== Szuperszámítógépeink összehasonlítása ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot; &lt;br /&gt;
|- &lt;br /&gt;
| Helyszín&lt;br /&gt;
| Budapest&lt;br /&gt;
| [[Budapest2_klaszter|Budapest2]]&lt;br /&gt;
| Szeged&lt;br /&gt;
| Debrecen&lt;br /&gt;
| [[Debrecen2_GPU_klaszter|Debrecen2-GPU]] (tesztüzem)&lt;br /&gt;
| Debrecen3-Phi (beüzemelése folyamatban van)&lt;br /&gt;
| Pécs&lt;br /&gt;
&lt;br /&gt;
| [[Miskolc_UV_2000|Miskolc]] (tesztüzem)&lt;br /&gt;
|- &lt;br /&gt;
| Típus&lt;br /&gt;
| HP CP4000SL&lt;br /&gt;
| HP SL250s &lt;br /&gt;
| HP CP4000BL&lt;br /&gt;
| SGI ICE8400EX&lt;br /&gt;
| HP SL250s&lt;br /&gt;
&lt;br /&gt;
| HP Apollo 8000&lt;br /&gt;
| SGI UV 1000&lt;br /&gt;
&lt;br /&gt;
| SGI UV 2000&lt;br /&gt;
|- &lt;br /&gt;
| CPU-k / node &lt;br /&gt;
| 2&lt;br /&gt;
| 2 &lt;br /&gt;
| 4 &lt;br /&gt;
| 2&lt;br /&gt;
| 2&lt;br /&gt;
&lt;br /&gt;
| 2 &lt;br /&gt;
| 192&lt;br /&gt;
&lt;br /&gt;
| 44 &lt;br /&gt;
|- &lt;br /&gt;
| Core-ok / CPU &lt;br /&gt;
| 12&lt;br /&gt;
| 10&lt;br /&gt;
| 12&lt;br /&gt;
| 6&lt;br /&gt;
| 8&lt;br /&gt;
| 12&lt;br /&gt;
| 6&lt;br /&gt;
| 8&lt;br /&gt;
|- &lt;br /&gt;
| Memória / node &lt;br /&gt;
| 66 GB&lt;br /&gt;
| 63 GB &lt;br /&gt;
| 132 GB&lt;br /&gt;
| 47 GB&lt;br /&gt;
| 125 GB&lt;br /&gt;
| 125 GB&lt;br /&gt;
| 6 TB&lt;br /&gt;
| 1.4 TB&lt;br /&gt;
|- &lt;br /&gt;
| Memória / core &lt;br /&gt;
| 2.6 GB&lt;br /&gt;
| 3 GB &lt;br /&gt;
| 2.6 GB&lt;br /&gt;
| 2.6 GB&lt;br /&gt;
| 7.5 GB&lt;br /&gt;
| 7.5 GB&lt;br /&gt;
| 5 GB&lt;br /&gt;
| 4 GB&lt;br /&gt;
|- &lt;br /&gt;
| CPU &lt;br /&gt;
| AMD Opteron 6174 @ 2.2GHz&lt;br /&gt;
| Intel Xeon E5-2680 v2 @ 2.80GHz    &lt;br /&gt;
| AMD Opteron 6174 @ 2.2GHz &lt;br /&gt;
| Intel Xeon X5680 @ 3.33 GHz&lt;br /&gt;
| Intel Xeon E5-2650 v2 @ 2.60GHz &lt;br /&gt;
| Intel Xeon E5-2670 v3 @ 2.30GHz&lt;br /&gt;
| Intel Xeon X7542 @ 2.66 GHz&lt;br /&gt;
| Intel Xeon E5-4627 v2 @ 3.33 GHz&lt;br /&gt;
|- &lt;br /&gt;
| GPU &lt;br /&gt;
| -&lt;br /&gt;
| -   &lt;br /&gt;
| 2 * 6 Nvidia M2070&lt;br /&gt;
| -&lt;br /&gt;
| 68 * 3 Nvidia K20x + 16 * 3 Nvidia K40x &lt;br /&gt;
| -&lt;br /&gt;
| -&lt;br /&gt;
| -&lt;br /&gt;
|-&lt;br /&gt;
| Intel Xeon Phi &lt;br /&gt;
| -&lt;br /&gt;
| [[Intel_Xeon_Phi |14 * 2 * Intel(R) Xeon Phi(TM) MIC SE10/7120]]&lt;br /&gt;
| -&lt;br /&gt;
| -  &lt;br /&gt;
| -&lt;br /&gt;
| [[Intel_Xeon_Phi |44 * 2 * Intel(R) Xeon Phi(TM) MIC SE10/7120]]&lt;br /&gt;
| -&lt;br /&gt;
| -&lt;br /&gt;
|- &lt;br /&gt;
| Linpack teljesítmény (Rmax)&lt;br /&gt;
| 5 Tflops&lt;br /&gt;
| 27 Tflops&lt;br /&gt;
| 20 Tflops&lt;br /&gt;
| 18 Tlops&lt;br /&gt;
| 254 Tflops&lt;br /&gt;
| ~106 Tflops&lt;br /&gt;
| 10 Tflops&lt;br /&gt;
| 8 Tflops&lt;br /&gt;
|- &lt;br /&gt;
| Compute node-ok száma &lt;br /&gt;
| 32&lt;br /&gt;
| 14&lt;br /&gt;
| 50&lt;br /&gt;
| 128&lt;br /&gt;
| 84&lt;br /&gt;
| 44&lt;br /&gt;
| 1&lt;br /&gt;
| 1&lt;br /&gt;
|-  &lt;br /&gt;
| Dedikált storage &lt;br /&gt;
| 50 TB&lt;br /&gt;
| 500 TB&lt;br /&gt;
| 250 TB&lt;br /&gt;
| 500 TB&lt;br /&gt;
| 585 TB (Phi-vel közös)&lt;br /&gt;
| 585 TB (GPU-val közös)&lt;br /&gt;
| 500 TB&lt;br /&gt;
| 240 TB&lt;br /&gt;
|- &lt;br /&gt;
| Scheduler &lt;br /&gt;
| [http://slurm.schedmd.com/ SLURM]&lt;br /&gt;
| [http://slurm.schedmd.com/ SLURM]  &lt;br /&gt;
| [http://slurm.schedmd.com/ SLURM]&lt;br /&gt;
| [http://slurm.schedmd.com/ SLURM]&lt;br /&gt;
| [http://slurm.schedmd.com/ SLURM] &lt;br /&gt;
| [http://slurm.schedmd.com/ SLURM]&lt;br /&gt;
| [http://slurm.schedmd.com/ SLURM] &lt;br /&gt;
| [http://slurm.schedmd.com/ SLURM] &lt;br /&gt;
|- &lt;br /&gt;
| MPI &lt;br /&gt;
| OpenMPI (ompi)&lt;br /&gt;
| IntelMPI (impi) &lt;br /&gt;
| OpenMPI (ompi) &lt;br /&gt;
| SGI MPT (mpt)&lt;br /&gt;
| OpenMPI (ompi)&lt;br /&gt;
&lt;br /&gt;
| OpenMPI (ompi)&lt;br /&gt;
| SGI MPT (mpt)&lt;br /&gt;
&lt;br /&gt;
| SGI MPT (mpt)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== További információ ==&lt;br /&gt;
=== Az új HPC-k dokumentációja ===&lt;br /&gt;
* [[Budapest2_klaszter|Budapest2]]&lt;br /&gt;
* [[Miskolc_UV_2000|Miskolc]]&lt;br /&gt;
* [[Debrecen2_GPU_klaszter|Debrecen2 GPU klaszter (LEO)]]&lt;br /&gt;
&lt;br /&gt;
=== Kapcsolódó oldalak===&lt;br /&gt;
* [[HPC-Portal|HPC portál: projekt és témaszámok kezelése, leállások, statisztikák]]&lt;br /&gt;
* [[NIIF_szuperszámítógépek_használata|A szuperszámítógépek használata]]&lt;br /&gt;
* [[HPC-GYIK|GY.I.K]]&lt;br /&gt;
* [http://www.niif.hu/hu/niif_intezet/az_niifi_szuperszamitogepek_hasznalata NIIF.HU HPC oldal]&lt;br /&gt;
* [[HPC_software|Telepített HPC Szoftverek ]]&lt;br /&gt;
&lt;br /&gt;
=== Saját HPC oktató tartalmaink ===&lt;br /&gt;
&lt;br /&gt;
* [http://videotorium.hu/hu/channels/details/1895 HPC oktató videók]&lt;br /&gt;
* [http://conference.niif.hu/event/3/session/23/contribution/25 HPC tutorial (NWS 2015) anyagai ]&lt;br /&gt;
* [http://videotorium.hu/hu/events/details/1864,GPU_programozas_workshop GPU programozás workshop (videotorium)]&lt;br /&gt;
* [[Intel_Xeon_Phi |Phi kártyák használata és alkamazások optimalizációja]]&lt;br /&gt;
&lt;br /&gt;
=== Angol nyelvű tréningek, videók, anyagok ===&lt;br /&gt;
* [http://www.training.prace-ri.eu PRACE training portal]&lt;br /&gt;
* [[PRACE_User_Support|PRACE User Support]]&lt;br /&gt;
[[Kategória: HPC]]&lt;br /&gt;
[[Kategória: Összefoglaló lapok]]&lt;/div&gt;</summary>
		<author><name>Szigeti(AT)niif.hu</name></author>	</entry>

	<entry>
		<id>https://wiki.niif.hu/index.php?title=HPC&amp;diff=3762</id>
		<title>HPC</title>
		<link rel="alternate" type="text/html" href="https://wiki.niif.hu/index.php?title=HPC&amp;diff=3762"/>
				<updated>2016-05-06T07:10:57Z</updated>
		
		<summary type="html">&lt;p&gt;Szigeti(AT)niif.hu: /* Szuperszámítógépeink összehasonlítása */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
[[HPC_en|NIIF HPC in ENGLISH]]&lt;br /&gt;
&lt;br /&gt;
== A szuperszámítógépekről ==&lt;br /&gt;
Az NIIF Intézet szuperszámítógép szolgáltatása tudományos számítási feladatok futtatására, valamint tudományos célú adattárolásra szolgál. A jelenleg integrált szuperszámítógép-rendszer komponensei négy helyszínen találhatók:&lt;br /&gt;
&lt;br /&gt;
* Debreceni Egyetem&lt;br /&gt;
* NIIFI központ&lt;br /&gt;
* Pécsi Tudományegyetem&lt;br /&gt;
* Szegedi Tudományegyetem&lt;br /&gt;
* Miskolci Egyetem&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Fájl:Orszagos-attekinto_hpc.png|center|700px|alt=Az NIIF szuperszámítógépei térképen]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
A tudományos számítási feladatok különböző típusainak minél szélesebb körű lefedettsége érdekében az egyes helyszíneken különböző felépítésű gépek találhatók: két helyszínen ccNUMA, három helyszínen pedig &amp;quot;fat-node&amp;quot; fürtözött megoldás. Az alrendszereket az NIIFI nagy sávszélességű, alacsony késleltetésű HBONE+ adathálózatán keresztül, ARC grid köztesréteg, valamint harmonizált felhasználói azonosítás segítségével integráljuk egységes elvek mentén elérhető számítási egységgé. Az erőforrás jelenleg Magyarország legnagyobb tudományos számítás céljára felhasználható erőforrása, amely összesen 50 billió lebegőpontos művelet elvégzését teszi lehetővé másodpercenként.&lt;br /&gt;
A szuperszámítógép-rendszert az NIIFI üzemelteti és fejleszti. A rendszerhez regisztrációt követően, minden olyan személy vagy kutatócsoport hozzáférhet, amely tagintézményi szerződéses kapcsolatban áll az NIIF Intézettel.&lt;br /&gt;
A gépen megtalálhatók és futtathatók a legkorszerűbb fejlesztőeszközök és tudományos számításra szolgáló alkalmazások, valamint, az NIIF adatközpontban elhelyezett alrendszer kivételével, valamennyi alrendszer kiegészül a számítási feladatok eredményeit megjelenítő vizualizációs eszközökkel.&lt;br /&gt;
&lt;br /&gt;
==== Szuperszámítógépeink összehasonlítása ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot; &lt;br /&gt;
|- &lt;br /&gt;
| Helyszín&lt;br /&gt;
| Budapest&lt;br /&gt;
| [[Budapest2_klaszter|Budapest2]]&lt;br /&gt;
| Szeged&lt;br /&gt;
| Debrecen&lt;br /&gt;
| [[Debrecen2_GPU_klaszter|Debrecen2-GPU]] (tesztüzem)&lt;br /&gt;
| Debrecen3-Phi (beüzemelése folyamatban van)&lt;br /&gt;
| Pécs&lt;br /&gt;
&lt;br /&gt;
| [[Miskolc_UV_2000|Miskolc]] (tesztüzem)&lt;br /&gt;
|- &lt;br /&gt;
| Típus&lt;br /&gt;
| HP CP4000SL&lt;br /&gt;
| HP SL250s &lt;br /&gt;
| HP CP4000BL&lt;br /&gt;
| SGI ICE8400EX&lt;br /&gt;
| HP SL250s&lt;br /&gt;
&lt;br /&gt;
| HP Apollo 8000&lt;br /&gt;
| SGI UV 1000&lt;br /&gt;
&lt;br /&gt;
| SGI UV 2000&lt;br /&gt;
|- &lt;br /&gt;
| CPU-k / node &lt;br /&gt;
| 2&lt;br /&gt;
| 2 &lt;br /&gt;
| 4 &lt;br /&gt;
| 2&lt;br /&gt;
| 2&lt;br /&gt;
&lt;br /&gt;
| 2 &lt;br /&gt;
| 192&lt;br /&gt;
&lt;br /&gt;
| 44 &lt;br /&gt;
|- &lt;br /&gt;
| Core-ok / CPU &lt;br /&gt;
| 12&lt;br /&gt;
| 10&lt;br /&gt;
| 12&lt;br /&gt;
| 6&lt;br /&gt;
| 8&lt;br /&gt;
| 12&lt;br /&gt;
| 6&lt;br /&gt;
| 8&lt;br /&gt;
|- &lt;br /&gt;
| Memória / node &lt;br /&gt;
| 66 GB&lt;br /&gt;
| 63 GB &lt;br /&gt;
| 132 GB&lt;br /&gt;
| 47 GB&lt;br /&gt;
| 125 GB&lt;br /&gt;
| 125 GB&lt;br /&gt;
| 6 TB&lt;br /&gt;
| 1.4 TB&lt;br /&gt;
|- &lt;br /&gt;
| Memória / core &lt;br /&gt;
| 2.6 GB&lt;br /&gt;
| 3 GB &lt;br /&gt;
| 2.6 GB&lt;br /&gt;
| 2.6 GB&lt;br /&gt;
| 7.5 GB&lt;br /&gt;
| 7.5 GB&lt;br /&gt;
| 5 GB&lt;br /&gt;
| 4 GB&lt;br /&gt;
|- &lt;br /&gt;
| CPU &lt;br /&gt;
| AMD Opteron 6174 @ 2.2GHz&lt;br /&gt;
| Intel Xeon E5-2680 v2 @ 2.80GHz    &lt;br /&gt;
| AMD Opteron 6174 @ 2.2GHz &lt;br /&gt;
| Intel Xeon X5680 @ 3.33 GHz&lt;br /&gt;
| Intel Xeon E5-2650 v2 @ 2.60GHz &lt;br /&gt;
| Intel Xeon E5-2670 v3 @ 2.30GHz&lt;br /&gt;
| Intel Xeon X7542 @ 2.66 GHz&lt;br /&gt;
| Intel Xeon E5-4627 v2 @ 3.33 GHz&lt;br /&gt;
|- &lt;br /&gt;
| GPU &lt;br /&gt;
| -&lt;br /&gt;
| -   &lt;br /&gt;
| 2 * 6 Nvidia M2070&lt;br /&gt;
| -&lt;br /&gt;
| 68 * 3 Nvidia K20x + 16 * 3 Nvidia K40x &lt;br /&gt;
| -&lt;br /&gt;
| -&lt;br /&gt;
| -&lt;br /&gt;
|-&lt;br /&gt;
| Intel Xeon Phi &lt;br /&gt;
| -&lt;br /&gt;
| [[Intel_Xeon_Phi |14 * 2 * Intel(R) Xeon Phi(TM) MIC SE10/7120]]&lt;br /&gt;
| -&lt;br /&gt;
| -  &lt;br /&gt;
| -&lt;br /&gt;
| [[Intel_Xeon_Phi |44 * 2 * Intel(R) Xeon Phi(TM) MIC SE10/7120]]&lt;br /&gt;
| -&lt;br /&gt;
| -&lt;br /&gt;
|- &lt;br /&gt;
| Linpack teljesítmény (Rmax)&lt;br /&gt;
| 5 Tflops&lt;br /&gt;
| 27 Tflops&lt;br /&gt;
| 20 Tflops&lt;br /&gt;
| 18 Tlops&lt;br /&gt;
| 254 Tflops&lt;br /&gt;
| ~106 Tflops&lt;br /&gt;
| 10 Tflops&lt;br /&gt;
| 8 Tflops&lt;br /&gt;
|- &lt;br /&gt;
| Compute node-ok száma &lt;br /&gt;
| 32&lt;br /&gt;
| 14&lt;br /&gt;
| 50&lt;br /&gt;
| 128&lt;br /&gt;
| 84&lt;br /&gt;
| 44&lt;br /&gt;
| 1&lt;br /&gt;
| 1&lt;br /&gt;
|-  &lt;br /&gt;
| Dedikált storage &lt;br /&gt;
| 50 TB&lt;br /&gt;
| 500 TB&lt;br /&gt;
| 250 TB&lt;br /&gt;
| 500 TB&lt;br /&gt;
| 585 TB (Phi-vel közös)&lt;br /&gt;
| 585 TB (GPU-val közös)&lt;br /&gt;
| 500 TB&lt;br /&gt;
| 240 TB&lt;br /&gt;
|- &lt;br /&gt;
| Scheduler &lt;br /&gt;
| [http://slurm.schedmd.com/ SLURM]&lt;br /&gt;
| [http://slurm.schedmd.com/ SLURM]  &lt;br /&gt;
| [http://slurm.schedmd.com/ SLURM]&lt;br /&gt;
| [http://slurm.schedmd.com/ SLURM]&lt;br /&gt;
| [http://slurm.schedmd.com/ SLURM] &lt;br /&gt;
| [http://slurm.schedmd.com/ SLURM]&lt;br /&gt;
| [http://slurm.schedmd.com/ SLURM] &lt;br /&gt;
| [http://slurm.schedmd.com/ SLURM] &lt;br /&gt;
|- &lt;br /&gt;
| MPI &lt;br /&gt;
| OpenMPI (ompi)&lt;br /&gt;
| IntelMPI (impi) &lt;br /&gt;
| OpenMPI (ompi) &lt;br /&gt;
| SGI MPT (mpt)&lt;br /&gt;
| OpenMPI (ompi)&lt;br /&gt;
&lt;br /&gt;
| OpenMPI (ompi)&lt;br /&gt;
| SGI MPT (mpt)&lt;br /&gt;
&lt;br /&gt;
| SGI MPT (mpt)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== További információ ==&lt;br /&gt;
=== Az új HPC-k dokumentációja ===&lt;br /&gt;
* [[Budapest2_klaszter|Budapest2]]&lt;br /&gt;
* [[Miskolc_UV_2000|Miskolc]]&lt;br /&gt;
* [[Debrecen2_GPU_klaszter|Debrecen2 GPU klaszter (LEO)]]&lt;br /&gt;
&lt;br /&gt;
=== Kapcsolódó oldalak===&lt;br /&gt;
* [[HPC-Portal|HPC portál: projekt és témaszámok kezelése, leállások, statisztikák]]&lt;br /&gt;
* [[NIIF_szuperszámítógépek_használata|A szuperszámítógépek használata]]&lt;br /&gt;
* [[HPC-GYIK|GY.I.K]]&lt;br /&gt;
* [http://www.niif.hu/hu/niif_intezet/az_niifi_szuperszamitogepek_hasznalata NIIF.HU HPC oldal]&lt;br /&gt;
* [[HPC_software|Telepített HPC Szoftverek ]]&lt;br /&gt;
&lt;br /&gt;
=== Saját HPC oktató tartalmaink ===&lt;br /&gt;
&lt;br /&gt;
* [http://videotorium.hu/hu/channels/details/1895 HPC oktató videók]&lt;br /&gt;
* [http://conference.niif.hu/event/3/session/23/contribution/25 HPC tutorial (NWS 2015) anyagai ]&lt;br /&gt;
* [http://videotorium.hu/hu/events/details/1864,GPU_programozas_workshop GPU programozás workshop (videotorium)]&lt;br /&gt;
&lt;br /&gt;
=== Angol nyelvű tréningek, videók, anyagok ===&lt;br /&gt;
* [http://www.training.prace-ri.eu PRACE training portal]&lt;br /&gt;
* [[PRACE_User_Support|PRACE User Support]]&lt;br /&gt;
[[Kategória: HPC]]&lt;br /&gt;
[[Kategória: Összefoglaló lapok]]&lt;/div&gt;</summary>
		<author><name>Szigeti(AT)niif.hu</name></author>	</entry>

	<entry>
		<id>https://wiki.niif.hu/index.php?title=Budapest2_klaszter&amp;diff=3761</id>
		<title>Budapest2 klaszter</title>
		<link rel="alternate" type="text/html" href="https://wiki.niif.hu/index.php?title=Budapest2_klaszter&amp;diff=3761"/>
				<updated>2016-05-06T07:09:24Z</updated>
		
		<summary type="html">&lt;p&gt;Szigeti(AT)niif.hu: /* Intel Xeon Phi használata */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot; &lt;br /&gt;
|- &lt;br /&gt;
| Klaszter&lt;br /&gt;
| Budapest2&lt;br /&gt;
|- &lt;br /&gt;
| Típus&lt;br /&gt;
| HP SL205s&lt;br /&gt;
|- &lt;br /&gt;
| core / node&lt;br /&gt;
| 10 × 2 Xeon E5-2680v2 2.80GHz  &lt;br /&gt;
|- &lt;br /&gt;
| Intel Xeon Phi / node&lt;br /&gt;
| 2 × Intel(R) Xeon Phi(TM) MIC SE10/7120 &lt;br /&gt;
|- &lt;br /&gt;
| Compute node-ok&lt;br /&gt;
| 14 db&lt;br /&gt;
|-&lt;br /&gt;
| Max Walltime&lt;br /&gt;
| 7-00:00:00&lt;br /&gt;
|-&lt;br /&gt;
| Max core-ok / projekt&lt;br /&gt;
| 140 (7db node)&lt;br /&gt;
|-&lt;br /&gt;
| Max mem / core&lt;br /&gt;
| 3000 MB&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Belépés ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ssh USER@login.budapest2.hpc.niif.hu&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Nem alapértelmezett kulcs használata esetén a &amp;lt;code&amp;gt;-i KULCS&amp;lt;/code&amp;gt; kapcsolóval megadása szükséges (SSH és SCP parancsok).&lt;br /&gt;
&lt;br /&gt;
=== Fájlok másolása SCP-vel ===&lt;br /&gt;
Letöltés a HOME könyvtárból és feltöltés a HOME könyvtárba:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Fel:  scp FILE USER@login.budapest2.hpc.niif.hu:FILE&lt;br /&gt;
Le :  scp USER@login.budapest2.hpc.niif.hu:FILE FILE&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Adatszinkronizáció ===&lt;br /&gt;
Nagyobb fájlok ill. könyvtárstruktúrák szinkronizálásához a következő parancsokat kell használni&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Fel:  rsync -a -e ssh DIRECTORY USER@login.budapest2.hpc.niif.hu:/home/USER&lt;br /&gt;
Le :  rsync -a -e ssh USER@login.budapest2.hpc.niif.hu:/home/USER/DIRECTORY&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
A törölt fájlok szinkronizálásához a &amp;lt;code&amp;gt;--delete&amp;lt;/code&amp;gt; opciót kell megadni.&lt;br /&gt;
&lt;br /&gt;
== Felhasználói felület ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
               rövid CWD&lt;br /&gt;
                     |&lt;br /&gt;
    BUDAPEST2[login] ~ (0)$&lt;br /&gt;
        |       |       |&lt;br /&gt;
   HPC állomás  |       |&lt;br /&gt;
         rövid gép név  |&lt;br /&gt;
               előző parancs exit kódja&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Modul környezet ===&lt;br /&gt;
Az elérhető modulok listáját a következő paranccsal kapjuk meg:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
module avail&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
a már betöltött modulok listáját:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
module list&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Alkalmazást a következő paranccsal tölthetünk be:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
module load APP&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
A NIIF által beállított környezeti változókat &amp;lt;code&amp;gt;nce&amp;lt;/code&amp;gt; parancs listázza ki.&lt;br /&gt;
&lt;br /&gt;
=== Adatok megosztása projekt tagok számára ===&lt;br /&gt;
Fájlok ill. könyvtárak megosztásához ACL-eket kell beállítani. A HOME könyvtárat más felhasználó (OTHER) számára olvashatóvá, így tehetjük&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
setfacl -m u:OTHER:rx $HOME&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Addott könyvtárat (DIRECTORY) írahtóvá:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
setfacl -m u:OTHER:rxw $HOME/DIRECTORY&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
A kiterjesztett jogokat a következő paranccsal kérhetjük le:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
getfacl $HOME/DIRECTORY&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Közös home könyvtár használata ==&lt;br /&gt;
A szuperszámítógépek login gépeit összekötő közös fájlrendszer a következő könyvtár alatt található:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/mnt/fhgfs/home/$USER&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Biztonsági mentést a közös könyvtárba a következő paranccsal tudunk végezni:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
rsync -avuP --delete $HOME/DIRECTORY /mnt/fhgfs/home/$USER&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Alkalmazások lefordítása ==&lt;br /&gt;
Mindenkitől azt kérjük, hogy először próbálja meg saját maga lefordítani az alkalmazását. Ha ez valamilyen oknál fogva mégsem sikerülne, akkor következő lépésként a magyarországi szuperszámítógép felhasználóktól érdemes kérdezni, mert nagy esély van rá hogy mások is belefutottak ugyanabba a problémába. Ezen a címen lehet őket elérni: &amp;lt;code&amp;gt;hpc-forum kukac listserv.niif.hu&amp;lt;/code&amp;gt;. Feliratkozni [https://listserv.niif.hu/mailman/listinfo/hpc-forum itt lehet erre] a levelezőlistára. Az archívumban is érdemes utánanézni a kérdésnek. Az NIIF HPC support csak igen korlátozottan képes egyéni fordítási kérésekkel foglalkozni, de problémával felkeresheti a &amp;lt;code&amp;gt;hpc-support kukac niif.hu&amp;lt;/code&amp;gt; címet. Az utóbbi esetben pár napos türelmüket mindenképp kérjük válaszunkig.&lt;br /&gt;
&lt;br /&gt;
== Intel Xeon Phi használata ==&lt;br /&gt;
&lt;br /&gt;
* [http://software.intel.com/en-us/mic-developer Itt] található az Intel hivatalos dokumentációja ezzel kapcsolatban&lt;br /&gt;
* [http://software.intel.com/en-us/articles/intel-mkl-link-line-advisor Itt] tájékozódhatnak az Intel matematikai könyvtárainak a linkeléséről&lt;br /&gt;
* [[Intel_Xeon_Phi |Részletes]] használati és alkalmazás optimalizációs leírás&lt;br /&gt;
&lt;br /&gt;
== SLURM ütemező használata ==&lt;br /&gt;
A szuperszámítógépen CPU óra (gépidő) alapú ütemezés működik. A felhasználóhoz tartozó Slurm projektek (Account) állapotáról a következő parancs ad információt:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sbalance&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
A második oszlopban (Usage) az egyes felhasználók elhasznált gépideje, a negyedik oszlopban pedig a számla összesített gépideje látható. Az utolsó két oszlop a maximális (Account Limit) és a még elérhető (Available) gépidőről ad tájékoztatást.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Scheduler Account Balance&lt;br /&gt;
---------- ----------- + ---------------- ----------- + ------------- -----------&lt;br /&gt;
User             Usage |          Account       Usage | Account Limit   Available (CPU hrs)&lt;br /&gt;
---------- ----------- + ---------------- ----------- + ------------- -----------&lt;br /&gt;
bob *                7 |           foobar           7 |         1,000         993&lt;br /&gt;
alice                0 |           foobar           7 |         1,000         993&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== A gépidő becslése ===&lt;br /&gt;
Nagyüzemi (production) futtatások előtt gépidőbecslést érdemes végezni. Ehhez a következő parancs használható:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sestimate -N NODES -t WALLTIME&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
ahol a &amp;lt;code&amp;gt;NODES&amp;lt;/code&amp;gt; a lefoglalni kívánt node-ok száma, a &amp;lt;code&amp;gt;WALLTIME&amp;lt;/code&amp;gt; pedig a futás maximális ideje.&lt;br /&gt;
&lt;br /&gt;
'''Fontos, hogy a lefoglalni kívánt gépidőt a lehető legpontosabban adjuk meg, mivel az ütemező ez alapján is rangsorolja a futtatásra váró feladatokat. Általában igaz, hogy a rövidebb job hamarabb sorra kerül. Érdemes minden futás idejét utólag az &amp;lt;code&amp;gt;sacct&amp;lt;/code&amp;gt; paranccsal is ellenőrizni.'''&lt;br /&gt;
&lt;br /&gt;
=== Állapotinformációk ===&lt;br /&gt;
Az ütemezőben lévő jobokról az &amp;lt;code&amp;gt;squeue&amp;lt;/code&amp;gt;, a klaszter általános állapotáról az &amp;lt;code&amp;gt;sinfo&amp;lt;/code&amp;gt; parancs ad tájékoztatást. Minden beküldött jobhoz egy egyedi azonosítószám (JOBID) rendelődik. Ennek ismeretében további információkat kérhetünk. Feladott vagy már futó job jellemzői:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
scontrol show job JOBID&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Minden job egy ún. számlázási adatbázisba (accounting) is bekerül. Ebből az adatbázisból visszakereshetők a lefuttatott feladatok jellemzői és erőforrás-felhasználás statisztikái. A részletes statisztikát a következő paranccsal tudjuk megnézni:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sacct -l -j JOBID&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A felhasznált memóriáról a következő parancs ad tájékoztatást:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
smemory JOBID&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A lemezhasználatról pedig a&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sdisk JOBID&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Slurm figyelmeztető üzenetek ====&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Resources/AssociationResourceLimit - Erőforrásra vár&lt;br /&gt;
AssociationJobLimit/QOSJobLimit - Nincs elég CPU idő vagy a maximális CPU szám le van foglalva&lt;br /&gt;
Piority - Alacsony prioritás miatt várakozik&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Az utóbbi esetben, csökkenteni kell a job által lefoglalni kívánt időt. Egy adott projekt részére maximálisan 512 CPU-n futhatnak jobok egy adott időben.&lt;br /&gt;
&lt;br /&gt;
==== Licenszek ellenőrzése ====&lt;br /&gt;
Az elérhető és éppen használt licenszekről a következő parancs ad információt:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
slicenses&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Karbantartás ellenőrzése ====&lt;br /&gt;
A karbantartási időablakban az ütemező nem indít új jobokat, de beküldeni lehet. A karbantartások időpontjairól a következő parancs ad tájékoztatást:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sreservations&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Összesített felhasználás ====&lt;br /&gt;
Egy hónapra visszamenőleg az elfogyasztott CPU perceket a következő paranccsal kérhetjük le:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
susage&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Teljes fogyasztás ====&lt;br /&gt;
Ha szeretnénk tájékozódni arról, hogy egy bizony idő óta mennyi a CPU idő felhasználásunk akkor azt ezzel paranccsal tudjuk lekérdezni:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sreport -t Hours Cluster AccountUtilizationByUser Accounts=ACCOUNT Start=2015-01-01&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Feladatok futtatása ===&lt;br /&gt;
Alkalmazások futtatása a szupergépeken kötegelt (batch) üzemmódban lehetséges. Ez azt jelenti, hogy minden futtatáshoz egy job szkriptet kell elkészíteni, amely tartalmazza az igényelt erőforrások leírását és a futtatáshoz szükséges parancsokat. Az ütemező paramétereit (erőforrás igények) a &amp;lt;code&amp;gt;#SBATCH&amp;lt;/code&amp;gt; direktívával kell megadni.&lt;br /&gt;
&lt;br /&gt;
==== Kötelező paraméterek ====&lt;br /&gt;
A következő paramétereket minden esetben meg kell adni:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#!/bin/bash&lt;br /&gt;
#SBATCH -A ACCOUNT&lt;br /&gt;
#SBATCH --job-name=NAME&lt;br /&gt;
#SBATCH --time=TIME&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
ahol az &amp;lt;code&amp;gt;ACCOUNT&amp;lt;/code&amp;gt; a terhelendő számla neve (elérhető számláinkről az &amp;lt;code&amp;gt;sbalance&amp;lt;/code&amp;gt; parancs ad felvilágosítást), a &amp;lt;code&amp;gt;NAME&amp;lt;/code&amp;gt; a job rövid neve, a &amp;lt;code&amp;gt;TIME&amp;lt;/code&amp;gt; pedig a maximális walltime idő (&amp;lt;code&amp;gt;DD-HH:MM:SS&amp;lt;/code&amp;gt;). A következő időformátumok használhatók: &lt;br /&gt;
&amp;quot;minutes&amp;quot;, &amp;quot;minutes:seconds&amp;quot;, &amp;quot;hours:minutes:seconds&amp;quot;, &amp;quot;days-hours&amp;quot;, &amp;quot;days-hours:minutes&amp;quot; és &amp;quot;days-hours:minutes:seconds&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
==== Intel Xeon Phi-k lefoglalása ====&lt;br /&gt;
A Intel Xeon Phi-k lefoglalása a következő direktívával történik:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#SBATCH --gres=mic:N&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Az &amp;lt;code&amp;gt;N&amp;lt;/code&amp;gt; az Intel Xeon Phi-k/node számát adja meg, ami 1 és 2 lehet maximum.&lt;br /&gt;
&lt;br /&gt;
==== Job-ok indítása ====&lt;br /&gt;
A jobok feladását a következő parancs végzi:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sbatch slurm.sh&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sikeres feladás esetén a következő kimenetet kapjuk:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Submitted batch job JOBID&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
ahol a &amp;lt;code&amp;gt;JOBID&amp;lt;/code&amp;gt; a feladat egyedi azonosítószáma.&lt;br /&gt;
&lt;br /&gt;
A feladat leállítását a következő parancs végzi:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
scancel JOBID&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Nem újrainduló jobok ====&lt;br /&gt;
Nem újrainduló jobokhoz a következő direktívát kell használni:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#SBATCH --no-requeue&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Feladat sorok ====&lt;br /&gt;
A szupergépeken két, egymást nem átfedő, sor (partíció) áll rendelkezésre, a &amp;lt;code&amp;gt;test&amp;lt;/code&amp;gt; sor és a &amp;lt;code&amp;gt;prod&amp;lt;/code&amp;gt; sor. Utóbbi az éles számolásokra való, előbbi fejlesztés és tesztelés céljára használható. A teszt sorban összesen 1 node-ot, maximum fél órára lehet lefoglalni. Az alapértelmezett sor a &amp;lt;code&amp;gt;prod&amp;lt;/code&amp;gt;. A teszt partíciót a következő direktívával lehet kiválasztani:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#SBATCH --partition=test&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== A szolgáltatás minősége (QOS) ====&lt;br /&gt;
A szolgáltatást alapértelmezett minősége &amp;lt;code&amp;gt;normal&amp;lt;/code&amp;gt;, azaz nem megszakítható a futás.&lt;br /&gt;
&lt;br /&gt;
===== Magas prioritás =====&lt;br /&gt;
A magas prioritású jobok maximum 24 óráig futhatnak, és kétszer gyorsabb időelszámolással rendelkeznek, cserébe az ütemező előreveszi ezeket a feladatokat.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#SBATCH --qos=fast&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Alacsony prioritás =====&lt;br /&gt;
Lehetőség van alacsony prioritású jobok feladására is. Az ilyen feladatokat bármilyen normál prioritású job bármikor megszakíthatja, cserébe az elhasznált gépidő fele számlázódik csak. A megszakított jobok automatikusan újraütemeződnek. Fontos, hogy olyan feladatokat indítsunk alacsony prioritással, amelyek kibírják a véletlenszerű megszakításokat, rendszeresen elmentik az állapotukat (checkpoint) és ebből gyorsan újra tudnak indulni.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#SBATCH --qos=lowpri&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Memória foglalás ====&lt;br /&gt;
Alapértelmezetten 1 CPU core-hoz 1000 MB memória van rendelve, ennél többet a következő direktívával igényelhetünk:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#SBATCH --mem-per-cpu=MEMORY&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
ahol &amp;lt;code&amp;gt;MEMORY&amp;lt;/code&amp;gt; MB egységben van megadva. A maximális memória/core 3000 MB lehet.&lt;br /&gt;
&lt;br /&gt;
Amennyiben a szálaknak 4GB-nál több virtuális memóriára van szükségük, akkor az indítandó program elé a következő parancsot is el kell helyezni:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ulimit -v unlimited&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Email értesítés ====&lt;br /&gt;
Levél küldése job állapotának változásakor (elindulás,leállás,hiba):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#SBATCH --mail-type=ALL&lt;br /&gt;
#SBATCH --mail-user=EMAIL&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
ahol az &amp;lt;code&amp;gt;EMAIL&amp;lt;/code&amp;gt; az értesítendő emial cím.&lt;br /&gt;
&lt;br /&gt;
==== Tömbfeladatok (arrayjob) ====&lt;br /&gt;
Tömbfeladatokra akkor van szükségünk, egy szálon futó (soros) alkalmazást szeretnénk egyszerre sok példányban (más-más adatokkal) futtatni. A példányok számára az ütemező a &amp;lt;code&amp;gt;SLURM_ARRAY_TASK_ID&amp;lt;/code&amp;gt; környezeti változóban tárolja az egyedi azonosítót. Ennek lekérdezésével lehet az arrayjob szálait elkülöníteni. A szálak kimenetei a &amp;lt;code&amp;gt;slurm-SLURM_ARRAY_JOB_ID-SLURM_ARRAY_TASK_ID.out&amp;lt;/code&amp;gt; fájlokba íródnak. Az ütemező a feltöltést szoros pakolás szerint végzi. Ebben az esetben is érdemes a processzorszám többszörösének választani a szálak számát. [http://slurm.schedmd.com/job_array.html Bővebb ismertető]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#!/bin/bash&lt;br /&gt;
#SBATCH -A ACCOUNT&lt;br /&gt;
#SBATCH --job-name=array&lt;br /&gt;
#SBATCH --time=24:00:00&lt;br /&gt;
#SBATCH --array=1-96&lt;br /&gt;
srun envtest.sh&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Soros (serial) jobok ====&lt;br /&gt;
Több soros job futtatását a tömbfeladatokhoz hasonlóan kell indítani.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#!/bin/bash&lt;br /&gt;
&lt;br /&gt;
#SBATCH -A ACCOUNT&lt;br /&gt;
#SBATCH --job-name=serial&lt;br /&gt;
#SBATCH --time=24:30:00&lt;br /&gt;
#SBATCH -n 3&lt;br /&gt;
#SBATCH --partition=test&lt;br /&gt;
srun -n 1 program input1 &amp;amp;&lt;br /&gt;
srun -n 1 program input2 &amp;amp;&lt;br /&gt;
srun -n 1 program input3&lt;br /&gt;
wait&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== IntelMPI feladatok ====&lt;br /&gt;
MPI feladatok esetén meg kell adnunk az egy node-on elinduló MPI processzek számát is (&amp;lt;code&amp;gt;#SBATCH --ntasks-per-node=&amp;lt;/code&amp;gt;). A leggyakoribb esetben ez az egy node-ban található CPU core-ok száma. A párhuzamos programot az &amp;lt;code&amp;gt;mpirun&amp;lt;/code&amp;gt; paranccsal kell indítani.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#!/bin/bash&lt;br /&gt;
#SBATCH -A ACCOUNT&lt;br /&gt;
#SBATCH --job-name=mpi&lt;br /&gt;
#SBATCH -N 2&lt;br /&gt;
#SBATCH --ntasks-per-node=20&lt;br /&gt;
#SBATCH --time=12:00:00&lt;br /&gt;
&lt;br /&gt;
mpirun PROGRAM&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
OpenMPI FAQ: http://www.open-mpi.org/faq&lt;br /&gt;
&lt;br /&gt;
==== OpenMP (OMP) feladatok ====&lt;br /&gt;
OpenMP párhuzamos alkalmazásokhoz maximum 1 node-ot lehet lefoglalni. Az OMP szálák számát az &amp;lt;code&amp;gt;OMP_NUM_THREADS&amp;lt;/code&amp;gt; környezeti változóval kell megadni. A változót vagy az alkamazás elé kell írni (ld. példa), vagy exportálni kell az indító parancs előtt:&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
export OMP_NUM_THREADS=10&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A következő példában egy taskhoz 10 CPU core-t rendeltunk, a 10 CPU core-nak egy node-on kell lennie. A CPU core-ok számát a &amp;lt;code&amp;gt;&lt;br /&gt;
SLURM_CPUS_PER_TASK&amp;lt;/code&amp;gt; változó tartalmazza, és ez állítja be az OMP szálak számát is.&lt;br /&gt;
&lt;br /&gt;
Alice felhasználó a foobar számla terhére, maximum 6 órára indít el egy 10 szálas OMP alkalmazást.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#!/bin/bash&lt;br /&gt;
#SBATCH -A foobar&lt;br /&gt;
#SBATCH --job-name=omp&lt;br /&gt;
#SBATCH --time=06:00:00&lt;br /&gt;
#SBATCH --ntasks=1&lt;br /&gt;
#SBATCH --cpus-per-task=10&lt;br /&gt;
OMP_NUM_THREADS=$SLURM_CPUS_PER_TASK ./a.out&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Hibrid MPI-OMP feladatok ====&lt;br /&gt;
Hibrid MPI-OMP módról akkor beszélünk, ha a párhuzamos alkalmazás MPI-t és OMP-t is használ. Érdemes tudni, hogy az Intel MKL-el linkelt programok MKL hívásai OpenMP képesek. Általában a következő elosztás javasolt: az MPI processzek száma 1-től az egy node-ban található CPU foglalatok száma, az OMP szálak ennek megfelelően az egy node-ban található összes CPU core szám vagy annak fele, negyede (értelem szerűen). A jobszkipthez a fenti két mód paramétereit kombinálni kell.&lt;br /&gt;
&lt;br /&gt;
A következő példában 2 node-ot, és node-onként 1-1 taskot indítunk taskonként 10 szállal. Alice felhasználó a foobar számla terhére, 8 órára, 2 node-ra küldött be egy hibrid jobot. Egy node-on egyszerre csak 1 db MPI processz fut ami node-onként 10 OMP szálat használ. A 2 gépen összesen 2 MPI proceszz és 2 x 10 OMP szál fut.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#!/bin/bash&lt;br /&gt;
#SBATCH -A foobar&lt;br /&gt;
#SBATCH --job-name=mpiomp&lt;br /&gt;
#SBATCH --time=08:00:00&lt;br /&gt;
#SBATCH -N 2&lt;br /&gt;
#SBATCH --ntasks=2&lt;br /&gt;
#SBATCH --ntasks-per-node=1&lt;br /&gt;
#SBATCH --cpus-per-task=10&lt;br /&gt;
#SBATCH -o slurm.out&lt;br /&gt;
export OMP_NUM_THREADS=$SLURM_CPUS_PER_TASK&lt;br /&gt;
mpirun ./a.out&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Kategória: HPC]]&lt;/div&gt;</summary>
		<author><name>Szigeti(AT)niif.hu</name></author>	</entry>

	<entry>
		<id>https://wiki.niif.hu/index.php?title=Intel_Xeon_Phi&amp;diff=3760</id>
		<title>Intel Xeon Phi</title>
		<link rel="alternate" type="text/html" href="https://wiki.niif.hu/index.php?title=Intel_Xeon_Phi&amp;diff=3760"/>
				<updated>2016-05-06T06:51:53Z</updated>
		
		<summary type="html">&lt;p&gt;Szigeti(AT)niif.hu: /* Phi használat slurm-mel */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Az oldalon található információk segítséget nyújtanak a HPC felhasználóinknak, hogy alkalmazásaikat miként portolhatják valamint optimalizálhatják a hatékonyabb futást szem előtt tartva Intel Xeon Phi koprocesszor kártyákon.&lt;br /&gt;
&lt;br /&gt;
==Hardver bemutatása==&lt;br /&gt;
Intel® Xeon Phi™ Coprocessor 7120P&lt;br /&gt;
&amp;lt;gallery&amp;gt;&lt;br /&gt;
Fájl:Xeon-phi-family-rwd.png.rendition.intel.web.720.405.png|&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-  align=&amp;quot;center&amp;quot;&lt;br /&gt;
! CPU&lt;br /&gt;
! threads/Core&lt;br /&gt;
!max threads&lt;br /&gt;
! CPU Freq&lt;br /&gt;
! Memória&lt;br /&gt;
! L1 cache&lt;br /&gt;
! L2 cache&lt;br /&gt;
! Interfész&lt;br /&gt;
! Ptot&lt;br /&gt;
! Számítási kapacitás&lt;br /&gt;
|-  align=&amp;quot;center&amp;quot;&lt;br /&gt;
| 61&lt;br /&gt;
| 4 Hardver&lt;br /&gt;
| 244&lt;br /&gt;
| 1238 GHz&lt;br /&gt;
| 16GB GDDR5&lt;br /&gt;
| 32KB/core&lt;br /&gt;
| 512KB/core&lt;br /&gt;
| PCIe&lt;br /&gt;
| 300W&lt;br /&gt;
| 1,2 TFlop/s dupla pontosság&lt;br /&gt;
|-  align=&amp;quot;center&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
| OpenMP-nél csak '''240'''&lt;br /&gt;
|&lt;br /&gt;
| ~100 cycles&lt;br /&gt;
~174 GB/s&lt;br /&gt;
| ~3 cycles&lt;br /&gt;
| ~10 cycles&lt;br /&gt;
| ~7 GB/s&lt;br /&gt;
~1 microsecond&lt;br /&gt;
|&lt;br /&gt;
| 2,4 TFlop/s egyszeres pontosság&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Az Intel® 1. generációs Xeon Phi™ koprocesszorában található 61 processzor ugyanazt a 16GB memóriát éri el, de semmilyen módon se fér hozzá közvetlenül a hoszt gép fizikai memóriájához.&amp;lt;br /&amp;gt;&lt;br /&gt;
[[Budapest2_klaszter|Budapest2]] és [[HPC#Szupersz.C3.A1m.C3.ADt.C3.B3g.C3.A9peink_.C3.B6sszehasonl.C3.ADt.C3.A1sa|Debrecen3-Phi]] gépeinkben számítási nodonként 2-2 Phi™ koprocesszor található. &amp;lt;br/&amp;gt;&lt;br /&gt;
A kártyákon speciális célú Linux kernel fut melyre a belépés limitáltan csak a rendszergazdák számára lehetséges SSH-n keresztül a hoszt gépről. Természetesen a kártyára belépve olyan érzésünk lehet, mintha egy sok processzoros gépen dolgoznánk, ami rendelkezik rengeteg memóriával. Ez így is van, de a hatékonyabb kihasználás érdekében az SSH-n keresztüli elérés nem lehetséges, csak a Slurm segítségével valamint a programozási nyelvek által támogatott csatornákon keresztül. Ennek részletezése lentebb megtalálható.&lt;br /&gt;
&lt;br /&gt;
==Használati módok==&lt;br /&gt;
A koprocesszor használatai módjait két csoportba lehet sorolni. A '''natív''' és az '''offload''' használatot.  &amp;lt;br/&amp;gt;&lt;br /&gt;
Ezen használati módokról a lenti fejezetekben több részletet is megtudhat. Az itt megfogalmazott &amp;quot;támogatás&amp;quot; csak a mi infrastruktúránkra érvényes! &amp;lt;br /&amp;gt;&lt;br /&gt;
A bemutatott példák ''c++'' nyelven vannak, de természetesen ''c'' és ''fortran'' esetén is megtalálható az ekvivalens módszer.&lt;br /&gt;
&lt;br /&gt;
===a) nem támogatott mód===&lt;br /&gt;
====közvetlen natív használat====&lt;br /&gt;
A natív mód arra szolgál, hogy a MIC kártyára  lefordított alkalmazásunkat felmásoljuk a kártyára, majd belépünk SSH-n a kártyára és azon futtatjuk a bináris kódunkat. &amp;lt;br /&amp;gt;&lt;br /&gt;
Ez a legegyszerűbb használati mód, ugyanis nincs más dolgunk, csak a '''-mmic''' kapcsolót használni a fordításnál és a fordító egy máris a MIC architektúrára kész bináris készít nekünk. &amp;lt;br/&amp;gt;&lt;br /&gt;
Workflow a következő lenne:&lt;br /&gt;
# Alkalmazás megírása. például [[Média:hello.cc|''hello.cc'']]&lt;br /&gt;
# Alkalmazás fordítása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;icpc -o hello-MIC -mmic hello.cc&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Másolás a MIC kártyára &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;scp hello-MIC mic0:&amp;lt;/pre&amp;gt;&lt;br /&gt;
# belépés a kártyára &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;ssh mic0&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Alkalmazás futtatása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;./hello-MIC&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===b) támogatott, de nem preferált használat===&lt;br /&gt;
====automatikus kódátvitel====&lt;br /&gt;
Ebben az esetben nem kell átmásolnunk a bináris alkalmazásunkat a kártyára sőt még csak be se kell lépnünk, mert ezt a két lépést egy beépített tool végzi el. &amp;lt;br/&amp;gt;&lt;br /&gt;
A tool eléréséhez szükséges parancs neve: '''micnativeloadex'''&lt;br /&gt;
&lt;br /&gt;
Ebben az esetben a workflow a következőképpen módosul:&lt;br /&gt;
# Alkalmazás megírása. például [[Média:hello.cc|''hello.cc'']]&lt;br /&gt;
# Alkalmazás fordítása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;icpc -o hello-MIC -mmic hello.cc&amp;lt;/pre&amp;gt;&lt;br /&gt;
# A tool futásához szükséges library-k megadása &amp;lt;br&amp;gt; &amp;lt;pre&amp;gt;export SINK_LD_LIBRARY_PATH=/opt/intel/composerxe/compiler/lib/mic&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Alkalmazás futtatása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;micnativeloadex hello-MIC&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| BUDAPEST2[cn10] phi (0)$ export SINK_LD_LIBRARY_PATH=/opt/intel/composerxe/compiler/lib/mic &amp;lt;br/&amp;gt;BUDAPEST2[cn10] phi (0)$ micnativeloadex hello-MIC &amp;lt;br/&amp;gt;Hello world! I have 244 logical cores.&amp;lt;br/&amp;gt;BUDAPEST2[cn10] phi (0)$&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===c) támogatott módok===&lt;br /&gt;
====Offload====&lt;br /&gt;
Az Offload modell esetén szükséges a forráskód módosítása, mégpedig úgy, hogy azon kódrészletet, amit a koprocesszoron szeretnénk futtatni, ki kell egészíteni egy fordítási direktívával. Az alkalmazás fordításakor elegendő csak a CPU-ra való binárist készíteni mivel a modell végzi a megfelelő forrás részlet mozgatását és futtatását.&amp;lt;br /&amp;gt;&lt;br /&gt;
[[Fájl:offload.png|Offload modell]] &amp;lt;br /&amp;gt;&lt;br /&gt;
Példánkban jól látható, hogy a második kiíratás már a koprocesszoron fut.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
int main(int argc, char * argv[]) {&lt;br /&gt;
  printf(&amp;quot;Hello world from host!\n&amp;quot;);&lt;br /&gt;
#pragma offload target(mic)&lt;br /&gt;
  {&lt;br /&gt;
    printf(&amp;quot;Hello world from coprocessor!\n&amp;quot;); fflush(stdout);&lt;br /&gt;
  }&lt;br /&gt;
  printf(&amp;quot;Bye\n&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ebben a példában az operációs rendszer választja ki, hogy melyik Phi kártyán fusson le a kódrészlet. Amennyiben mi szeretnénk explicite megmondani, hogy melyik kártyán fusson, akkor a fordítási direktívában a mic kulcsszó után kell tenni egy &amp;quot;:&amp;quot;-t és a kártya sorszámát [mi esetünkben 0 vagy 1]. Ekkor így nézne ki a direktíva: ''#pragma offload target(mic:1)''&lt;br /&gt;
&lt;br /&gt;
Amennyiben az offload szekcióban függvény hívás is szerepel, akkor szükségeltetik az függvény futásához szükséges változók adatterületének átmásolása a Phi kártyára, majd a lefutás után visszamásolni a hoszt gépre. Ezen másolást az ''offload_attribute'' ''push'' és ''pop'' paraméterei segítségével határozhatjuk meg. &amp;lt;br /&amp;gt;&lt;br /&gt;
A következő mátrix-vektor szorzás példánál az A mátrix globális változó, míg a b és c vektorok a függvény paraméterei.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//A változók felmásolásra kerülnek a Xeon Phi-re&lt;br /&gt;
#pragma offload_attribute(push, target(mic))&lt;br /&gt;
  double* A;&lt;br /&gt;
  void MatrixVector(double *b, double *c, double m, double n) {&lt;br /&gt;
    ...&lt;br /&gt;
    c[] = A[..]*b[]&lt;br /&gt;
  }&lt;br /&gt;
// felmásolt objektumok visszamásolása&lt;br /&gt;
#pragma offload_attribute(pop)&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
  ...&lt;br /&gt;
#pragma offload target(mic) in(A[0:n*m])&lt;br /&gt;
// így is meg lehetne adni: #pragma offload target(mic:0) in(A : length(n*m))	        &lt;br /&gt;
  {&lt;br /&gt;
    MatrixVector(b, c, m, n);&lt;br /&gt;
  }&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ekkor minden memória területet 2x kell másolni, egyszer fel a kártyára, majd másodszor vissza a hoszt gépre, pedig elegendő lenne csak az eredmény vektort visszamásolni. Ezen optimalizálással egy későbbi fejezetben foglalkozunk. &amp;lt;p /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Futás során kaphatunk részletes riportot is, hogy pontosan milyen adatmozgatások történnek az alkalmazás futása során. Ehhez nincs szükség másra csak az '''OFFLOAD_REPORT''' környezeti változó megfelelő beállítása. A beállított értékek 1, 2 vagy 3 lehetnek, attól függően mennyire részletes riportot szeretnénk kapni. &amp;lt;br /&amp;gt;&lt;br /&gt;
beállítás:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export OFFLOAD_REPORT=3&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
példa kimenet a hello alkalmazásra:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Hello world from host!&lt;br /&gt;
[Offload] [MIC 0] [File]                    hello_offload.cc&lt;br /&gt;
[Offload] [MIC 0] [Line]                    5&lt;br /&gt;
[Offload] [MIC 0] [Tag]                     Tag 0&lt;br /&gt;
Hello world from coprocessor!&lt;br /&gt;
[Offload] [HOST]  [Tag 0] [CPU Time]        0.554653(seconds)&lt;br /&gt;
[Offload] [MIC 0] [Tag 0] [CPU-&amp;gt;MIC Data]   0 (bytes)&lt;br /&gt;
[Offload] [MIC 0] [Tag 0] [MIC Time]        0.000140(seconds)&lt;br /&gt;
[Offload] [MIC 0] [Tag 0] [MIC-&amp;gt;CPU Data]   0 (bytes)&lt;br /&gt;
Bye&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Offload + OpenMP====&lt;br /&gt;
Amennyiben nem csak 1 processzort szeretnénk használni a Phi kártyánkon és az alkalmazásunkban használt számítás megengedi a párhuzamosítást, akkor az OpenMP segítségével párhuzamosíthatjuk a megfelelő számítást. &amp;lt;br /&amp;gt;&lt;br /&gt;
[[Fájl:Offload+openmp.png|Offload modell és OpenMP]] &amp;lt;br /&amp;gt;&lt;br /&gt;
Példánk maradjon ugyanúgy a mátrix-vektor szorzás. Amennyiben a teljes kártya adta kapacitást ki szeretnénk használni, akkor az OMP threadek számát 240-re kell beállítani. Azért nem 244-re, mivel 1 CPU-t dedikáltan csak a kommunikációra használ a rendszer és 1 CPU 4 hardver threaddel rendelkezik.&lt;br /&gt;
Ekkor a példánk így változik:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;omp.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#pragma offload_attribute(push, target(mic))&lt;br /&gt;
void multiply(int n, int m, double* A, double* b, double* c){&lt;br /&gt;
  const int nCPUs = omp_get_max_threads();   //240&lt;br /&gt;
  omp_set_num_threads(nCPUs);&lt;br /&gt;
#pragma omp parallel for schedule(dynamic, 1)&lt;br /&gt;
  for ( int i = 0 ; i &amp;lt; m ; i++)&lt;br /&gt;
     for ( int j = 0 ; j &amp;lt; n ; j++)&lt;br /&gt;
       c[i] += A[i*n+j] * b[j];&lt;br /&gt;
}&lt;br /&gt;
#pragma offload_attribute(pop)&lt;br /&gt;
&lt;br /&gt;
int main(){&lt;br /&gt;
  double * A = (double*) malloc(sizeof(double)*n*m);&lt;br /&gt;
  ...&lt;br /&gt;
#pragma offload target(mic) in(A:length(n*m))&lt;br /&gt;
     multiply(n, m, A, b, c);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Az alkalmazás fordításánál a fordítónak meg kell adni, hogy használhja az openmp library-ket is. Ezt a '''-qopenmp''' kapcsolóval tudjuk elérni.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
icpc -o matrix-CPU -qopenmp matrix.cc&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====MPI + Offload + OpenMP====&lt;br /&gt;
Abban az esetben, ha 1 számítási node nem elég, MPI segítségével össze tudunk kötni több nodet, heterogén rendszert alkotva, és azokon alkalmazható a fenti Offload + OpenMP modell. &amp;lt;br /&amp;gt;&lt;br /&gt;
[[Fájl:Mpi+offload+openmp.png|MPI elosztás valamint Offload modell és OpenMP]] &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Phi használat slurm-mel==&lt;br /&gt;
Slurm esetén a mic opciót kell megadni a gres-knél. Amennyiben egynél több kártyára van szükség, akkor azt a ''mic:2''-vel lehetséges megadni. Ekkor két kártyát foglal le az ütemező a futtatandó feladatnak. &amp;lt;br /&amp;gt;&lt;br /&gt;
Egy lehetséges feladat, amit srun-nal lehet a slurmben beküldeni.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ cat slurm_job_openmp_phi&lt;br /&gt;
&lt;br /&gt;
#!/bin/bash&lt;br /&gt;
#SBATCH -A &amp;lt;PROJECT NEVE&amp;gt;&lt;br /&gt;
#SBATCH --job-name=&amp;lt;JOB NEVE&amp;gt;&lt;br /&gt;
#SBATCH --gres mic:2&lt;br /&gt;
#SBATCH --time=1:00:00&lt;br /&gt;
#SBATCH --partition=prod&lt;br /&gt;
#SBATCH -o slurm-%A.out&lt;br /&gt;
export OMP_NUM_THREADS=2&lt;br /&gt;
./runme_openmp_2phi&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Ekkor a feladat beküldése a következő paranccsal lehetséges.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
srun ./slurm_job_openmp_phi&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Alkalmazások optimalizálása a kártyára==&lt;br /&gt;
Ebben a fejezetben optimalizálási lehetőségeket mutatunk be, melyek segítségül szolgálhatnak az egyes alkalmazások hatékonyabb futása érdekében ezzel jobban kihasználni a rendelkezésre álló erőforrások adta számítási kapacitás.&lt;br /&gt;
&lt;br /&gt;
===Környezeti változók===&lt;br /&gt;
Lehetséges csak a Phi kártyákon használt környezeti változók beállítása, amit az alkalmazásunk használni is tud. &amp;lt;br /&amp;gt;&lt;br /&gt;
Példánkban az OpenMP esetén használható szálak számát állíthatjuk be:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export MIC_ENV_PREFIX=XEONPHI&lt;br /&gt;
export OMP_NUM_THREADS=12&lt;br /&gt;
export XEONPHI_OMP_NUM_THREADS=240&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
A beállított prefix tetszőleges karaktersorozat lehet, a lényeg, hogy ezt a beállított prefixet használjuk a környezeti változók esetén is!&lt;br /&gt;
 &lt;br /&gt;
===Hosszredukció===&lt;br /&gt;
Többnyire a fordítók, ha nem mondjuk meg explicite, a dupla pontosságú változatot választják a lehetséges változó- és függvény típusok közül. Amennyiben a számításunk nem igényli a dupla pontosságot, akkor használjuk a forrásban mindenhol az adott típus vagy függvény egyszeres pontosságú változatát, mellyel jelentős teljesítmény növekedést tudunk elérni. Továbbá az egyes műveletek futási ideje nem egyforma, így a műveletet helyettesíthetjük más művelettel, ami gyorsabb, akkor ismét javíthatunk a hatékonyságon. &amp;lt;br /&amp;gt;&lt;br /&gt;
Íme pár példa, amit alkalmazni lehet:&lt;br /&gt;
* &amp;quot;''/''&amp;quot; művelet helyett &amp;quot;''*''&amp;quot; -t használjunk, mert azt gyorsabban végre tudja hajtani a processzor&lt;br /&gt;
* ''pow'' =&amp;gt; ''1.0 / sqrt()''    , tetszőleges hatványozás, pl.: 3/2-ik hatvány&lt;br /&gt;
* hardver áltatl támogatott műveletek:&lt;br /&gt;
** ''exp'' =&amp;gt; ''exp2''&lt;br /&gt;
** ''log'' =&amp;gt; ''log2''&lt;br /&gt;
* dupla pontosság -&amp;gt; egyszeres pontosság lebegőpontos számok és műveletek esetén &lt;br /&gt;
** ''1.0'' =&amp;gt; ''1.0f''&lt;br /&gt;
** ''pow'' =&amp;gt; ''powf''&lt;br /&gt;
&lt;br /&gt;
===Vektorizáció===&lt;br /&gt;
A vektorizáció azon fordítási időben eldönthető optimalizációs faktor, amely nagy mértékben függ attól a hardver felépítésétől, amire éppen a fordítás történni fog. Legfontosabb befolyásoló tényezők a processzor cache méretei illetve a vektor műveletekért felelős aritmetikai egységének jellemzői mivel ezeket használja fel a fordító az optimalizálási folyamata során. &amp;lt;br /&amp;gt;&lt;br /&gt;
Az automatikus vektorizáció minimum -O2 szinttől érvényes, de mivel általában ez a szint az alapértelmezett, ezért minden fordításnál alkalmazásra kerül. &amp;lt;br /&amp;gt;&lt;br /&gt;
A fordító programnak a következő kapcsolókat lehet megadni, melyek segítenek a minél jobb vektorizációban:&lt;br /&gt;
* ''-qopt-report=&amp;lt;szám&amp;gt;'' ; az optimalizációs jelentés részletességét lehet megadni 0-tól 5-ig, ahol az 5-s szint a legrészletesebbet jelenti&lt;br /&gt;
* ''-qopt-report-phase:vec'' ; a vektorizációról szeretnék jelentést kapni&lt;br /&gt;
* ''-opt-report-file=&amp;lt;fájlnév&amp;gt;'' ; megadható a riportfájl neve&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
icpc autovec.cc -mmic -qopt-report=5 -opt-report-file=vectorization.oMIC.optrpt -qopt-report-phase:vec&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====SIMD====&lt;br /&gt;
Az adatok hatékonyabb feldolgozására az egyik megvalósítás a SIMD (Single Instruction Multiple Data), amikor is a különálló adatokat úgy kezeli, mintha az vektorok volnának és azon végzi el a vektor-műveletet. Ezzel egy a hardver által támogatott műveletet végzünk egy lépésben egy nagyobb adatmennyiségen, ezzel párhuzamosítva a feldolgozási folyamatot. Ezzel a módszerrel nagyon jó adat szintű párhuzamosítást lehet elérni.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Fájl:Simd.png|SIMD]]&lt;br /&gt;
&lt;br /&gt;
Főbb támogatott műveletek:&lt;br /&gt;
* aritmetikai műveletek, bináris operátorok&lt;br /&gt;
* összehasonlítások&lt;br /&gt;
* konverziók és típus kasztolások&lt;br /&gt;
* bit-műveletek: NOT, AND, OR, XOR, XAND&lt;br /&gt;
&lt;br /&gt;
Használata: '''simd''' fordítási direktívával lehetséges &amp;lt;br /&amp;gt;&lt;br /&gt;
Például:&lt;br /&gt;
* egyszerű vektorizáció&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma simd&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
  A[i] += B[i];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* adat párhuzamosítás OpenMP segítségével&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for simd&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
  DoSomeWork(A[i]);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====AoS -&amp;gt; SoA====&lt;br /&gt;
A kiindulás probléma az, hogy a struktúrákat tartalmazó vektort nem lehet jól vektorizálni, mivel a cache-ben tárolt adatok nagy többsége feleslegesen van bent, mivel azon adatokat csak egy későbbi ciklus futás során fogja az alkalmazás feldolgozni. Tehát ezen adatok jelenleg feleslegesen foglalják a cache területet, ahelyett, hogy hasznos adatot tárolnánk ott.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
struct MyStructure {&lt;br /&gt;
  float x, y, z;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void My_Function(cons int nItem, MyStructure* const Items, const float delta) {&lt;br /&gt;
  for (int i = 0; i &amp;lt; nItem; i++) {&lt;br /&gt;
    ...&lt;br /&gt;
    for (int j = 0; j &amp;lt; nItem; j++) {&lt;br /&gt;
      const float dx = Items[j].x - Items[i].x;&lt;br /&gt;
      const float dy = Items[j].y - Items[i].y;&lt;br /&gt;
      const float dz = Items[j].z - Items[i].z;&lt;br /&gt;
      ...  &lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  ... &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A vektorban a struktúrák tárolása helyett minden elemnek egy dedikált vektort allokálunk és így könnyen tudja a fordító vektorizálni azt. Természetesen segítségként a vektor feldolgozását &amp;quot;tömbösítettük&amp;quot; a tileSize méret megadásával illetve a ''unroll'' fordítási direktíva segítségével.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
struct MyStructureSet {&lt;br /&gt;
  float *x, *y, *z;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void My_Function(cons int nItem, MyStructure* const Items, const float delta) {&lt;br /&gt;
  const int tileSize = 16;&lt;br /&gt;
#pragma omp parallel for schedule(guided)&lt;br /&gt;
  for (int ii = 0; ii &amp;lt; nItem; ii+= tileSize) {&lt;br /&gt;
    ...&lt;br /&gt;
#pragma unroll(tileSize)&lt;br /&gt;
    for (int j = 0; j &amp;lt; nItem; j++) {&lt;br /&gt;
#pragma unroll(tileSize)&lt;br /&gt;
      for (int i = ii; i &amp;lt; ii + tileSize; i++) {&lt;br /&gt;
        const float dx = Items.x[j] - Items.x[i];&lt;br /&gt;
        const float dy = Items.y[j] - Items.y[i];&lt;br /&gt;
        const float dz = Items.z[j] - Items.z[i];&lt;br /&gt;
        ...&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  ... &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Hatékony hossz választás====&lt;br /&gt;
Olyan esetben jelent előnyt, ha például az n*n-es mátrix esetén n nem osztható 16-val, mert akkor a vektorizáció nem teljesen hajtható végre. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// n%16 != 0&lt;br /&gt;
float* A = _mm_malloc(sizeof(float)*n*n, 64);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Ilyen esetben mindig marad egy kis vektor részlet, amire elemenként kell elvégezni a műveletet.&amp;lt;br /&amp;gt;&lt;br /&gt;
A megoldást az jelenti, hogy a memória allokálást kiterjesztjük és nagyobb memória területet foglalunk a változónak, így már alkalmazható lesz a vektorizáció, mivel a maradvány területen is el tudjuk végezni a műveletet egy lépésben.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int lda=n;&lt;br /&gt;
if (n % 16 != 0) lda += (16 - n%16); // most lda%16==0&lt;br /&gt;
float* A = _mm_malloc(sizeof(float)*n*lda, 64);&lt;br /&gt;
&lt;br /&gt;
// használat&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
  for (int j = 0; j &amp;lt; n; j++)&lt;br /&gt;
    A[i*lda + j] = ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====&amp;quot;Héj ciklusok&amp;quot; kiküszöbölése====&lt;br /&gt;
Ciklusok esetén lehetséges, hogy a vektorizációt nem teljes mértékben ismeri fel a fordító program. &lt;br /&gt;
Ekkor segíthetünk a fordítónak egy fordítási direktívával ('''#pragma vector aligned'''), ha teljes mértékben biztosak vagyunk, hogy a ciklusunk vektorizálható!&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma vector aligned&lt;br /&gt;
  for (int j = 0; j &amp;lt; n; j++)&lt;br /&gt;
    A[i*lda + j] -= ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Ettől jelentős futás idejű javulást érhetünk el, mivel ezzel a #pragma-val kikapcsoltuk a futás idejű ellenőrzést, de előfordulhatnak olyan esetek, főleg amikor nem jól mértük fel a ciklusunkat és nem sikerült a vektorizálás, hogy ''Segmentation fault'' hibára fogunk futni.&lt;br /&gt;
&lt;br /&gt;
====Regularizáció====&lt;br /&gt;
Van egy ciklusunk, ami egy vektor elemeit módosítja. A processzor az adatokat &amp;quot;csokorba szedve&amp;quot; emeli be a belső kis méretű cache-be. Egyszerre természetesen több vektor elem is bekerül a a cache-be ezzel gyorsítva a feldolgozást. Ezen vektor iterációk valahogyan ráilleszkedik a vektorra.&lt;br /&gt;
&lt;br /&gt;
[[Fájl:regularization.png|Vektor regularizáció]]&lt;br /&gt;
&lt;br /&gt;
Abban az esetben, amikor ez az illesztés nem teljesen tökéletes (lsd. piros elemek), keletkeznem héj ciklusok, melyeket a regularizációval is eliminálni lehet. &amp;lt;br /&amp;gt;&lt;br /&gt;
A feladat eredetileg valahogyan így nézhet ki:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
for (int i = 0; i&amp;lt;n; i++)&lt;br /&gt;
  A[i]=...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ez a következő átalakítással már alkalmas vektorizációra és nem lesz &amp;quot;peel loop&amp;quot; probléma.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
for (int b=0; b&amp;lt;n; b++) {&lt;br /&gt;
  const int jStart = b- b%16; // mindig osztható 16-val&lt;br /&gt;
  ...&lt;br /&gt;
#pragma simd&lt;br /&gt;
  for (int j = jStart; j&amp;lt;n; j++)&lt;br /&gt;
    A[j]=...&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Párhuzamosítás===&lt;br /&gt;
Több féle mód létezik a párhuzamosításra. Korábban említettük a SIMD technikát, ami adatpárhuzamosításra való 1 processzoron belül, de létezik még processzorok közti párhuzamosítás, amit az OpenMP segítségével tudunk hatékonyan megvalósítani. Amennyiben gépek közti párhuzamosításra, üzenek küldésre van szükség, akkor az MPI-t kell segítségül hívni. Általános megoldás nem létezik minden feladatra, mert MPI is alkalmas lehet gépen belüli párhuzamosításra, nem csak az OpenMP. Amit mérlegelni kell, hogy mi az amire az alkalmazásnak szükséges van illetve mekkora kommunikációs overhead keletkezik, ha lecseréljük az OpenMP-t MPI-ra. Általában az egymástól független számításokat OpenMP-vel, míg a kommunikációt igénylő feladatokra MPI-t szoktak használni.&lt;br /&gt;
&lt;br /&gt;
====Szálak párhuzamosítása====&lt;br /&gt;
Az OpenMP-nek környezeti változók segítségével megadható a párhuzamosan futtatható szálak száma. Ezt a következő paranccsal tudjuk megtenni, ahol 5-re állítjuk.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export OMP_NUM_THREADS=5&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Forrásban a párhuzamos szálakat a ''#pragma omp parallel'' segítségével adhatjuk meg.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for              	//külső for szálak párhuzamosítása&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
#pragma simd				// vektorizáció a belső cikluson&lt;br /&gt;
  for (int j = 0; j &amp;lt; m; j++)&lt;br /&gt;
    My_Function(A[i][j]);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A fordítási direktívák össze is vonhatóak, ilyenkor 1 ciklus is elég.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for simd&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
    My_Function(A[i]);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=====Változók láthatósága, megosztása=====&lt;br /&gt;
Változók hatókörét a programozási nyelven kívül pragma-k segítségével is beállíthatjuk, hogy megosztott legyen a változó vagy a szálra nézve saját példány. Amennyiben nem definiáljuk felül, akkor minden változó megosztott. A felül definiálást a private és a shared paraméterekkel lehet megadni az OpenMP-nek.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int A, B, C;&lt;br /&gt;
&lt;br /&gt;
#pragma omp parallel private(A) shared(B)&lt;br /&gt;
{...}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Az itteni példában az B és C megosztott változó a szálak között, míg A-t minden szálnak lemásolja a futás kezdetén.&lt;br /&gt;
&lt;br /&gt;
====Párhuzamos szálak ütemezése====&lt;br /&gt;
OpenMp a szálak ütemezésére három féle módod kínál fel.&lt;br /&gt;
Módok:&lt;br /&gt;
* '''static''' (alapértelmezett): egyenletesen arányban egymás után elosztja a processzeket az indított szálakon, ezért a futási idő tetszőlegesen elhúzódhat, ha egyes processzek lassabban futnak le.&lt;br /&gt;
* '''dynamic''': minden esetben az első szabad szálra kerül a következő processz ütemezése, folyamatos kihasználtságot elérve, de &amp;quot;költséges&amp;quot; a sok-sok ütemezés&lt;br /&gt;
* '''guided''': Az előző két mód ötvözése. Nagyobb procesz számot ütemez az éppen szabad szálakra, melyek számát folyamatosan csökkenti a futás előre haladtával, hogy körülbelül egyszerre fejeződjön be minden szál futása, ezzel csökkentve a dynamic esetén magad ütemezési időráfordítást.&lt;br /&gt;
&lt;br /&gt;
Mindhárom esetben megadható az egyszerre ütemezett processzek száma, amit a ''chunk'' értéke határoz meg. Ha megvan adva a chunk értéke, akkor az minden esetben annak megfelelő processzámot ütemez a szálakra.&lt;br /&gt;
Használata:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel [schedule (&amp;lt;mode&amp;gt;,&amp;lt;chunk&amp;gt;) {...}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Szálak szinkronizációja====&lt;br /&gt;
Szála szinkronizálását többféle módon lehet megvalósítani, de ezen megoldások sajno jelentősen lelassítják a program futását.&lt;br /&gt;
* mutexxel: nagyon lassú, állandóan várnak egymásra a párhuzamos szálak&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++) {&lt;br /&gt;
#pragma omp critical&lt;br /&gt;
  { // kritikus szekció, csak egy szál hajthatja végre&lt;br /&gt;
    total = total + i;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* atomi művelet definiálása: sajnos még ez is lassú, de itt már nem olyan &amp;quot;erős&amp;quot; a szinkronizáció, ezért rendkívül sok limitációja van az itt használható atomi műveleteknek.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++) {&lt;br /&gt;
#pragma omp atomic&lt;br /&gt;
   total += i;&lt;br /&gt;
}	&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=====Párhuzamos redukció=====&lt;br /&gt;
Az itt bemutatott eljárás gyorsabb mint a fenti szinkronizációs megoldás, de sajnos nem alkalmazható vektorokra! A redukciót a ''reduction(operation: scalar)'' formában kell megadni.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int sum = 0;&lt;br /&gt;
#pragma omp parallel for reduction(+: sum)&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++) {&lt;br /&gt;
  sum = sum + i;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Tulajdonképpen a fenti problémakörre a következő minta ad megoldást. Ekkor egy külön változóban akkumuláltatjuk a szál általi összeget és ezen értékeke összegyűjtését végezzük csak atomi műveletként.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int sum = 0;&lt;br /&gt;
#pragma omp parallel&lt;br /&gt;
{&lt;br /&gt;
  int sum_thr = 0;&lt;br /&gt;
#pragma omp for&lt;br /&gt;
  for (int i=0; i&amp;lt;n; i++)&lt;br /&gt;
    sum_thr += i;&lt;br /&gt;
&lt;br /&gt;
#pragma omp atomic&lt;br /&gt;
  sum += sum_thr;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Ciklusok párhuzamosítása====&lt;br /&gt;
Ciklus párhuzamosítására a következő mintát szokás használni.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel&lt;br /&gt;
{&lt;br /&gt;
  // Az itt definiált forrást minden szál esetén végre fog hajtani&lt;br /&gt;
  ...&lt;br /&gt;
#pragma omp for [schedule (&amp;lt;mode&amp;gt;,&amp;lt;chunk&amp;gt;)&lt;br /&gt;
  for (int i = 0; i &amp;lt; n; i++) {&lt;br /&gt;
    // Ez a kódrész lesz párhuzamosítva&lt;br /&gt;
    ...&lt;br /&gt;
  }&lt;br /&gt;
  // ... és ez a köd is minden szál esetén végrehajtódik&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Ciklus kifejtés====&lt;br /&gt;
A technika már egy példa erejéig a vektorizációnál is látható, de ott nem került részletezésre. A megoldás lényege, hogy a kellően nagy ciklust szétdaraboljuk kisebb darabokra, amit már külön feldolgozhatóak, ezzel párhuzamosítva a folyamatokat. Az egy darab kiindulási ciklusból két darab egymásba ágyazott ciklust hozunk létre, ami ugyanazon a feladattéren dolgozik. &lt;br /&gt;
&lt;br /&gt;
Eredeti állapot:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++) {...}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Átírt változat:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int STRIP=1024;&lt;br /&gt;
for (int ii = 0; ii &amp;lt; n; ii += STRIP)&lt;br /&gt;
 for (int i = ii; i &amp;lt; ii+STRIP; i++) {...}&lt;br /&gt;
&lt;br /&gt;
// ha még van maradék elem, akkor azokon is végrehajtjuk a számításokat&lt;br /&gt;
if (n%STRIP != 0) {&lt;br /&gt;
  for (i=n-(n%STRIP)+1; i&amp;lt;n; i++) {...}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A darabok mérete (STRIP értéke) az adott rendszer és feladat függvényében változhat, tehát ez egyfajta &amp;quot;tuning&amp;quot; paraméter.&lt;br /&gt;
&lt;br /&gt;
====Automatikus ciklusösszevonás====&lt;br /&gt;
Az OpenMP támogatást nyújt több egymásba ágyazott ciklus &amp;quot;összevonására&amp;quot;. Ehhez csak a '''collaps''' fordítási direktívát és az összevonandó ciklusok számát kell megadni. &amp;lt;br /&amp;gt;&lt;br /&gt;
FONTOS: ekkor az automatikus vektorizáció kikapcsolásra kerül!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for collaps(2)&lt;br /&gt;
for (int i=0; i&amp;lt;n; i++)&lt;br /&gt;
  for( int j=0; j&amp;lt;m; j++){...}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A fneti példában a 2db for ciklis kerül párhuzamosításra úgy, hogy a fordítás során 1db ciklus generálódik belőle, ami a következő paraméterezéssel ekvivalens:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for&lt;br /&gt;
for (int c=0; c &amp;lt; n*m; c++) {&lt;br /&gt;
  int i = c/n;&lt;br /&gt;
  int j = c%n;&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Szigeti(AT)niif.hu</name></author>	</entry>

	<entry>
		<id>https://wiki.niif.hu/index.php?title=Intel_Xeon_Phi&amp;diff=3759</id>
		<title>Intel Xeon Phi</title>
		<link rel="alternate" type="text/html" href="https://wiki.niif.hu/index.php?title=Intel_Xeon_Phi&amp;diff=3759"/>
				<updated>2016-05-06T06:51:25Z</updated>
		
		<summary type="html">&lt;p&gt;Szigeti(AT)niif.hu: /* Használati módok */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Az oldalon található információk segítséget nyújtanak a HPC felhasználóinknak, hogy alkalmazásaikat miként portolhatják valamint optimalizálhatják a hatékonyabb futást szem előtt tartva Intel Xeon Phi koprocesszor kártyákon.&lt;br /&gt;
&lt;br /&gt;
==Hardver bemutatása==&lt;br /&gt;
Intel® Xeon Phi™ Coprocessor 7120P&lt;br /&gt;
&amp;lt;gallery&amp;gt;&lt;br /&gt;
Fájl:Xeon-phi-family-rwd.png.rendition.intel.web.720.405.png|&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-  align=&amp;quot;center&amp;quot;&lt;br /&gt;
! CPU&lt;br /&gt;
! threads/Core&lt;br /&gt;
!max threads&lt;br /&gt;
! CPU Freq&lt;br /&gt;
! Memória&lt;br /&gt;
! L1 cache&lt;br /&gt;
! L2 cache&lt;br /&gt;
! Interfész&lt;br /&gt;
! Ptot&lt;br /&gt;
! Számítási kapacitás&lt;br /&gt;
|-  align=&amp;quot;center&amp;quot;&lt;br /&gt;
| 61&lt;br /&gt;
| 4 Hardver&lt;br /&gt;
| 244&lt;br /&gt;
| 1238 GHz&lt;br /&gt;
| 16GB GDDR5&lt;br /&gt;
| 32KB/core&lt;br /&gt;
| 512KB/core&lt;br /&gt;
| PCIe&lt;br /&gt;
| 300W&lt;br /&gt;
| 1,2 TFlop/s dupla pontosság&lt;br /&gt;
|-  align=&amp;quot;center&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
| OpenMP-nél csak '''240'''&lt;br /&gt;
|&lt;br /&gt;
| ~100 cycles&lt;br /&gt;
~174 GB/s&lt;br /&gt;
| ~3 cycles&lt;br /&gt;
| ~10 cycles&lt;br /&gt;
| ~7 GB/s&lt;br /&gt;
~1 microsecond&lt;br /&gt;
|&lt;br /&gt;
| 2,4 TFlop/s egyszeres pontosság&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Az Intel® 1. generációs Xeon Phi™ koprocesszorában található 61 processzor ugyanazt a 16GB memóriát éri el, de semmilyen módon se fér hozzá közvetlenül a hoszt gép fizikai memóriájához.&amp;lt;br /&amp;gt;&lt;br /&gt;
[[Budapest2_klaszter|Budapest2]] és [[HPC#Szupersz.C3.A1m.C3.ADt.C3.B3g.C3.A9peink_.C3.B6sszehasonl.C3.ADt.C3.A1sa|Debrecen3-Phi]] gépeinkben számítási nodonként 2-2 Phi™ koprocesszor található. &amp;lt;br/&amp;gt;&lt;br /&gt;
A kártyákon speciális célú Linux kernel fut melyre a belépés limitáltan csak a rendszergazdák számára lehetséges SSH-n keresztül a hoszt gépről. Természetesen a kártyára belépve olyan érzésünk lehet, mintha egy sok processzoros gépen dolgoznánk, ami rendelkezik rengeteg memóriával. Ez így is van, de a hatékonyabb kihasználás érdekében az SSH-n keresztüli elérés nem lehetséges, csak a Slurm segítségével valamint a programozási nyelvek által támogatott csatornákon keresztül. Ennek részletezése lentebb megtalálható.&lt;br /&gt;
&lt;br /&gt;
==Használati módok==&lt;br /&gt;
A koprocesszor használatai módjait két csoportba lehet sorolni. A '''natív''' és az '''offload''' használatot.  &amp;lt;br/&amp;gt;&lt;br /&gt;
Ezen használati módokról a lenti fejezetekben több részletet is megtudhat. Az itt megfogalmazott &amp;quot;támogatás&amp;quot; csak a mi infrastruktúránkra érvényes! &amp;lt;br /&amp;gt;&lt;br /&gt;
A bemutatott példák ''c++'' nyelven vannak, de természetesen ''c'' és ''fortran'' esetén is megtalálható az ekvivalens módszer.&lt;br /&gt;
&lt;br /&gt;
===a) nem támogatott mód===&lt;br /&gt;
====közvetlen natív használat====&lt;br /&gt;
A natív mód arra szolgál, hogy a MIC kártyára  lefordított alkalmazásunkat felmásoljuk a kártyára, majd belépünk SSH-n a kártyára és azon futtatjuk a bináris kódunkat. &amp;lt;br /&amp;gt;&lt;br /&gt;
Ez a legegyszerűbb használati mód, ugyanis nincs más dolgunk, csak a '''-mmic''' kapcsolót használni a fordításnál és a fordító egy máris a MIC architektúrára kész bináris készít nekünk. &amp;lt;br/&amp;gt;&lt;br /&gt;
Workflow a következő lenne:&lt;br /&gt;
# Alkalmazás megírása. például [[Média:hello.cc|''hello.cc'']]&lt;br /&gt;
# Alkalmazás fordítása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;icpc -o hello-MIC -mmic hello.cc&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Másolás a MIC kártyára &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;scp hello-MIC mic0:&amp;lt;/pre&amp;gt;&lt;br /&gt;
# belépés a kártyára &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;ssh mic0&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Alkalmazás futtatása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;./hello-MIC&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===b) támogatott, de nem preferált használat===&lt;br /&gt;
====automatikus kódátvitel====&lt;br /&gt;
Ebben az esetben nem kell átmásolnunk a bináris alkalmazásunkat a kártyára sőt még csak be se kell lépnünk, mert ezt a két lépést egy beépített tool végzi el. &amp;lt;br/&amp;gt;&lt;br /&gt;
A tool eléréséhez szükséges parancs neve: '''micnativeloadex'''&lt;br /&gt;
&lt;br /&gt;
Ebben az esetben a workflow a következőképpen módosul:&lt;br /&gt;
# Alkalmazás megírása. például [[Média:hello.cc|''hello.cc'']]&lt;br /&gt;
# Alkalmazás fordítása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;icpc -o hello-MIC -mmic hello.cc&amp;lt;/pre&amp;gt;&lt;br /&gt;
# A tool futásához szükséges library-k megadása &amp;lt;br&amp;gt; &amp;lt;pre&amp;gt;export SINK_LD_LIBRARY_PATH=/opt/intel/composerxe/compiler/lib/mic&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Alkalmazás futtatása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;micnativeloadex hello-MIC&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| BUDAPEST2[cn10] phi (0)$ export SINK_LD_LIBRARY_PATH=/opt/intel/composerxe/compiler/lib/mic &amp;lt;br/&amp;gt;BUDAPEST2[cn10] phi (0)$ micnativeloadex hello-MIC &amp;lt;br/&amp;gt;Hello world! I have 244 logical cores.&amp;lt;br/&amp;gt;BUDAPEST2[cn10] phi (0)$&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===c) támogatott módok===&lt;br /&gt;
====Offload====&lt;br /&gt;
Az Offload modell esetén szükséges a forráskód módosítása, mégpedig úgy, hogy azon kódrészletet, amit a koprocesszoron szeretnénk futtatni, ki kell egészíteni egy fordítási direktívával. Az alkalmazás fordításakor elegendő csak a CPU-ra való binárist készíteni mivel a modell végzi a megfelelő forrás részlet mozgatását és futtatását.&amp;lt;br /&amp;gt;&lt;br /&gt;
[[Fájl:offload.png|Offload modell]] &amp;lt;br /&amp;gt;&lt;br /&gt;
Példánkban jól látható, hogy a második kiíratás már a koprocesszoron fut.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
int main(int argc, char * argv[]) {&lt;br /&gt;
  printf(&amp;quot;Hello world from host!\n&amp;quot;);&lt;br /&gt;
#pragma offload target(mic)&lt;br /&gt;
  {&lt;br /&gt;
    printf(&amp;quot;Hello world from coprocessor!\n&amp;quot;); fflush(stdout);&lt;br /&gt;
  }&lt;br /&gt;
  printf(&amp;quot;Bye\n&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ebben a példában az operációs rendszer választja ki, hogy melyik Phi kártyán fusson le a kódrészlet. Amennyiben mi szeretnénk explicite megmondani, hogy melyik kártyán fusson, akkor a fordítási direktívában a mic kulcsszó után kell tenni egy &amp;quot;:&amp;quot;-t és a kártya sorszámát [mi esetünkben 0 vagy 1]. Ekkor így nézne ki a direktíva: ''#pragma offload target(mic:1)''&lt;br /&gt;
&lt;br /&gt;
Amennyiben az offload szekcióban függvény hívás is szerepel, akkor szükségeltetik az függvény futásához szükséges változók adatterületének átmásolása a Phi kártyára, majd a lefutás után visszamásolni a hoszt gépre. Ezen másolást az ''offload_attribute'' ''push'' és ''pop'' paraméterei segítségével határozhatjuk meg. &amp;lt;br /&amp;gt;&lt;br /&gt;
A következő mátrix-vektor szorzás példánál az A mátrix globális változó, míg a b és c vektorok a függvény paraméterei.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//A változók felmásolásra kerülnek a Xeon Phi-re&lt;br /&gt;
#pragma offload_attribute(push, target(mic))&lt;br /&gt;
  double* A;&lt;br /&gt;
  void MatrixVector(double *b, double *c, double m, double n) {&lt;br /&gt;
    ...&lt;br /&gt;
    c[] = A[..]*b[]&lt;br /&gt;
  }&lt;br /&gt;
// felmásolt objektumok visszamásolása&lt;br /&gt;
#pragma offload_attribute(pop)&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
  ...&lt;br /&gt;
#pragma offload target(mic) in(A[0:n*m])&lt;br /&gt;
// így is meg lehetne adni: #pragma offload target(mic:0) in(A : length(n*m))	        &lt;br /&gt;
  {&lt;br /&gt;
    MatrixVector(b, c, m, n);&lt;br /&gt;
  }&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ekkor minden memória területet 2x kell másolni, egyszer fel a kártyára, majd másodszor vissza a hoszt gépre, pedig elegendő lenne csak az eredmény vektort visszamásolni. Ezen optimalizálással egy későbbi fejezetben foglalkozunk. &amp;lt;p /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Futás során kaphatunk részletes riportot is, hogy pontosan milyen adatmozgatások történnek az alkalmazás futása során. Ehhez nincs szükség másra csak az '''OFFLOAD_REPORT''' környezeti változó megfelelő beállítása. A beállított értékek 1, 2 vagy 3 lehetnek, attól függően mennyire részletes riportot szeretnénk kapni. &amp;lt;br /&amp;gt;&lt;br /&gt;
beállítás:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export OFFLOAD_REPORT=3&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
példa kimenet a hello alkalmazásra:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Hello world from host!&lt;br /&gt;
[Offload] [MIC 0] [File]                    hello_offload.cc&lt;br /&gt;
[Offload] [MIC 0] [Line]                    5&lt;br /&gt;
[Offload] [MIC 0] [Tag]                     Tag 0&lt;br /&gt;
Hello world from coprocessor!&lt;br /&gt;
[Offload] [HOST]  [Tag 0] [CPU Time]        0.554653(seconds)&lt;br /&gt;
[Offload] [MIC 0] [Tag 0] [CPU-&amp;gt;MIC Data]   0 (bytes)&lt;br /&gt;
[Offload] [MIC 0] [Tag 0] [MIC Time]        0.000140(seconds)&lt;br /&gt;
[Offload] [MIC 0] [Tag 0] [MIC-&amp;gt;CPU Data]   0 (bytes)&lt;br /&gt;
Bye&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Offload + OpenMP====&lt;br /&gt;
Amennyiben nem csak 1 processzort szeretnénk használni a Phi kártyánkon és az alkalmazásunkban használt számítás megengedi a párhuzamosítást, akkor az OpenMP segítségével párhuzamosíthatjuk a megfelelő számítást. &amp;lt;br /&amp;gt;&lt;br /&gt;
[[Fájl:Offload+openmp.png|Offload modell és OpenMP]] &amp;lt;br /&amp;gt;&lt;br /&gt;
Példánk maradjon ugyanúgy a mátrix-vektor szorzás. Amennyiben a teljes kártya adta kapacitást ki szeretnénk használni, akkor az OMP threadek számát 240-re kell beállítani. Azért nem 244-re, mivel 1 CPU-t dedikáltan csak a kommunikációra használ a rendszer és 1 CPU 4 hardver threaddel rendelkezik.&lt;br /&gt;
Ekkor a példánk így változik:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;omp.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#pragma offload_attribute(push, target(mic))&lt;br /&gt;
void multiply(int n, int m, double* A, double* b, double* c){&lt;br /&gt;
  const int nCPUs = omp_get_max_threads();   //240&lt;br /&gt;
  omp_set_num_threads(nCPUs);&lt;br /&gt;
#pragma omp parallel for schedule(dynamic, 1)&lt;br /&gt;
  for ( int i = 0 ; i &amp;lt; m ; i++)&lt;br /&gt;
     for ( int j = 0 ; j &amp;lt; n ; j++)&lt;br /&gt;
       c[i] += A[i*n+j] * b[j];&lt;br /&gt;
}&lt;br /&gt;
#pragma offload_attribute(pop)&lt;br /&gt;
&lt;br /&gt;
int main(){&lt;br /&gt;
  double * A = (double*) malloc(sizeof(double)*n*m);&lt;br /&gt;
  ...&lt;br /&gt;
#pragma offload target(mic) in(A:length(n*m))&lt;br /&gt;
     multiply(n, m, A, b, c);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Az alkalmazás fordításánál a fordítónak meg kell adni, hogy használhja az openmp library-ket is. Ezt a '''-qopenmp''' kapcsolóval tudjuk elérni.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
icpc -o matrix-CPU -qopenmp matrix.cc&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====MPI + Offload + OpenMP====&lt;br /&gt;
Abban az esetben, ha 1 számítási node nem elég, MPI segítségével össze tudunk kötni több nodet, heterogén rendszert alkotva, és azokon alkalmazható a fenti Offload + OpenMP modell. &amp;lt;br /&amp;gt;&lt;br /&gt;
[[Fájl:Mpi+offload+openmp.png|MPI elosztás valamint Offload modell és OpenMP]] &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Phi használat slurm-mel==&lt;br /&gt;
Slurm esetén a mic opciót kell megadni a gres-knél. Amennyiben egynél több kártyára van szükség, akkor azt a ''mic:2''-vel lehetséges megadni. Ekkor két kártyát foglal le az ütemező a futtatandó feladatnak.&lt;br /&gt;
Egy lehetséges feladat, amit srun-nal lehet a slurmben beküldeni.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ cat slurm_job_openmp_phi&lt;br /&gt;
&lt;br /&gt;
#!/bin/bash&lt;br /&gt;
#SBATCH -A &amp;lt;PROJECT NEVE&amp;gt;&lt;br /&gt;
#SBATCH --job-name=&amp;lt;JOB NEVE&amp;gt;&lt;br /&gt;
#SBATCH --gres mic:2&lt;br /&gt;
#SBATCH --time=1:00:00&lt;br /&gt;
#SBATCH --partition=prod&lt;br /&gt;
#SBATCH -o slurm-%A.out&lt;br /&gt;
export OMP_NUM_THREADS=2&lt;br /&gt;
./runme_openmp_2phi&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Ekkor a feladat beküldése a következő paranccsal lehetséges.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
srun ./slurm_job_openmp_phi&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Alkalmazások optimalizálása a kártyára==&lt;br /&gt;
Ebben a fejezetben optimalizálási lehetőségeket mutatunk be, melyek segítségül szolgálhatnak az egyes alkalmazások hatékonyabb futása érdekében ezzel jobban kihasználni a rendelkezésre álló erőforrások adta számítási kapacitás.&lt;br /&gt;
&lt;br /&gt;
===Környezeti változók===&lt;br /&gt;
Lehetséges csak a Phi kártyákon használt környezeti változók beállítása, amit az alkalmazásunk használni is tud. &amp;lt;br /&amp;gt;&lt;br /&gt;
Példánkban az OpenMP esetén használható szálak számát állíthatjuk be:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export MIC_ENV_PREFIX=XEONPHI&lt;br /&gt;
export OMP_NUM_THREADS=12&lt;br /&gt;
export XEONPHI_OMP_NUM_THREADS=240&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
A beállított prefix tetszőleges karaktersorozat lehet, a lényeg, hogy ezt a beállított prefixet használjuk a környezeti változók esetén is!&lt;br /&gt;
 &lt;br /&gt;
===Hosszredukció===&lt;br /&gt;
Többnyire a fordítók, ha nem mondjuk meg explicite, a dupla pontosságú változatot választják a lehetséges változó- és függvény típusok közül. Amennyiben a számításunk nem igényli a dupla pontosságot, akkor használjuk a forrásban mindenhol az adott típus vagy függvény egyszeres pontosságú változatát, mellyel jelentős teljesítmény növekedést tudunk elérni. Továbbá az egyes műveletek futási ideje nem egyforma, így a műveletet helyettesíthetjük más művelettel, ami gyorsabb, akkor ismét javíthatunk a hatékonyságon. &amp;lt;br /&amp;gt;&lt;br /&gt;
Íme pár példa, amit alkalmazni lehet:&lt;br /&gt;
* &amp;quot;''/''&amp;quot; művelet helyett &amp;quot;''*''&amp;quot; -t használjunk, mert azt gyorsabban végre tudja hajtani a processzor&lt;br /&gt;
* ''pow'' =&amp;gt; ''1.0 / sqrt()''    , tetszőleges hatványozás, pl.: 3/2-ik hatvány&lt;br /&gt;
* hardver áltatl támogatott műveletek:&lt;br /&gt;
** ''exp'' =&amp;gt; ''exp2''&lt;br /&gt;
** ''log'' =&amp;gt; ''log2''&lt;br /&gt;
* dupla pontosság -&amp;gt; egyszeres pontosság lebegőpontos számok és műveletek esetén &lt;br /&gt;
** ''1.0'' =&amp;gt; ''1.0f''&lt;br /&gt;
** ''pow'' =&amp;gt; ''powf''&lt;br /&gt;
&lt;br /&gt;
===Vektorizáció===&lt;br /&gt;
A vektorizáció azon fordítási időben eldönthető optimalizációs faktor, amely nagy mértékben függ attól a hardver felépítésétől, amire éppen a fordítás történni fog. Legfontosabb befolyásoló tényezők a processzor cache méretei illetve a vektor műveletekért felelős aritmetikai egységének jellemzői mivel ezeket használja fel a fordító az optimalizálási folyamata során. &amp;lt;br /&amp;gt;&lt;br /&gt;
Az automatikus vektorizáció minimum -O2 szinttől érvényes, de mivel általában ez a szint az alapértelmezett, ezért minden fordításnál alkalmazásra kerül. &amp;lt;br /&amp;gt;&lt;br /&gt;
A fordító programnak a következő kapcsolókat lehet megadni, melyek segítenek a minél jobb vektorizációban:&lt;br /&gt;
* ''-qopt-report=&amp;lt;szám&amp;gt;'' ; az optimalizációs jelentés részletességét lehet megadni 0-tól 5-ig, ahol az 5-s szint a legrészletesebbet jelenti&lt;br /&gt;
* ''-qopt-report-phase:vec'' ; a vektorizációról szeretnék jelentést kapni&lt;br /&gt;
* ''-opt-report-file=&amp;lt;fájlnév&amp;gt;'' ; megadható a riportfájl neve&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
icpc autovec.cc -mmic -qopt-report=5 -opt-report-file=vectorization.oMIC.optrpt -qopt-report-phase:vec&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====SIMD====&lt;br /&gt;
Az adatok hatékonyabb feldolgozására az egyik megvalósítás a SIMD (Single Instruction Multiple Data), amikor is a különálló adatokat úgy kezeli, mintha az vektorok volnának és azon végzi el a vektor-műveletet. Ezzel egy a hardver által támogatott műveletet végzünk egy lépésben egy nagyobb adatmennyiségen, ezzel párhuzamosítva a feldolgozási folyamatot. Ezzel a módszerrel nagyon jó adat szintű párhuzamosítást lehet elérni.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Fájl:Simd.png|SIMD]]&lt;br /&gt;
&lt;br /&gt;
Főbb támogatott műveletek:&lt;br /&gt;
* aritmetikai műveletek, bináris operátorok&lt;br /&gt;
* összehasonlítások&lt;br /&gt;
* konverziók és típus kasztolások&lt;br /&gt;
* bit-műveletek: NOT, AND, OR, XOR, XAND&lt;br /&gt;
&lt;br /&gt;
Használata: '''simd''' fordítási direktívával lehetséges &amp;lt;br /&amp;gt;&lt;br /&gt;
Például:&lt;br /&gt;
* egyszerű vektorizáció&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma simd&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
  A[i] += B[i];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* adat párhuzamosítás OpenMP segítségével&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for simd&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
  DoSomeWork(A[i]);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====AoS -&amp;gt; SoA====&lt;br /&gt;
A kiindulás probléma az, hogy a struktúrákat tartalmazó vektort nem lehet jól vektorizálni, mivel a cache-ben tárolt adatok nagy többsége feleslegesen van bent, mivel azon adatokat csak egy későbbi ciklus futás során fogja az alkalmazás feldolgozni. Tehát ezen adatok jelenleg feleslegesen foglalják a cache területet, ahelyett, hogy hasznos adatot tárolnánk ott.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
struct MyStructure {&lt;br /&gt;
  float x, y, z;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void My_Function(cons int nItem, MyStructure* const Items, const float delta) {&lt;br /&gt;
  for (int i = 0; i &amp;lt; nItem; i++) {&lt;br /&gt;
    ...&lt;br /&gt;
    for (int j = 0; j &amp;lt; nItem; j++) {&lt;br /&gt;
      const float dx = Items[j].x - Items[i].x;&lt;br /&gt;
      const float dy = Items[j].y - Items[i].y;&lt;br /&gt;
      const float dz = Items[j].z - Items[i].z;&lt;br /&gt;
      ...  &lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  ... &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A vektorban a struktúrák tárolása helyett minden elemnek egy dedikált vektort allokálunk és így könnyen tudja a fordító vektorizálni azt. Természetesen segítségként a vektor feldolgozását &amp;quot;tömbösítettük&amp;quot; a tileSize méret megadásával illetve a ''unroll'' fordítási direktíva segítségével.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
struct MyStructureSet {&lt;br /&gt;
  float *x, *y, *z;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void My_Function(cons int nItem, MyStructure* const Items, const float delta) {&lt;br /&gt;
  const int tileSize = 16;&lt;br /&gt;
#pragma omp parallel for schedule(guided)&lt;br /&gt;
  for (int ii = 0; ii &amp;lt; nItem; ii+= tileSize) {&lt;br /&gt;
    ...&lt;br /&gt;
#pragma unroll(tileSize)&lt;br /&gt;
    for (int j = 0; j &amp;lt; nItem; j++) {&lt;br /&gt;
#pragma unroll(tileSize)&lt;br /&gt;
      for (int i = ii; i &amp;lt; ii + tileSize; i++) {&lt;br /&gt;
        const float dx = Items.x[j] - Items.x[i];&lt;br /&gt;
        const float dy = Items.y[j] - Items.y[i];&lt;br /&gt;
        const float dz = Items.z[j] - Items.z[i];&lt;br /&gt;
        ...&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  ... &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Hatékony hossz választás====&lt;br /&gt;
Olyan esetben jelent előnyt, ha például az n*n-es mátrix esetén n nem osztható 16-val, mert akkor a vektorizáció nem teljesen hajtható végre. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// n%16 != 0&lt;br /&gt;
float* A = _mm_malloc(sizeof(float)*n*n, 64);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Ilyen esetben mindig marad egy kis vektor részlet, amire elemenként kell elvégezni a műveletet.&amp;lt;br /&amp;gt;&lt;br /&gt;
A megoldást az jelenti, hogy a memória allokálást kiterjesztjük és nagyobb memória területet foglalunk a változónak, így már alkalmazható lesz a vektorizáció, mivel a maradvány területen is el tudjuk végezni a műveletet egy lépésben.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int lda=n;&lt;br /&gt;
if (n % 16 != 0) lda += (16 - n%16); // most lda%16==0&lt;br /&gt;
float* A = _mm_malloc(sizeof(float)*n*lda, 64);&lt;br /&gt;
&lt;br /&gt;
// használat&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
  for (int j = 0; j &amp;lt; n; j++)&lt;br /&gt;
    A[i*lda + j] = ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====&amp;quot;Héj ciklusok&amp;quot; kiküszöbölése====&lt;br /&gt;
Ciklusok esetén lehetséges, hogy a vektorizációt nem teljes mértékben ismeri fel a fordító program. &lt;br /&gt;
Ekkor segíthetünk a fordítónak egy fordítási direktívával ('''#pragma vector aligned'''), ha teljes mértékben biztosak vagyunk, hogy a ciklusunk vektorizálható!&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma vector aligned&lt;br /&gt;
  for (int j = 0; j &amp;lt; n; j++)&lt;br /&gt;
    A[i*lda + j] -= ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Ettől jelentős futás idejű javulást érhetünk el, mivel ezzel a #pragma-val kikapcsoltuk a futás idejű ellenőrzést, de előfordulhatnak olyan esetek, főleg amikor nem jól mértük fel a ciklusunkat és nem sikerült a vektorizálás, hogy ''Segmentation fault'' hibára fogunk futni.&lt;br /&gt;
&lt;br /&gt;
====Regularizáció====&lt;br /&gt;
Van egy ciklusunk, ami egy vektor elemeit módosítja. A processzor az adatokat &amp;quot;csokorba szedve&amp;quot; emeli be a belső kis méretű cache-be. Egyszerre természetesen több vektor elem is bekerül a a cache-be ezzel gyorsítva a feldolgozást. Ezen vektor iterációk valahogyan ráilleszkedik a vektorra.&lt;br /&gt;
&lt;br /&gt;
[[Fájl:regularization.png|Vektor regularizáció]]&lt;br /&gt;
&lt;br /&gt;
Abban az esetben, amikor ez az illesztés nem teljesen tökéletes (lsd. piros elemek), keletkeznem héj ciklusok, melyeket a regularizációval is eliminálni lehet. &amp;lt;br /&amp;gt;&lt;br /&gt;
A feladat eredetileg valahogyan így nézhet ki:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
for (int i = 0; i&amp;lt;n; i++)&lt;br /&gt;
  A[i]=...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ez a következő átalakítással már alkalmas vektorizációra és nem lesz &amp;quot;peel loop&amp;quot; probléma.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
for (int b=0; b&amp;lt;n; b++) {&lt;br /&gt;
  const int jStart = b- b%16; // mindig osztható 16-val&lt;br /&gt;
  ...&lt;br /&gt;
#pragma simd&lt;br /&gt;
  for (int j = jStart; j&amp;lt;n; j++)&lt;br /&gt;
    A[j]=...&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Párhuzamosítás===&lt;br /&gt;
Több féle mód létezik a párhuzamosításra. Korábban említettük a SIMD technikát, ami adatpárhuzamosításra való 1 processzoron belül, de létezik még processzorok közti párhuzamosítás, amit az OpenMP segítségével tudunk hatékonyan megvalósítani. Amennyiben gépek közti párhuzamosításra, üzenek küldésre van szükség, akkor az MPI-t kell segítségül hívni. Általános megoldás nem létezik minden feladatra, mert MPI is alkalmas lehet gépen belüli párhuzamosításra, nem csak az OpenMP. Amit mérlegelni kell, hogy mi az amire az alkalmazásnak szükséges van illetve mekkora kommunikációs overhead keletkezik, ha lecseréljük az OpenMP-t MPI-ra. Általában az egymástól független számításokat OpenMP-vel, míg a kommunikációt igénylő feladatokra MPI-t szoktak használni.&lt;br /&gt;
&lt;br /&gt;
====Szálak párhuzamosítása====&lt;br /&gt;
Az OpenMP-nek környezeti változók segítségével megadható a párhuzamosan futtatható szálak száma. Ezt a következő paranccsal tudjuk megtenni, ahol 5-re állítjuk.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export OMP_NUM_THREADS=5&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Forrásban a párhuzamos szálakat a ''#pragma omp parallel'' segítségével adhatjuk meg.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for              	//külső for szálak párhuzamosítása&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
#pragma simd				// vektorizáció a belső cikluson&lt;br /&gt;
  for (int j = 0; j &amp;lt; m; j++)&lt;br /&gt;
    My_Function(A[i][j]);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A fordítási direktívák össze is vonhatóak, ilyenkor 1 ciklus is elég.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for simd&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
    My_Function(A[i]);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=====Változók láthatósága, megosztása=====&lt;br /&gt;
Változók hatókörét a programozási nyelven kívül pragma-k segítségével is beállíthatjuk, hogy megosztott legyen a változó vagy a szálra nézve saját példány. Amennyiben nem definiáljuk felül, akkor minden változó megosztott. A felül definiálást a private és a shared paraméterekkel lehet megadni az OpenMP-nek.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int A, B, C;&lt;br /&gt;
&lt;br /&gt;
#pragma omp parallel private(A) shared(B)&lt;br /&gt;
{...}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Az itteni példában az B és C megosztott változó a szálak között, míg A-t minden szálnak lemásolja a futás kezdetén.&lt;br /&gt;
&lt;br /&gt;
====Párhuzamos szálak ütemezése====&lt;br /&gt;
OpenMp a szálak ütemezésére három féle módod kínál fel.&lt;br /&gt;
Módok:&lt;br /&gt;
* '''static''' (alapértelmezett): egyenletesen arányban egymás után elosztja a processzeket az indított szálakon, ezért a futási idő tetszőlegesen elhúzódhat, ha egyes processzek lassabban futnak le.&lt;br /&gt;
* '''dynamic''': minden esetben az első szabad szálra kerül a következő processz ütemezése, folyamatos kihasználtságot elérve, de &amp;quot;költséges&amp;quot; a sok-sok ütemezés&lt;br /&gt;
* '''guided''': Az előző két mód ötvözése. Nagyobb procesz számot ütemez az éppen szabad szálakra, melyek számát folyamatosan csökkenti a futás előre haladtával, hogy körülbelül egyszerre fejeződjön be minden szál futása, ezzel csökkentve a dynamic esetén magad ütemezési időráfordítást.&lt;br /&gt;
&lt;br /&gt;
Mindhárom esetben megadható az egyszerre ütemezett processzek száma, amit a ''chunk'' értéke határoz meg. Ha megvan adva a chunk értéke, akkor az minden esetben annak megfelelő processzámot ütemez a szálakra.&lt;br /&gt;
Használata:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel [schedule (&amp;lt;mode&amp;gt;,&amp;lt;chunk&amp;gt;) {...}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Szálak szinkronizációja====&lt;br /&gt;
Szála szinkronizálását többféle módon lehet megvalósítani, de ezen megoldások sajno jelentősen lelassítják a program futását.&lt;br /&gt;
* mutexxel: nagyon lassú, állandóan várnak egymásra a párhuzamos szálak&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++) {&lt;br /&gt;
#pragma omp critical&lt;br /&gt;
  { // kritikus szekció, csak egy szál hajthatja végre&lt;br /&gt;
    total = total + i;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* atomi művelet definiálása: sajnos még ez is lassú, de itt már nem olyan &amp;quot;erős&amp;quot; a szinkronizáció, ezért rendkívül sok limitációja van az itt használható atomi műveleteknek.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++) {&lt;br /&gt;
#pragma omp atomic&lt;br /&gt;
   total += i;&lt;br /&gt;
}	&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=====Párhuzamos redukció=====&lt;br /&gt;
Az itt bemutatott eljárás gyorsabb mint a fenti szinkronizációs megoldás, de sajnos nem alkalmazható vektorokra! A redukciót a ''reduction(operation: scalar)'' formában kell megadni.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int sum = 0;&lt;br /&gt;
#pragma omp parallel for reduction(+: sum)&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++) {&lt;br /&gt;
  sum = sum + i;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Tulajdonképpen a fenti problémakörre a következő minta ad megoldást. Ekkor egy külön változóban akkumuláltatjuk a szál általi összeget és ezen értékeke összegyűjtését végezzük csak atomi műveletként.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int sum = 0;&lt;br /&gt;
#pragma omp parallel&lt;br /&gt;
{&lt;br /&gt;
  int sum_thr = 0;&lt;br /&gt;
#pragma omp for&lt;br /&gt;
  for (int i=0; i&amp;lt;n; i++)&lt;br /&gt;
    sum_thr += i;&lt;br /&gt;
&lt;br /&gt;
#pragma omp atomic&lt;br /&gt;
  sum += sum_thr;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Ciklusok párhuzamosítása====&lt;br /&gt;
Ciklus párhuzamosítására a következő mintát szokás használni.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel&lt;br /&gt;
{&lt;br /&gt;
  // Az itt definiált forrást minden szál esetén végre fog hajtani&lt;br /&gt;
  ...&lt;br /&gt;
#pragma omp for [schedule (&amp;lt;mode&amp;gt;,&amp;lt;chunk&amp;gt;)&lt;br /&gt;
  for (int i = 0; i &amp;lt; n; i++) {&lt;br /&gt;
    // Ez a kódrész lesz párhuzamosítva&lt;br /&gt;
    ...&lt;br /&gt;
  }&lt;br /&gt;
  // ... és ez a köd is minden szál esetén végrehajtódik&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Ciklus kifejtés====&lt;br /&gt;
A technika már egy példa erejéig a vektorizációnál is látható, de ott nem került részletezésre. A megoldás lényege, hogy a kellően nagy ciklust szétdaraboljuk kisebb darabokra, amit már külön feldolgozhatóak, ezzel párhuzamosítva a folyamatokat. Az egy darab kiindulási ciklusból két darab egymásba ágyazott ciklust hozunk létre, ami ugyanazon a feladattéren dolgozik. &lt;br /&gt;
&lt;br /&gt;
Eredeti állapot:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++) {...}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Átírt változat:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int STRIP=1024;&lt;br /&gt;
for (int ii = 0; ii &amp;lt; n; ii += STRIP)&lt;br /&gt;
 for (int i = ii; i &amp;lt; ii+STRIP; i++) {...}&lt;br /&gt;
&lt;br /&gt;
// ha még van maradék elem, akkor azokon is végrehajtjuk a számításokat&lt;br /&gt;
if (n%STRIP != 0) {&lt;br /&gt;
  for (i=n-(n%STRIP)+1; i&amp;lt;n; i++) {...}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A darabok mérete (STRIP értéke) az adott rendszer és feladat függvényében változhat, tehát ez egyfajta &amp;quot;tuning&amp;quot; paraméter.&lt;br /&gt;
&lt;br /&gt;
====Automatikus ciklusösszevonás====&lt;br /&gt;
Az OpenMP támogatást nyújt több egymásba ágyazott ciklus &amp;quot;összevonására&amp;quot;. Ehhez csak a '''collaps''' fordítási direktívát és az összevonandó ciklusok számát kell megadni. &amp;lt;br /&amp;gt;&lt;br /&gt;
FONTOS: ekkor az automatikus vektorizáció kikapcsolásra kerül!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for collaps(2)&lt;br /&gt;
for (int i=0; i&amp;lt;n; i++)&lt;br /&gt;
  for( int j=0; j&amp;lt;m; j++){...}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A fneti példában a 2db for ciklis kerül párhuzamosításra úgy, hogy a fordítás során 1db ciklus generálódik belőle, ami a következő paraméterezéssel ekvivalens:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for&lt;br /&gt;
for (int c=0; c &amp;lt; n*m; c++) {&lt;br /&gt;
  int i = c/n;&lt;br /&gt;
  int j = c%n;&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Szigeti(AT)niif.hu</name></author>	</entry>

	<entry>
		<id>https://wiki.niif.hu/index.php?title=Intel_Xeon_Phi&amp;diff=3758</id>
		<title>Intel Xeon Phi</title>
		<link rel="alternate" type="text/html" href="https://wiki.niif.hu/index.php?title=Intel_Xeon_Phi&amp;diff=3758"/>
				<updated>2016-05-05T12:44:59Z</updated>
		
		<summary type="html">&lt;p&gt;Szigeti(AT)niif.hu: /* Szálak szinkronizációja */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Az oldalon található információk segítséget nyújtanak a HPC felhasználóinknak, hogy alkalmazásaikat miként portolhatják valamint optimalizálhatják a hatékonyabb futást szem előtt tartva Intel Xeon Phi koprocesszor kártyákon.&lt;br /&gt;
&lt;br /&gt;
==Hardver bemutatása==&lt;br /&gt;
Intel® Xeon Phi™ Coprocessor 7120P&lt;br /&gt;
&amp;lt;gallery&amp;gt;&lt;br /&gt;
Fájl:Xeon-phi-family-rwd.png.rendition.intel.web.720.405.png|&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-  align=&amp;quot;center&amp;quot;&lt;br /&gt;
! CPU&lt;br /&gt;
! threads/Core&lt;br /&gt;
!max threads&lt;br /&gt;
! CPU Freq&lt;br /&gt;
! Memória&lt;br /&gt;
! L1 cache&lt;br /&gt;
! L2 cache&lt;br /&gt;
! Interfész&lt;br /&gt;
! Ptot&lt;br /&gt;
! Számítási kapacitás&lt;br /&gt;
|-  align=&amp;quot;center&amp;quot;&lt;br /&gt;
| 61&lt;br /&gt;
| 4 Hardver&lt;br /&gt;
| 244&lt;br /&gt;
| 1238 GHz&lt;br /&gt;
| 16GB GDDR5&lt;br /&gt;
| 32KB/core&lt;br /&gt;
| 512KB/core&lt;br /&gt;
| PCIe&lt;br /&gt;
| 300W&lt;br /&gt;
| 1,2 TFlop/s dupla pontosság&lt;br /&gt;
|-  align=&amp;quot;center&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
| OpenMP-nél csak '''240'''&lt;br /&gt;
|&lt;br /&gt;
| ~100 cycles&lt;br /&gt;
~174 GB/s&lt;br /&gt;
| ~3 cycles&lt;br /&gt;
| ~10 cycles&lt;br /&gt;
| ~7 GB/s&lt;br /&gt;
~1 microsecond&lt;br /&gt;
|&lt;br /&gt;
| 2,4 TFlop/s egyszeres pontosság&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Az Intel® 1. generációs Xeon Phi™ koprocesszorában található 61 processzor ugyanazt a 16GB memóriát éri el, de semmilyen módon se fér hozzá közvetlenül a hoszt gép fizikai memóriájához.&amp;lt;br /&amp;gt;&lt;br /&gt;
[[Budapest2_klaszter|Budapest2]] és [[HPC#Szupersz.C3.A1m.C3.ADt.C3.B3g.C3.A9peink_.C3.B6sszehasonl.C3.ADt.C3.A1sa|Debrecen3-Phi]] gépeinkben számítási nodonként 2-2 Phi™ koprocesszor található. &amp;lt;br/&amp;gt;&lt;br /&gt;
A kártyákon speciális célú Linux kernel fut melyre a belépés limitáltan csak a rendszergazdák számára lehetséges SSH-n keresztül a hoszt gépről. Természetesen a kártyára belépve olyan érzésünk lehet, mintha egy sok processzoros gépen dolgoznánk, ami rendelkezik rengeteg memóriával. Ez így is van, de a hatékonyabb kihasználás érdekében az SSH-n keresztüli elérés nem lehetséges, csak a Slurm segítségével valamint a programozási nyelvek által támogatott csatornákon keresztül. Ennek részletezése lentebb megtalálható.&lt;br /&gt;
&lt;br /&gt;
==Használati módok==&lt;br /&gt;
A koprocesszor használatai módjait két csoportba lehet sorolni. A '''natív''' és az '''offload''' használatot.  &amp;lt;br/&amp;gt;&lt;br /&gt;
Ezen használati módokról a lenti fejezetekben több részletet is megtudhat. Az itt megfogalmazott &amp;quot;támogatás&amp;quot; csak a mi infrastruktúránkra érvényes! &amp;lt;br /&amp;gt;&lt;br /&gt;
A bemutatott példák ''c++'' nyelven vannak, de természetesen ''c'' és ''fortran'' esetén is megtalálható az ekvivalens módszer.&lt;br /&gt;
&lt;br /&gt;
===a) nem támogatott mód===&lt;br /&gt;
====közvetlen natív használat====&lt;br /&gt;
A natív mód arra szolgál, hogy a MIC kártyára  lefordított alkalmazásunkat felmásoljuk a kártyára, majd belépünk SSH-n a kártyára és azon futtatjuk a bináris kódunkat. &amp;lt;br /&amp;gt;&lt;br /&gt;
Ez a legegyszerűbb használati mód, ugyanis nincs más dolgunk, csak a '''-mmic''' kapcsolót használni a fordításnál és a fordító egy máris a MIC architektúrára kész bináris készít nekünk. &amp;lt;br/&amp;gt;&lt;br /&gt;
Workflow a következő lenne:&lt;br /&gt;
# Alkalmazás megírása. például [[Média:hello.cc|''hello.cc'']]&lt;br /&gt;
# Alkalmazás fordítása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;icpc -o hello-MIC -mmic hello.cc&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Másolás a MIC kártyára &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;scp hello-MIC mic0:&amp;lt;/pre&amp;gt;&lt;br /&gt;
# belépés a kártyára &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;ssh mic0&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Alkalmazás futtatása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;./hello-MIC&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===b) támogatott, de nem preferált használat===&lt;br /&gt;
====automatikus kódátvitel====&lt;br /&gt;
Ebben az esetben nem kell átmásolnunk a bináris alkalmazásunkat a kártyára sőt még csak be se kell lépnünk, mert ezt a két lépést egy beépített tool végzi el. &amp;lt;br/&amp;gt;&lt;br /&gt;
A tool eléréséhez szükséges parancs neve: '''micnativeloadex'''&lt;br /&gt;
&lt;br /&gt;
Ebben az esetben a workflow a következőképpen módosul:&lt;br /&gt;
# Alkalmazás megírása. például [[Média:hello.cc|''hello.cc'']]&lt;br /&gt;
# Alkalmazás fordítása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;icpc -o hello-MIC -mmic hello.cc&amp;lt;/pre&amp;gt;&lt;br /&gt;
# A tool futásához szükséges library-k megadása &amp;lt;br&amp;gt; &amp;lt;pre&amp;gt;export SINK_LD_LIBRARY_PATH=/opt/intel/composerxe/compiler/lib/mic&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Alkalmazás futtatása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;micnativeloadex hello-MIC&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| BUDAPEST2[cn10] phi (0)$ export SINK_LD_LIBRARY_PATH=/opt/intel/composerxe/compiler/lib/mic &amp;lt;br/&amp;gt;BUDAPEST2[cn10] phi (0)$ micnativeloadex hello-MIC &amp;lt;br/&amp;gt;Hello world! I have 244 logical cores.&amp;lt;br/&amp;gt;BUDAPEST2[cn10] phi (0)$&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===c) támogatott módok===&lt;br /&gt;
====Offload====&lt;br /&gt;
Az Offload modell esetén szükséges a forráskód módosítása, mégpedig úgy, hogy azon kódrészletet, amit a koprocesszoron szeretnénk futtatni, ki kell egészíteni egy fordítási direktívával. Az alkalmazás fordításakor elegendő csak a CPU-ra való binárist készíteni mivel a modell végzi a megfelelő forrás részlet mozgatását és futtatását.&amp;lt;br /&amp;gt;&lt;br /&gt;
[[Fájl:offload.png|Offload modell]] &amp;lt;br /&amp;gt;&lt;br /&gt;
Példánkban jól látható, hogy a második kiíratás már a koprocesszoron fut.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
int main(int argc, char * argv[]) {&lt;br /&gt;
  printf(&amp;quot;Hello world from host!\n&amp;quot;);&lt;br /&gt;
#pragma offload target(mic)&lt;br /&gt;
  {&lt;br /&gt;
    printf(&amp;quot;Hello world from coprocessor!\n&amp;quot;); fflush(stdout);&lt;br /&gt;
  }&lt;br /&gt;
  printf(&amp;quot;Bye\n&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ebben a példában az operációs rendszer választja ki, hogy melyik Phi kártyán fusson le a kódrészlet. Amennyiben mi szeretnénk explicite megmondani, hogy melyik kártyán fusson, akkor a fordítási direktívában a mic kulcsszó után kell tenni egy &amp;quot;:&amp;quot;-t és a kártya sorszámát [mi esetünkben 0 vagy 1]. Ekkor így nézne ki a direktíva: ''#pragma offload target(mic:1)''&lt;br /&gt;
&lt;br /&gt;
Amennyiben az offload szekcióban függvény hívás is szerepel, akkor szükségeltetik az függvény futásához szükséges változók adatterületének átmásolása a Phi kártyára, majd a lefutás után visszamásolni a hoszt gépre. Ezen másolást az ''offload_attribute'' ''push'' és ''pop'' paraméterei segítségével határozhatjuk meg. &amp;lt;br /&amp;gt;&lt;br /&gt;
A következő mátrix-vektor szorzás példánál az A mátrix globális változó, míg a b és c vektorok a függvény paraméterei.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//A változók felmásolásra kerülnek a Xeon Phi-re&lt;br /&gt;
#pragma offload_attribute(push, target(mic))&lt;br /&gt;
  double* A;&lt;br /&gt;
  void MatrixVector(double *b, double *c, double m, double n) {&lt;br /&gt;
    ...&lt;br /&gt;
    c[] = A[..]*b[]&lt;br /&gt;
  }&lt;br /&gt;
// felmásolt objektumok visszamásolása&lt;br /&gt;
#pragma offload_attribute(pop)&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
  ...&lt;br /&gt;
#pragma offload target(mic) in(A[0:n*m])&lt;br /&gt;
// így is meg lehetne adni: #pragma offload target(mic:0) in(A : length(n*m))	        &lt;br /&gt;
  {&lt;br /&gt;
    MatrixVector(b, c, m, n);&lt;br /&gt;
  }&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ekkor minden memória területet 2x kell másolni, egyszer fel a kártyára, majd másodszor vissza a hoszt gépre, pedig elegendő lenne csak az eredmény vektort visszamásolni. Ezen optimalizálással egy későbbi fejezetben foglalkozunk. &amp;lt;p /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Futás során kaphatunk részletes riportot is, hogy pontosan milyen adatmozgatások történnek az alkalmazás futása során. Ehhez nincs szükség másra csak az '''OFFLOAD_REPORT''' környezeti változó megfelelő beállítása. A beállított értékek 1, 2 vagy 3 lehetnek, attól függően mennyire részletes riportot szeretnénk kapni. &amp;lt;br /&amp;gt;&lt;br /&gt;
beállítás:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export OFFLOAD_REPORT=3&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
példa kimenet a hello alkalmazásra:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Hello world from host!&lt;br /&gt;
[Offload] [MIC 0] [File]                    hello_offload.cc&lt;br /&gt;
[Offload] [MIC 0] [Line]                    5&lt;br /&gt;
[Offload] [MIC 0] [Tag]                     Tag 0&lt;br /&gt;
Hello world from coprocessor!&lt;br /&gt;
[Offload] [HOST]  [Tag 0] [CPU Time]        0.554653(seconds)&lt;br /&gt;
[Offload] [MIC 0] [Tag 0] [CPU-&amp;gt;MIC Data]   0 (bytes)&lt;br /&gt;
[Offload] [MIC 0] [Tag 0] [MIC Time]        0.000140(seconds)&lt;br /&gt;
[Offload] [MIC 0] [Tag 0] [MIC-&amp;gt;CPU Data]   0 (bytes)&lt;br /&gt;
Bye&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Offload + OpenMP====&lt;br /&gt;
Amennyiben nem csak 1 processzort szeretnénk használni a Phi kártyánkon és az alkalmazásunkban használt számítás megengedi a párhuzamosítást, akkor az OpenMP segítségével párhuzamosíthatjuk a megfelelő számítást. &amp;lt;br /&amp;gt;&lt;br /&gt;
[[Fájl:Offload+openmp.png|Offload modell és OpenMP]] &amp;lt;br /&amp;gt;&lt;br /&gt;
Példánk maradjon ugyanúgy a mátrix-vektor szorzás. Amennyiben a teljes kártya adta kapacitást ki szeretnénk használni, akkor az OMP threadek számát 240-re kell beállítani. Azért nem 244-re, mivel 1 CPU-t dedikáltan csak a kommunikációra használ a rendszer és 1 CPU 4 hardver threaddel rendelkezik.&lt;br /&gt;
Ekkor a példánk így változik:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;omp.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#pragma offload_attribute(push, target(mic))&lt;br /&gt;
void multiply(int n, int m, double* A, double* b, double* c){&lt;br /&gt;
  const int nCPUs = omp_get_max_threads();   //240&lt;br /&gt;
  omp_set_num_threads(nCPUs);&lt;br /&gt;
#pragma omp parallel for schedule(dynamic, 1)&lt;br /&gt;
  for ( int i = 0 ; i &amp;lt; m ; i++)&lt;br /&gt;
     for ( int j = 0 ; j &amp;lt; n ; j++)&lt;br /&gt;
       c[i] += A[i*n+j] * b[j];&lt;br /&gt;
}&lt;br /&gt;
#pragma offload_attribute(pop)&lt;br /&gt;
&lt;br /&gt;
int main(){&lt;br /&gt;
  double * A = (double*) malloc(sizeof(double)*n*m);&lt;br /&gt;
  ...&lt;br /&gt;
#pragma offload target(mic) in(A:length(n*m))&lt;br /&gt;
     multiply(n, m, A, b, c);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Az alkalmazás fordításánál a fordítónak meg kell adni, hogy használhja az openmp library-ket is. Ezt a '''-qopenmp''' kapcsolóval tudjuk elérni.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
icpc -o matrix-CPU -qopenmp matrix.cc&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====MPI + Offload + OpenMP====&lt;br /&gt;
Abban az esetben, ha 1 számítási node nem elég, MPI segítségével össze tudunk kötni több nodet, heterogén rendszert alkotva, és azokon alkalmazható a fenti Offload + OpenMP modell. &amp;lt;br /&amp;gt;&lt;br /&gt;
[[Fájl:Mpi+offload+openmp.png|MPI elosztás valamint Offload modell és OpenMP]] &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Alkalmazások optimalizálása a kártyára==&lt;br /&gt;
Ebben a fejezetben optimalizálási lehetőségeket mutatunk be, melyek segítségül szolgálhatnak az egyes alkalmazások hatékonyabb futása érdekében ezzel jobban kihasználni a rendelkezésre álló erőforrások adta számítási kapacitás.&lt;br /&gt;
&lt;br /&gt;
===Környezeti változók===&lt;br /&gt;
Lehetséges csak a Phi kártyákon használt környezeti változók beállítása, amit az alkalmazásunk használni is tud. &amp;lt;br /&amp;gt;&lt;br /&gt;
Példánkban az OpenMP esetén használható szálak számát állíthatjuk be:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export MIC_ENV_PREFIX=XEONPHI&lt;br /&gt;
export OMP_NUM_THREADS=12&lt;br /&gt;
export XEONPHI_OMP_NUM_THREADS=240&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
A beállított prefix tetszőleges karaktersorozat lehet, a lényeg, hogy ezt a beállított prefixet használjuk a környezeti változók esetén is!&lt;br /&gt;
 &lt;br /&gt;
===Hosszredukció===&lt;br /&gt;
Többnyire a fordítók, ha nem mondjuk meg explicite, a dupla pontosságú változatot választják a lehetséges változó- és függvény típusok közül. Amennyiben a számításunk nem igényli a dupla pontosságot, akkor használjuk a forrásban mindenhol az adott típus vagy függvény egyszeres pontosságú változatát, mellyel jelentős teljesítmény növekedést tudunk elérni. Továbbá az egyes műveletek futási ideje nem egyforma, így a műveletet helyettesíthetjük más művelettel, ami gyorsabb, akkor ismét javíthatunk a hatékonyságon. &amp;lt;br /&amp;gt;&lt;br /&gt;
Íme pár példa, amit alkalmazni lehet:&lt;br /&gt;
* &amp;quot;''/''&amp;quot; művelet helyett &amp;quot;''*''&amp;quot; -t használjunk, mert azt gyorsabban végre tudja hajtani a processzor&lt;br /&gt;
* ''pow'' =&amp;gt; ''1.0 / sqrt()''    , tetszőleges hatványozás, pl.: 3/2-ik hatvány&lt;br /&gt;
* hardver áltatl támogatott műveletek:&lt;br /&gt;
** ''exp'' =&amp;gt; ''exp2''&lt;br /&gt;
** ''log'' =&amp;gt; ''log2''&lt;br /&gt;
* dupla pontosság -&amp;gt; egyszeres pontosság lebegőpontos számok és műveletek esetén &lt;br /&gt;
** ''1.0'' =&amp;gt; ''1.0f''&lt;br /&gt;
** ''pow'' =&amp;gt; ''powf''&lt;br /&gt;
&lt;br /&gt;
===Vektorizáció===&lt;br /&gt;
A vektorizáció azon fordítási időben eldönthető optimalizációs faktor, amely nagy mértékben függ attól a hardver felépítésétől, amire éppen a fordítás történni fog. Legfontosabb befolyásoló tényezők a processzor cache méretei illetve a vektor műveletekért felelős aritmetikai egységének jellemzői mivel ezeket használja fel a fordító az optimalizálási folyamata során. &amp;lt;br /&amp;gt;&lt;br /&gt;
Az automatikus vektorizáció minimum -O2 szinttől érvényes, de mivel általában ez a szint az alapértelmezett, ezért minden fordításnál alkalmazásra kerül. &amp;lt;br /&amp;gt;&lt;br /&gt;
A fordító programnak a következő kapcsolókat lehet megadni, melyek segítenek a minél jobb vektorizációban:&lt;br /&gt;
* ''-qopt-report=&amp;lt;szám&amp;gt;'' ; az optimalizációs jelentés részletességét lehet megadni 0-tól 5-ig, ahol az 5-s szint a legrészletesebbet jelenti&lt;br /&gt;
* ''-qopt-report-phase:vec'' ; a vektorizációról szeretnék jelentést kapni&lt;br /&gt;
* ''-opt-report-file=&amp;lt;fájlnév&amp;gt;'' ; megadható a riportfájl neve&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
icpc autovec.cc -mmic -qopt-report=5 -opt-report-file=vectorization.oMIC.optrpt -qopt-report-phase:vec&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====SIMD====&lt;br /&gt;
Az adatok hatékonyabb feldolgozására az egyik megvalósítás a SIMD (Single Instruction Multiple Data), amikor is a különálló adatokat úgy kezeli, mintha az vektorok volnának és azon végzi el a vektor-műveletet. Ezzel egy a hardver által támogatott műveletet végzünk egy lépésben egy nagyobb adatmennyiségen, ezzel párhuzamosítva a feldolgozási folyamatot. Ezzel a módszerrel nagyon jó adat szintű párhuzamosítást lehet elérni.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Fájl:Simd.png|SIMD]]&lt;br /&gt;
&lt;br /&gt;
Főbb támogatott műveletek:&lt;br /&gt;
* aritmetikai műveletek, bináris operátorok&lt;br /&gt;
* összehasonlítások&lt;br /&gt;
* konverziók és típus kasztolások&lt;br /&gt;
* bit-műveletek: NOT, AND, OR, XOR, XAND&lt;br /&gt;
&lt;br /&gt;
Használata: '''simd''' fordítási direktívával lehetséges &amp;lt;br /&amp;gt;&lt;br /&gt;
Például:&lt;br /&gt;
* egyszerű vektorizáció&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma simd&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
  A[i] += B[i];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* adat párhuzamosítás OpenMP segítségével&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for simd&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
  DoSomeWork(A[i]);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====AoS -&amp;gt; SoA====&lt;br /&gt;
A kiindulás probléma az, hogy a struktúrákat tartalmazó vektort nem lehet jól vektorizálni, mivel a cache-ben tárolt adatok nagy többsége feleslegesen van bent, mivel azon adatokat csak egy későbbi ciklus futás során fogja az alkalmazás feldolgozni. Tehát ezen adatok jelenleg feleslegesen foglalják a cache területet, ahelyett, hogy hasznos adatot tárolnánk ott.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
struct MyStructure {&lt;br /&gt;
  float x, y, z;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void My_Function(cons int nItem, MyStructure* const Items, const float delta) {&lt;br /&gt;
  for (int i = 0; i &amp;lt; nItem; i++) {&lt;br /&gt;
    ...&lt;br /&gt;
    for (int j = 0; j &amp;lt; nItem; j++) {&lt;br /&gt;
      const float dx = Items[j].x - Items[i].x;&lt;br /&gt;
      const float dy = Items[j].y - Items[i].y;&lt;br /&gt;
      const float dz = Items[j].z - Items[i].z;&lt;br /&gt;
      ...  &lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  ... &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A vektorban a struktúrák tárolása helyett minden elemnek egy dedikált vektort allokálunk és így könnyen tudja a fordító vektorizálni azt. Természetesen segítségként a vektor feldolgozását &amp;quot;tömbösítettük&amp;quot; a tileSize méret megadásával illetve a ''unroll'' fordítási direktíva segítségével.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
struct MyStructureSet {&lt;br /&gt;
  float *x, *y, *z;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void My_Function(cons int nItem, MyStructure* const Items, const float delta) {&lt;br /&gt;
  const int tileSize = 16;&lt;br /&gt;
#pragma omp parallel for schedule(guided)&lt;br /&gt;
  for (int ii = 0; ii &amp;lt; nItem; ii+= tileSize) {&lt;br /&gt;
    ...&lt;br /&gt;
#pragma unroll(tileSize)&lt;br /&gt;
    for (int j = 0; j &amp;lt; nItem; j++) {&lt;br /&gt;
#pragma unroll(tileSize)&lt;br /&gt;
      for (int i = ii; i &amp;lt; ii + tileSize; i++) {&lt;br /&gt;
        const float dx = Items.x[j] - Items.x[i];&lt;br /&gt;
        const float dy = Items.y[j] - Items.y[i];&lt;br /&gt;
        const float dz = Items.z[j] - Items.z[i];&lt;br /&gt;
        ...&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  ... &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Hatékony hossz választás====&lt;br /&gt;
Olyan esetben jelent előnyt, ha például az n*n-es mátrix esetén n nem osztható 16-val, mert akkor a vektorizáció nem teljesen hajtható végre. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// n%16 != 0&lt;br /&gt;
float* A = _mm_malloc(sizeof(float)*n*n, 64);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Ilyen esetben mindig marad egy kis vektor részlet, amire elemenként kell elvégezni a műveletet.&amp;lt;br /&amp;gt;&lt;br /&gt;
A megoldást az jelenti, hogy a memória allokálást kiterjesztjük és nagyobb memória területet foglalunk a változónak, így már alkalmazható lesz a vektorizáció, mivel a maradvány területen is el tudjuk végezni a műveletet egy lépésben.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int lda=n;&lt;br /&gt;
if (n % 16 != 0) lda += (16 - n%16); // most lda%16==0&lt;br /&gt;
float* A = _mm_malloc(sizeof(float)*n*lda, 64);&lt;br /&gt;
&lt;br /&gt;
// használat&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
  for (int j = 0; j &amp;lt; n; j++)&lt;br /&gt;
    A[i*lda + j] = ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====&amp;quot;Héj ciklusok&amp;quot; kiküszöbölése====&lt;br /&gt;
Ciklusok esetén lehetséges, hogy a vektorizációt nem teljes mértékben ismeri fel a fordító program. &lt;br /&gt;
Ekkor segíthetünk a fordítónak egy fordítási direktívával ('''#pragma vector aligned'''), ha teljes mértékben biztosak vagyunk, hogy a ciklusunk vektorizálható!&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma vector aligned&lt;br /&gt;
  for (int j = 0; j &amp;lt; n; j++)&lt;br /&gt;
    A[i*lda + j] -= ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Ettől jelentős futás idejű javulást érhetünk el, mivel ezzel a #pragma-val kikapcsoltuk a futás idejű ellenőrzést, de előfordulhatnak olyan esetek, főleg amikor nem jól mértük fel a ciklusunkat és nem sikerült a vektorizálás, hogy ''Segmentation fault'' hibára fogunk futni.&lt;br /&gt;
&lt;br /&gt;
====Regularizáció====&lt;br /&gt;
Van egy ciklusunk, ami egy vektor elemeit módosítja. A processzor az adatokat &amp;quot;csokorba szedve&amp;quot; emeli be a belső kis méretű cache-be. Egyszerre természetesen több vektor elem is bekerül a a cache-be ezzel gyorsítva a feldolgozást. Ezen vektor iterációk valahogyan ráilleszkedik a vektorra.&lt;br /&gt;
&lt;br /&gt;
[[Fájl:regularization.png|Vektor regularizáció]]&lt;br /&gt;
&lt;br /&gt;
Abban az esetben, amikor ez az illesztés nem teljesen tökéletes (lsd. piros elemek), keletkeznem héj ciklusok, melyeket a regularizációval is eliminálni lehet. &amp;lt;br /&amp;gt;&lt;br /&gt;
A feladat eredetileg valahogyan így nézhet ki:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
for (int i = 0; i&amp;lt;n; i++)&lt;br /&gt;
  A[i]=...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ez a következő átalakítással már alkalmas vektorizációra és nem lesz &amp;quot;peel loop&amp;quot; probléma.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
for (int b=0; b&amp;lt;n; b++) {&lt;br /&gt;
  const int jStart = b- b%16; // mindig osztható 16-val&lt;br /&gt;
  ...&lt;br /&gt;
#pragma simd&lt;br /&gt;
  for (int j = jStart; j&amp;lt;n; j++)&lt;br /&gt;
    A[j]=...&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Párhuzamosítás===&lt;br /&gt;
Több féle mód létezik a párhuzamosításra. Korábban említettük a SIMD technikát, ami adatpárhuzamosításra való 1 processzoron belül, de létezik még processzorok közti párhuzamosítás, amit az OpenMP segítségével tudunk hatékonyan megvalósítani. Amennyiben gépek közti párhuzamosításra, üzenek küldésre van szükség, akkor az MPI-t kell segítségül hívni. Általános megoldás nem létezik minden feladatra, mert MPI is alkalmas lehet gépen belüli párhuzamosításra, nem csak az OpenMP. Amit mérlegelni kell, hogy mi az amire az alkalmazásnak szükséges van illetve mekkora kommunikációs overhead keletkezik, ha lecseréljük az OpenMP-t MPI-ra. Általában az egymástól független számításokat OpenMP-vel, míg a kommunikációt igénylő feladatokra MPI-t szoktak használni.&lt;br /&gt;
&lt;br /&gt;
====Szálak párhuzamosítása====&lt;br /&gt;
Az OpenMP-nek környezeti változók segítségével megadható a párhuzamosan futtatható szálak száma. Ezt a következő paranccsal tudjuk megtenni, ahol 5-re állítjuk.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export OMP_NUM_THREADS=5&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Forrásban a párhuzamos szálakat a ''#pragma omp parallel'' segítségével adhatjuk meg.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for              	//külső for szálak párhuzamosítása&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
#pragma simd				// vektorizáció a belső cikluson&lt;br /&gt;
  for (int j = 0; j &amp;lt; m; j++)&lt;br /&gt;
    My_Function(A[i][j]);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A fordítási direktívák össze is vonhatóak, ilyenkor 1 ciklus is elég.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for simd&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
    My_Function(A[i]);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=====Változók láthatósága, megosztása=====&lt;br /&gt;
Változók hatókörét a programozási nyelven kívül pragma-k segítségével is beállíthatjuk, hogy megosztott legyen a változó vagy a szálra nézve saját példány. Amennyiben nem definiáljuk felül, akkor minden változó megosztott. A felül definiálást a private és a shared paraméterekkel lehet megadni az OpenMP-nek.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int A, B, C;&lt;br /&gt;
&lt;br /&gt;
#pragma omp parallel private(A) shared(B)&lt;br /&gt;
{...}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Az itteni példában az B és C megosztott változó a szálak között, míg A-t minden szálnak lemásolja a futás kezdetén.&lt;br /&gt;
&lt;br /&gt;
====Párhuzamos szálak ütemezése====&lt;br /&gt;
OpenMp a szálak ütemezésére három féle módod kínál fel.&lt;br /&gt;
Módok:&lt;br /&gt;
* '''static''' (alapértelmezett): egyenletesen arányban egymás után elosztja a processzeket az indított szálakon, ezért a futási idő tetszőlegesen elhúzódhat, ha egyes processzek lassabban futnak le.&lt;br /&gt;
* '''dynamic''': minden esetben az első szabad szálra kerül a következő processz ütemezése, folyamatos kihasználtságot elérve, de &amp;quot;költséges&amp;quot; a sok-sok ütemezés&lt;br /&gt;
* '''guided''': Az előző két mód ötvözése. Nagyobb procesz számot ütemez az éppen szabad szálakra, melyek számát folyamatosan csökkenti a futás előre haladtával, hogy körülbelül egyszerre fejeződjön be minden szál futása, ezzel csökkentve a dynamic esetén magad ütemezési időráfordítást.&lt;br /&gt;
&lt;br /&gt;
Mindhárom esetben megadható az egyszerre ütemezett processzek száma, amit a ''chunk'' értéke határoz meg. Ha megvan adva a chunk értéke, akkor az minden esetben annak megfelelő processzámot ütemez a szálakra.&lt;br /&gt;
Használata:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel [schedule (&amp;lt;mode&amp;gt;,&amp;lt;chunk&amp;gt;) {...}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Szálak szinkronizációja====&lt;br /&gt;
Szála szinkronizálását többféle módon lehet megvalósítani, de ezen megoldások sajno jelentősen lelassítják a program futását.&lt;br /&gt;
* mutexxel: nagyon lassú, állandóan várnak egymásra a párhuzamos szálak&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++) {&lt;br /&gt;
#pragma omp critical&lt;br /&gt;
  { // kritikus szekció, csak egy szál hajthatja végre&lt;br /&gt;
    total = total + i;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* atomi művelet definiálása: sajnos még ez is lassú, de itt már nem olyan &amp;quot;erős&amp;quot; a szinkronizáció, ezért rendkívül sok limitációja van az itt használható atomi műveleteknek.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++) {&lt;br /&gt;
#pragma omp atomic&lt;br /&gt;
   total += i;&lt;br /&gt;
}	&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=====Párhuzamos redukció=====&lt;br /&gt;
Az itt bemutatott eljárás gyorsabb mint a fenti szinkronizációs megoldás, de sajnos nem alkalmazható vektorokra! A redukciót a ''reduction(operation: scalar)'' formában kell megadni.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int sum = 0;&lt;br /&gt;
#pragma omp parallel for reduction(+: sum)&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++) {&lt;br /&gt;
  sum = sum + i;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Tulajdonképpen a fenti problémakörre a következő minta ad megoldást. Ekkor egy külön változóban akkumuláltatjuk a szál általi összeget és ezen értékeke összegyűjtését végezzük csak atomi műveletként.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int sum = 0;&lt;br /&gt;
#pragma omp parallel&lt;br /&gt;
{&lt;br /&gt;
  int sum_thr = 0;&lt;br /&gt;
#pragma omp for&lt;br /&gt;
  for (int i=0; i&amp;lt;n; i++)&lt;br /&gt;
    sum_thr += i;&lt;br /&gt;
&lt;br /&gt;
#pragma omp atomic&lt;br /&gt;
  sum += sum_thr;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Ciklusok párhuzamosítása====&lt;br /&gt;
Ciklus párhuzamosítására a következő mintát szokás használni.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel&lt;br /&gt;
{&lt;br /&gt;
  // Az itt definiált forrást minden szál esetén végre fog hajtani&lt;br /&gt;
  ...&lt;br /&gt;
#pragma omp for [schedule (&amp;lt;mode&amp;gt;,&amp;lt;chunk&amp;gt;)&lt;br /&gt;
  for (int i = 0; i &amp;lt; n; i++) {&lt;br /&gt;
    // Ez a kódrész lesz párhuzamosítva&lt;br /&gt;
    ...&lt;br /&gt;
  }&lt;br /&gt;
  // ... és ez a köd is minden szál esetén végrehajtódik&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Ciklus kifejtés====&lt;br /&gt;
A technika már egy példa erejéig a vektorizációnál is látható, de ott nem került részletezésre. A megoldás lényege, hogy a kellően nagy ciklust szétdaraboljuk kisebb darabokra, amit már külön feldolgozhatóak, ezzel párhuzamosítva a folyamatokat. Az egy darab kiindulási ciklusból két darab egymásba ágyazott ciklust hozunk létre, ami ugyanazon a feladattéren dolgozik. &lt;br /&gt;
&lt;br /&gt;
Eredeti állapot:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++) {...}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Átírt változat:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int STRIP=1024;&lt;br /&gt;
for (int ii = 0; ii &amp;lt; n; ii += STRIP)&lt;br /&gt;
 for (int i = ii; i &amp;lt; ii+STRIP; i++) {...}&lt;br /&gt;
&lt;br /&gt;
// ha még van maradék elem, akkor azokon is végrehajtjuk a számításokat&lt;br /&gt;
if (n%STRIP != 0) {&lt;br /&gt;
  for (i=n-(n%STRIP)+1; i&amp;lt;n; i++) {...}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A darabok mérete (STRIP értéke) az adott rendszer és feladat függvényében változhat, tehát ez egyfajta &amp;quot;tuning&amp;quot; paraméter.&lt;br /&gt;
&lt;br /&gt;
====Automatikus ciklusösszevonás====&lt;br /&gt;
Az OpenMP támogatást nyújt több egymásba ágyazott ciklus &amp;quot;összevonására&amp;quot;. Ehhez csak a '''collaps''' fordítási direktívát és az összevonandó ciklusok számát kell megadni. &amp;lt;br /&amp;gt;&lt;br /&gt;
FONTOS: ekkor az automatikus vektorizáció kikapcsolásra kerül!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for collaps(2)&lt;br /&gt;
for (int i=0; i&amp;lt;n; i++)&lt;br /&gt;
  for( int j=0; j&amp;lt;m; j++){...}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A fneti példában a 2db for ciklis kerül párhuzamosításra úgy, hogy a fordítás során 1db ciklus generálódik belőle, ami a következő paraméterezéssel ekvivalens:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for&lt;br /&gt;
for (int c=0; c &amp;lt; n*m; c++) {&lt;br /&gt;
  int i = c/n;&lt;br /&gt;
  int j = c%n;&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Szigeti(AT)niif.hu</name></author>	</entry>

	<entry>
		<id>https://wiki.niif.hu/index.php?title=Intel_Xeon_Phi&amp;diff=3757</id>
		<title>Intel Xeon Phi</title>
		<link rel="alternate" type="text/html" href="https://wiki.niif.hu/index.php?title=Intel_Xeon_Phi&amp;diff=3757"/>
				<updated>2016-05-05T12:41:02Z</updated>
		
		<summary type="html">&lt;p&gt;Szigeti(AT)niif.hu: /* Párhuzamos redukció */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Az oldalon található információk segítséget nyújtanak a HPC felhasználóinknak, hogy alkalmazásaikat miként portolhatják valamint optimalizálhatják a hatékonyabb futást szem előtt tartva Intel Xeon Phi koprocesszor kártyákon.&lt;br /&gt;
&lt;br /&gt;
==Hardver bemutatása==&lt;br /&gt;
Intel® Xeon Phi™ Coprocessor 7120P&lt;br /&gt;
&amp;lt;gallery&amp;gt;&lt;br /&gt;
Fájl:Xeon-phi-family-rwd.png.rendition.intel.web.720.405.png|&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-  align=&amp;quot;center&amp;quot;&lt;br /&gt;
! CPU&lt;br /&gt;
! threads/Core&lt;br /&gt;
!max threads&lt;br /&gt;
! CPU Freq&lt;br /&gt;
! Memória&lt;br /&gt;
! L1 cache&lt;br /&gt;
! L2 cache&lt;br /&gt;
! Interfész&lt;br /&gt;
! Ptot&lt;br /&gt;
! Számítási kapacitás&lt;br /&gt;
|-  align=&amp;quot;center&amp;quot;&lt;br /&gt;
| 61&lt;br /&gt;
| 4 Hardver&lt;br /&gt;
| 244&lt;br /&gt;
| 1238 GHz&lt;br /&gt;
| 16GB GDDR5&lt;br /&gt;
| 32KB/core&lt;br /&gt;
| 512KB/core&lt;br /&gt;
| PCIe&lt;br /&gt;
| 300W&lt;br /&gt;
| 1,2 TFlop/s dupla pontosság&lt;br /&gt;
|-  align=&amp;quot;center&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
| OpenMP-nél csak '''240'''&lt;br /&gt;
|&lt;br /&gt;
| ~100 cycles&lt;br /&gt;
~174 GB/s&lt;br /&gt;
| ~3 cycles&lt;br /&gt;
| ~10 cycles&lt;br /&gt;
| ~7 GB/s&lt;br /&gt;
~1 microsecond&lt;br /&gt;
|&lt;br /&gt;
| 2,4 TFlop/s egyszeres pontosság&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Az Intel® 1. generációs Xeon Phi™ koprocesszorában található 61 processzor ugyanazt a 16GB memóriát éri el, de semmilyen módon se fér hozzá közvetlenül a hoszt gép fizikai memóriájához.&amp;lt;br /&amp;gt;&lt;br /&gt;
[[Budapest2_klaszter|Budapest2]] és [[HPC#Szupersz.C3.A1m.C3.ADt.C3.B3g.C3.A9peink_.C3.B6sszehasonl.C3.ADt.C3.A1sa|Debrecen3-Phi]] gépeinkben számítási nodonként 2-2 Phi™ koprocesszor található. &amp;lt;br/&amp;gt;&lt;br /&gt;
A kártyákon speciális célú Linux kernel fut melyre a belépés limitáltan csak a rendszergazdák számára lehetséges SSH-n keresztül a hoszt gépről. Természetesen a kártyára belépve olyan érzésünk lehet, mintha egy sok processzoros gépen dolgoznánk, ami rendelkezik rengeteg memóriával. Ez így is van, de a hatékonyabb kihasználás érdekében az SSH-n keresztüli elérés nem lehetséges, csak a Slurm segítségével valamint a programozási nyelvek által támogatott csatornákon keresztül. Ennek részletezése lentebb megtalálható.&lt;br /&gt;
&lt;br /&gt;
==Használati módok==&lt;br /&gt;
A koprocesszor használatai módjait két csoportba lehet sorolni. A '''natív''' és az '''offload''' használatot.  &amp;lt;br/&amp;gt;&lt;br /&gt;
Ezen használati módokról a lenti fejezetekben több részletet is megtudhat. Az itt megfogalmazott &amp;quot;támogatás&amp;quot; csak a mi infrastruktúránkra érvényes! &amp;lt;br /&amp;gt;&lt;br /&gt;
A bemutatott példák ''c++'' nyelven vannak, de természetesen ''c'' és ''fortran'' esetén is megtalálható az ekvivalens módszer.&lt;br /&gt;
&lt;br /&gt;
===a) nem támogatott mód===&lt;br /&gt;
====közvetlen natív használat====&lt;br /&gt;
A natív mód arra szolgál, hogy a MIC kártyára  lefordított alkalmazásunkat felmásoljuk a kártyára, majd belépünk SSH-n a kártyára és azon futtatjuk a bináris kódunkat. &amp;lt;br /&amp;gt;&lt;br /&gt;
Ez a legegyszerűbb használati mód, ugyanis nincs más dolgunk, csak a '''-mmic''' kapcsolót használni a fordításnál és a fordító egy máris a MIC architektúrára kész bináris készít nekünk. &amp;lt;br/&amp;gt;&lt;br /&gt;
Workflow a következő lenne:&lt;br /&gt;
# Alkalmazás megírása. például [[Média:hello.cc|''hello.cc'']]&lt;br /&gt;
# Alkalmazás fordítása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;icpc -o hello-MIC -mmic hello.cc&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Másolás a MIC kártyára &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;scp hello-MIC mic0:&amp;lt;/pre&amp;gt;&lt;br /&gt;
# belépés a kártyára &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;ssh mic0&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Alkalmazás futtatása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;./hello-MIC&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===b) támogatott, de nem preferált használat===&lt;br /&gt;
====automatikus kódátvitel====&lt;br /&gt;
Ebben az esetben nem kell átmásolnunk a bináris alkalmazásunkat a kártyára sőt még csak be se kell lépnünk, mert ezt a két lépést egy beépített tool végzi el. &amp;lt;br/&amp;gt;&lt;br /&gt;
A tool eléréséhez szükséges parancs neve: '''micnativeloadex'''&lt;br /&gt;
&lt;br /&gt;
Ebben az esetben a workflow a következőképpen módosul:&lt;br /&gt;
# Alkalmazás megírása. például [[Média:hello.cc|''hello.cc'']]&lt;br /&gt;
# Alkalmazás fordítása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;icpc -o hello-MIC -mmic hello.cc&amp;lt;/pre&amp;gt;&lt;br /&gt;
# A tool futásához szükséges library-k megadása &amp;lt;br&amp;gt; &amp;lt;pre&amp;gt;export SINK_LD_LIBRARY_PATH=/opt/intel/composerxe/compiler/lib/mic&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Alkalmazás futtatása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;micnativeloadex hello-MIC&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| BUDAPEST2[cn10] phi (0)$ export SINK_LD_LIBRARY_PATH=/opt/intel/composerxe/compiler/lib/mic &amp;lt;br/&amp;gt;BUDAPEST2[cn10] phi (0)$ micnativeloadex hello-MIC &amp;lt;br/&amp;gt;Hello world! I have 244 logical cores.&amp;lt;br/&amp;gt;BUDAPEST2[cn10] phi (0)$&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===c) támogatott módok===&lt;br /&gt;
====Offload====&lt;br /&gt;
Az Offload modell esetén szükséges a forráskód módosítása, mégpedig úgy, hogy azon kódrészletet, amit a koprocesszoron szeretnénk futtatni, ki kell egészíteni egy fordítási direktívával. Az alkalmazás fordításakor elegendő csak a CPU-ra való binárist készíteni mivel a modell végzi a megfelelő forrás részlet mozgatását és futtatását.&amp;lt;br /&amp;gt;&lt;br /&gt;
[[Fájl:offload.png|Offload modell]] &amp;lt;br /&amp;gt;&lt;br /&gt;
Példánkban jól látható, hogy a második kiíratás már a koprocesszoron fut.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
int main(int argc, char * argv[]) {&lt;br /&gt;
  printf(&amp;quot;Hello world from host!\n&amp;quot;);&lt;br /&gt;
#pragma offload target(mic)&lt;br /&gt;
  {&lt;br /&gt;
    printf(&amp;quot;Hello world from coprocessor!\n&amp;quot;); fflush(stdout);&lt;br /&gt;
  }&lt;br /&gt;
  printf(&amp;quot;Bye\n&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ebben a példában az operációs rendszer választja ki, hogy melyik Phi kártyán fusson le a kódrészlet. Amennyiben mi szeretnénk explicite megmondani, hogy melyik kártyán fusson, akkor a fordítási direktívában a mic kulcsszó után kell tenni egy &amp;quot;:&amp;quot;-t és a kártya sorszámát [mi esetünkben 0 vagy 1]. Ekkor így nézne ki a direktíva: ''#pragma offload target(mic:1)''&lt;br /&gt;
&lt;br /&gt;
Amennyiben az offload szekcióban függvény hívás is szerepel, akkor szükségeltetik az függvény futásához szükséges változók adatterületének átmásolása a Phi kártyára, majd a lefutás után visszamásolni a hoszt gépre. Ezen másolást az ''offload_attribute'' ''push'' és ''pop'' paraméterei segítségével határozhatjuk meg. &amp;lt;br /&amp;gt;&lt;br /&gt;
A következő mátrix-vektor szorzás példánál az A mátrix globális változó, míg a b és c vektorok a függvény paraméterei.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//A változók felmásolásra kerülnek a Xeon Phi-re&lt;br /&gt;
#pragma offload_attribute(push, target(mic))&lt;br /&gt;
  double* A;&lt;br /&gt;
  void MatrixVector(double *b, double *c, double m, double n) {&lt;br /&gt;
    ...&lt;br /&gt;
    c[] = A[..]*b[]&lt;br /&gt;
  }&lt;br /&gt;
// felmásolt objektumok visszamásolása&lt;br /&gt;
#pragma offload_attribute(pop)&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
  ...&lt;br /&gt;
#pragma offload target(mic) in(A[0:n*m])&lt;br /&gt;
// így is meg lehetne adni: #pragma offload target(mic:0) in(A : length(n*m))	        &lt;br /&gt;
  {&lt;br /&gt;
    MatrixVector(b, c, m, n);&lt;br /&gt;
  }&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ekkor minden memória területet 2x kell másolni, egyszer fel a kártyára, majd másodszor vissza a hoszt gépre, pedig elegendő lenne csak az eredmény vektort visszamásolni. Ezen optimalizálással egy későbbi fejezetben foglalkozunk. &amp;lt;p /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Futás során kaphatunk részletes riportot is, hogy pontosan milyen adatmozgatások történnek az alkalmazás futása során. Ehhez nincs szükség másra csak az '''OFFLOAD_REPORT''' környezeti változó megfelelő beállítása. A beállított értékek 1, 2 vagy 3 lehetnek, attól függően mennyire részletes riportot szeretnénk kapni. &amp;lt;br /&amp;gt;&lt;br /&gt;
beállítás:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export OFFLOAD_REPORT=3&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
példa kimenet a hello alkalmazásra:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Hello world from host!&lt;br /&gt;
[Offload] [MIC 0] [File]                    hello_offload.cc&lt;br /&gt;
[Offload] [MIC 0] [Line]                    5&lt;br /&gt;
[Offload] [MIC 0] [Tag]                     Tag 0&lt;br /&gt;
Hello world from coprocessor!&lt;br /&gt;
[Offload] [HOST]  [Tag 0] [CPU Time]        0.554653(seconds)&lt;br /&gt;
[Offload] [MIC 0] [Tag 0] [CPU-&amp;gt;MIC Data]   0 (bytes)&lt;br /&gt;
[Offload] [MIC 0] [Tag 0] [MIC Time]        0.000140(seconds)&lt;br /&gt;
[Offload] [MIC 0] [Tag 0] [MIC-&amp;gt;CPU Data]   0 (bytes)&lt;br /&gt;
Bye&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Offload + OpenMP====&lt;br /&gt;
Amennyiben nem csak 1 processzort szeretnénk használni a Phi kártyánkon és az alkalmazásunkban használt számítás megengedi a párhuzamosítást, akkor az OpenMP segítségével párhuzamosíthatjuk a megfelelő számítást. &amp;lt;br /&amp;gt;&lt;br /&gt;
[[Fájl:Offload+openmp.png|Offload modell és OpenMP]] &amp;lt;br /&amp;gt;&lt;br /&gt;
Példánk maradjon ugyanúgy a mátrix-vektor szorzás. Amennyiben a teljes kártya adta kapacitást ki szeretnénk használni, akkor az OMP threadek számát 240-re kell beállítani. Azért nem 244-re, mivel 1 CPU-t dedikáltan csak a kommunikációra használ a rendszer és 1 CPU 4 hardver threaddel rendelkezik.&lt;br /&gt;
Ekkor a példánk így változik:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;omp.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#pragma offload_attribute(push, target(mic))&lt;br /&gt;
void multiply(int n, int m, double* A, double* b, double* c){&lt;br /&gt;
  const int nCPUs = omp_get_max_threads();   //240&lt;br /&gt;
  omp_set_num_threads(nCPUs);&lt;br /&gt;
#pragma omp parallel for schedule(dynamic, 1)&lt;br /&gt;
  for ( int i = 0 ; i &amp;lt; m ; i++)&lt;br /&gt;
     for ( int j = 0 ; j &amp;lt; n ; j++)&lt;br /&gt;
       c[i] += A[i*n+j] * b[j];&lt;br /&gt;
}&lt;br /&gt;
#pragma offload_attribute(pop)&lt;br /&gt;
&lt;br /&gt;
int main(){&lt;br /&gt;
  double * A = (double*) malloc(sizeof(double)*n*m);&lt;br /&gt;
  ...&lt;br /&gt;
#pragma offload target(mic) in(A:length(n*m))&lt;br /&gt;
     multiply(n, m, A, b, c);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Az alkalmazás fordításánál a fordítónak meg kell adni, hogy használhja az openmp library-ket is. Ezt a '''-qopenmp''' kapcsolóval tudjuk elérni.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
icpc -o matrix-CPU -qopenmp matrix.cc&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====MPI + Offload + OpenMP====&lt;br /&gt;
Abban az esetben, ha 1 számítási node nem elég, MPI segítségével össze tudunk kötni több nodet, heterogén rendszert alkotva, és azokon alkalmazható a fenti Offload + OpenMP modell. &amp;lt;br /&amp;gt;&lt;br /&gt;
[[Fájl:Mpi+offload+openmp.png|MPI elosztás valamint Offload modell és OpenMP]] &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Alkalmazások optimalizálása a kártyára==&lt;br /&gt;
Ebben a fejezetben optimalizálási lehetőségeket mutatunk be, melyek segítségül szolgálhatnak az egyes alkalmazások hatékonyabb futása érdekében ezzel jobban kihasználni a rendelkezésre álló erőforrások adta számítási kapacitás.&lt;br /&gt;
&lt;br /&gt;
===Környezeti változók===&lt;br /&gt;
Lehetséges csak a Phi kártyákon használt környezeti változók beállítása, amit az alkalmazásunk használni is tud. &amp;lt;br /&amp;gt;&lt;br /&gt;
Példánkban az OpenMP esetén használható szálak számát állíthatjuk be:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export MIC_ENV_PREFIX=XEONPHI&lt;br /&gt;
export OMP_NUM_THREADS=12&lt;br /&gt;
export XEONPHI_OMP_NUM_THREADS=240&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
A beállított prefix tetszőleges karaktersorozat lehet, a lényeg, hogy ezt a beállított prefixet használjuk a környezeti változók esetén is!&lt;br /&gt;
 &lt;br /&gt;
===Hosszredukció===&lt;br /&gt;
Többnyire a fordítók, ha nem mondjuk meg explicite, a dupla pontosságú változatot választják a lehetséges változó- és függvény típusok közül. Amennyiben a számításunk nem igényli a dupla pontosságot, akkor használjuk a forrásban mindenhol az adott típus vagy függvény egyszeres pontosságú változatát, mellyel jelentős teljesítmény növekedést tudunk elérni. Továbbá az egyes műveletek futási ideje nem egyforma, így a műveletet helyettesíthetjük más művelettel, ami gyorsabb, akkor ismét javíthatunk a hatékonyságon. &amp;lt;br /&amp;gt;&lt;br /&gt;
Íme pár példa, amit alkalmazni lehet:&lt;br /&gt;
* &amp;quot;''/''&amp;quot; művelet helyett &amp;quot;''*''&amp;quot; -t használjunk, mert azt gyorsabban végre tudja hajtani a processzor&lt;br /&gt;
* ''pow'' =&amp;gt; ''1.0 / sqrt()''    , tetszőleges hatványozás, pl.: 3/2-ik hatvány&lt;br /&gt;
* hardver áltatl támogatott műveletek:&lt;br /&gt;
** ''exp'' =&amp;gt; ''exp2''&lt;br /&gt;
** ''log'' =&amp;gt; ''log2''&lt;br /&gt;
* dupla pontosság -&amp;gt; egyszeres pontosság lebegőpontos számok és műveletek esetén &lt;br /&gt;
** ''1.0'' =&amp;gt; ''1.0f''&lt;br /&gt;
** ''pow'' =&amp;gt; ''powf''&lt;br /&gt;
&lt;br /&gt;
===Vektorizáció===&lt;br /&gt;
A vektorizáció azon fordítási időben eldönthető optimalizációs faktor, amely nagy mértékben függ attól a hardver felépítésétől, amire éppen a fordítás történni fog. Legfontosabb befolyásoló tényezők a processzor cache méretei illetve a vektor műveletekért felelős aritmetikai egységének jellemzői mivel ezeket használja fel a fordító az optimalizálási folyamata során. &amp;lt;br /&amp;gt;&lt;br /&gt;
Az automatikus vektorizáció minimum -O2 szinttől érvényes, de mivel általában ez a szint az alapértelmezett, ezért minden fordításnál alkalmazásra kerül. &amp;lt;br /&amp;gt;&lt;br /&gt;
A fordító programnak a következő kapcsolókat lehet megadni, melyek segítenek a minél jobb vektorizációban:&lt;br /&gt;
* ''-qopt-report=&amp;lt;szám&amp;gt;'' ; az optimalizációs jelentés részletességét lehet megadni 0-tól 5-ig, ahol az 5-s szint a legrészletesebbet jelenti&lt;br /&gt;
* ''-qopt-report-phase:vec'' ; a vektorizációról szeretnék jelentést kapni&lt;br /&gt;
* ''-opt-report-file=&amp;lt;fájlnév&amp;gt;'' ; megadható a riportfájl neve&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
icpc autovec.cc -mmic -qopt-report=5 -opt-report-file=vectorization.oMIC.optrpt -qopt-report-phase:vec&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====SIMD====&lt;br /&gt;
Az adatok hatékonyabb feldolgozására az egyik megvalósítás a SIMD (Single Instruction Multiple Data), amikor is a különálló adatokat úgy kezeli, mintha az vektorok volnának és azon végzi el a vektor-műveletet. Ezzel egy a hardver által támogatott műveletet végzünk egy lépésben egy nagyobb adatmennyiségen, ezzel párhuzamosítva a feldolgozási folyamatot. Ezzel a módszerrel nagyon jó adat szintű párhuzamosítást lehet elérni.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Fájl:Simd.png|SIMD]]&lt;br /&gt;
&lt;br /&gt;
Főbb támogatott műveletek:&lt;br /&gt;
* aritmetikai műveletek, bináris operátorok&lt;br /&gt;
* összehasonlítások&lt;br /&gt;
* konverziók és típus kasztolások&lt;br /&gt;
* bit-műveletek: NOT, AND, OR, XOR, XAND&lt;br /&gt;
&lt;br /&gt;
Használata: '''simd''' fordítási direktívával lehetséges &amp;lt;br /&amp;gt;&lt;br /&gt;
Például:&lt;br /&gt;
* egyszerű vektorizáció&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma simd&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
  A[i] += B[i];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* adat párhuzamosítás OpenMP segítségével&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for simd&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
  DoSomeWork(A[i]);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====AoS -&amp;gt; SoA====&lt;br /&gt;
A kiindulás probléma az, hogy a struktúrákat tartalmazó vektort nem lehet jól vektorizálni, mivel a cache-ben tárolt adatok nagy többsége feleslegesen van bent, mivel azon adatokat csak egy későbbi ciklus futás során fogja az alkalmazás feldolgozni. Tehát ezen adatok jelenleg feleslegesen foglalják a cache területet, ahelyett, hogy hasznos adatot tárolnánk ott.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
struct MyStructure {&lt;br /&gt;
  float x, y, z;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void My_Function(cons int nItem, MyStructure* const Items, const float delta) {&lt;br /&gt;
  for (int i = 0; i &amp;lt; nItem; i++) {&lt;br /&gt;
    ...&lt;br /&gt;
    for (int j = 0; j &amp;lt; nItem; j++) {&lt;br /&gt;
      const float dx = Items[j].x - Items[i].x;&lt;br /&gt;
      const float dy = Items[j].y - Items[i].y;&lt;br /&gt;
      const float dz = Items[j].z - Items[i].z;&lt;br /&gt;
      ...  &lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  ... &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A vektorban a struktúrák tárolása helyett minden elemnek egy dedikált vektort allokálunk és így könnyen tudja a fordító vektorizálni azt. Természetesen segítségként a vektor feldolgozását &amp;quot;tömbösítettük&amp;quot; a tileSize méret megadásával illetve a ''unroll'' fordítási direktíva segítségével.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
struct MyStructureSet {&lt;br /&gt;
  float *x, *y, *z;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void My_Function(cons int nItem, MyStructure* const Items, const float delta) {&lt;br /&gt;
  const int tileSize = 16;&lt;br /&gt;
#pragma omp parallel for schedule(guided)&lt;br /&gt;
  for (int ii = 0; ii &amp;lt; nItem; ii+= tileSize) {&lt;br /&gt;
    ...&lt;br /&gt;
#pragma unroll(tileSize)&lt;br /&gt;
    for (int j = 0; j &amp;lt; nItem; j++) {&lt;br /&gt;
#pragma unroll(tileSize)&lt;br /&gt;
      for (int i = ii; i &amp;lt; ii + tileSize; i++) {&lt;br /&gt;
        const float dx = Items.x[j] - Items.x[i];&lt;br /&gt;
        const float dy = Items.y[j] - Items.y[i];&lt;br /&gt;
        const float dz = Items.z[j] - Items.z[i];&lt;br /&gt;
        ...&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  ... &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Hatékony hossz választás====&lt;br /&gt;
Olyan esetben jelent előnyt, ha például az n*n-es mátrix esetén n nem osztható 16-val, mert akkor a vektorizáció nem teljesen hajtható végre. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// n%16 != 0&lt;br /&gt;
float* A = _mm_malloc(sizeof(float)*n*n, 64);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Ilyen esetben mindig marad egy kis vektor részlet, amire elemenként kell elvégezni a műveletet.&amp;lt;br /&amp;gt;&lt;br /&gt;
A megoldást az jelenti, hogy a memória allokálást kiterjesztjük és nagyobb memória területet foglalunk a változónak, így már alkalmazható lesz a vektorizáció, mivel a maradvány területen is el tudjuk végezni a műveletet egy lépésben.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int lda=n;&lt;br /&gt;
if (n % 16 != 0) lda += (16 - n%16); // most lda%16==0&lt;br /&gt;
float* A = _mm_malloc(sizeof(float)*n*lda, 64);&lt;br /&gt;
&lt;br /&gt;
// használat&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
  for (int j = 0; j &amp;lt; n; j++)&lt;br /&gt;
    A[i*lda + j] = ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====&amp;quot;Héj ciklusok&amp;quot; kiküszöbölése====&lt;br /&gt;
Ciklusok esetén lehetséges, hogy a vektorizációt nem teljes mértékben ismeri fel a fordító program. &lt;br /&gt;
Ekkor segíthetünk a fordítónak egy fordítási direktívával ('''#pragma vector aligned'''), ha teljes mértékben biztosak vagyunk, hogy a ciklusunk vektorizálható!&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma vector aligned&lt;br /&gt;
  for (int j = 0; j &amp;lt; n; j++)&lt;br /&gt;
    A[i*lda + j] -= ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Ettől jelentős futás idejű javulást érhetünk el, mivel ezzel a #pragma-val kikapcsoltuk a futás idejű ellenőrzést, de előfordulhatnak olyan esetek, főleg amikor nem jól mértük fel a ciklusunkat és nem sikerült a vektorizálás, hogy ''Segmentation fault'' hibára fogunk futni.&lt;br /&gt;
&lt;br /&gt;
====Regularizáció====&lt;br /&gt;
Van egy ciklusunk, ami egy vektor elemeit módosítja. A processzor az adatokat &amp;quot;csokorba szedve&amp;quot; emeli be a belső kis méretű cache-be. Egyszerre természetesen több vektor elem is bekerül a a cache-be ezzel gyorsítva a feldolgozást. Ezen vektor iterációk valahogyan ráilleszkedik a vektorra.&lt;br /&gt;
&lt;br /&gt;
[[Fájl:regularization.png|Vektor regularizáció]]&lt;br /&gt;
&lt;br /&gt;
Abban az esetben, amikor ez az illesztés nem teljesen tökéletes (lsd. piros elemek), keletkeznem héj ciklusok, melyeket a regularizációval is eliminálni lehet. &amp;lt;br /&amp;gt;&lt;br /&gt;
A feladat eredetileg valahogyan így nézhet ki:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
for (int i = 0; i&amp;lt;n; i++)&lt;br /&gt;
  A[i]=...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ez a következő átalakítással már alkalmas vektorizációra és nem lesz &amp;quot;peel loop&amp;quot; probléma.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
for (int b=0; b&amp;lt;n; b++) {&lt;br /&gt;
  const int jStart = b- b%16; // mindig osztható 16-val&lt;br /&gt;
  ...&lt;br /&gt;
#pragma simd&lt;br /&gt;
  for (int j = jStart; j&amp;lt;n; j++)&lt;br /&gt;
    A[j]=...&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Párhuzamosítás===&lt;br /&gt;
Több féle mód létezik a párhuzamosításra. Korábban említettük a SIMD technikát, ami adatpárhuzamosításra való 1 processzoron belül, de létezik még processzorok közti párhuzamosítás, amit az OpenMP segítségével tudunk hatékonyan megvalósítani. Amennyiben gépek közti párhuzamosításra, üzenek küldésre van szükség, akkor az MPI-t kell segítségül hívni. Általános megoldás nem létezik minden feladatra, mert MPI is alkalmas lehet gépen belüli párhuzamosításra, nem csak az OpenMP. Amit mérlegelni kell, hogy mi az amire az alkalmazásnak szükséges van illetve mekkora kommunikációs overhead keletkezik, ha lecseréljük az OpenMP-t MPI-ra. Általában az egymástól független számításokat OpenMP-vel, míg a kommunikációt igénylő feladatokra MPI-t szoktak használni.&lt;br /&gt;
&lt;br /&gt;
====Szálak párhuzamosítása====&lt;br /&gt;
Az OpenMP-nek környezeti változók segítségével megadható a párhuzamosan futtatható szálak száma. Ezt a következő paranccsal tudjuk megtenni, ahol 5-re állítjuk.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export OMP_NUM_THREADS=5&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Forrásban a párhuzamos szálakat a ''#pragma omp parallel'' segítségével adhatjuk meg.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for              	//külső for szálak párhuzamosítása&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
#pragma simd				// vektorizáció a belső cikluson&lt;br /&gt;
  for (int j = 0; j &amp;lt; m; j++)&lt;br /&gt;
    My_Function(A[i][j]);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A fordítási direktívák össze is vonhatóak, ilyenkor 1 ciklus is elég.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for simd&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
    My_Function(A[i]);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=====Változók láthatósága, megosztása=====&lt;br /&gt;
Változók hatókörét a programozási nyelven kívül pragma-k segítségével is beállíthatjuk, hogy megosztott legyen a változó vagy a szálra nézve saját példány. Amennyiben nem definiáljuk felül, akkor minden változó megosztott. A felül definiálást a private és a shared paraméterekkel lehet megadni az OpenMP-nek.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int A, B, C;&lt;br /&gt;
&lt;br /&gt;
#pragma omp parallel private(A) shared(B)&lt;br /&gt;
{...}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Az itteni példában az B és C megosztott változó a szálak között, míg A-t minden szálnak lemásolja a futás kezdetén.&lt;br /&gt;
&lt;br /&gt;
====Párhuzamos szálak ütemezése====&lt;br /&gt;
OpenMp a szálak ütemezésére három féle módod kínál fel.&lt;br /&gt;
Módok:&lt;br /&gt;
* '''static''' (alapértelmezett): egyenletesen arányban egymás után elosztja a processzeket az indított szálakon, ezért a futási idő tetszőlegesen elhúzódhat, ha egyes processzek lassabban futnak le.&lt;br /&gt;
* '''dynamic''': minden esetben az első szabad szálra kerül a következő processz ütemezése, folyamatos kihasználtságot elérve, de &amp;quot;költséges&amp;quot; a sok-sok ütemezés&lt;br /&gt;
* '''guided''': Az előző két mód ötvözése. Nagyobb procesz számot ütemez az éppen szabad szálakra, melyek számát folyamatosan csökkenti a futás előre haladtával, hogy körülbelül egyszerre fejeződjön be minden szál futása, ezzel csökkentve a dynamic esetén magad ütemezési időráfordítást.&lt;br /&gt;
&lt;br /&gt;
Mindhárom esetben megadható az egyszerre ütemezett processzek száma, amit a ''chunk'' értéke határoz meg. Ha megvan adva a chunk értéke, akkor az minden esetben annak megfelelő processzámot ütemez a szálakra.&lt;br /&gt;
Használata:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel [schedule (&amp;lt;mode&amp;gt;,&amp;lt;chunk&amp;gt;) {...}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Szálak szinkronizációja====&lt;br /&gt;
Szála szinkronizálását többféle módon lehet megvalósítani, de ezen megoldások sajno jelentősen lelassítják a program futását.&lt;br /&gt;
* mutexxel: nagyon lassú, állandóan várnak egymásra a párhuzamos szálak&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++) {&lt;br /&gt;
#pragma omp critical&lt;br /&gt;
  { // kritikus szekció, csak egy szál hajthatja végre&lt;br /&gt;
    total = total + i;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* atomi művelet definiálása: sajnos még ez is lassú, de itt már nem olyan &amp;quot;erős&amp;quot; a szinkronizáció, ezért rendkívül sok limitációja van az itt használható atomi műveleteknek.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++) {&lt;br /&gt;
#pragma omp atomic&lt;br /&gt;
   total += i;&lt;br /&gt;
}	&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=====Párhuzamos redukció=====&lt;br /&gt;
Az itt bemutatott eljárás gyorsabb mint a fenti szinkronizációs megoldás, de sajnos nem alkalmazható vektorokra! A redukciót a ''reduction(operation: scalar)'' formában kell megadni.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int sum = 0;&lt;br /&gt;
#pragma omp parallel for reduction(+: sum)&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++) {&lt;br /&gt;
  sum = sum + i;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Ciklusok párhuzamosítása====&lt;br /&gt;
Ciklus párhuzamosítására a következő mintát szokás használni.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel&lt;br /&gt;
{&lt;br /&gt;
  // Az itt definiált forrást minden szál esetén végre fog hajtani&lt;br /&gt;
  ...&lt;br /&gt;
#pragma omp for [schedule (&amp;lt;mode&amp;gt;,&amp;lt;chunk&amp;gt;)&lt;br /&gt;
  for (int i = 0; i &amp;lt; n; i++) {&lt;br /&gt;
    // Ez a kódrész lesz párhuzamosítva&lt;br /&gt;
    ...&lt;br /&gt;
  }&lt;br /&gt;
  // ... és ez a köd is minden szál esetén végrehajtódik&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Ciklus kifejtés====&lt;br /&gt;
A technika már egy példa erejéig a vektorizációnál is látható, de ott nem került részletezésre. A megoldás lényege, hogy a kellően nagy ciklust szétdaraboljuk kisebb darabokra, amit már külön feldolgozhatóak, ezzel párhuzamosítva a folyamatokat. Az egy darab kiindulási ciklusból két darab egymásba ágyazott ciklust hozunk létre, ami ugyanazon a feladattéren dolgozik. &lt;br /&gt;
&lt;br /&gt;
Eredeti állapot:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++) {...}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Átírt változat:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int STRIP=1024;&lt;br /&gt;
for (int ii = 0; ii &amp;lt; n; ii += STRIP)&lt;br /&gt;
 for (int i = ii; i &amp;lt; ii+STRIP; i++) {...}&lt;br /&gt;
&lt;br /&gt;
// ha még van maradék elem, akkor azokon is végrehajtjuk a számításokat&lt;br /&gt;
if (n%STRIP != 0) {&lt;br /&gt;
  for (i=n-(n%STRIP)+1; i&amp;lt;n; i++) {...}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A darabok mérete (STRIP értéke) az adott rendszer és feladat függvényében változhat, tehát ez egyfajta &amp;quot;tuning&amp;quot; paraméter.&lt;br /&gt;
&lt;br /&gt;
====Automatikus ciklusösszevonás====&lt;br /&gt;
Az OpenMP támogatást nyújt több egymásba ágyazott ciklus &amp;quot;összevonására&amp;quot;. Ehhez csak a '''collaps''' fordítási direktívát és az összevonandó ciklusok számát kell megadni. &amp;lt;br /&amp;gt;&lt;br /&gt;
FONTOS: ekkor az automatikus vektorizáció kikapcsolásra kerül!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for collaps(2)&lt;br /&gt;
for (int i=0; i&amp;lt;n; i++)&lt;br /&gt;
  for( int j=0; j&amp;lt;m; j++){...}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A fneti példában a 2db for ciklis kerül párhuzamosításra úgy, hogy a fordítás során 1db ciklus generálódik belőle, ami a következő paraméterezéssel ekvivalens:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for&lt;br /&gt;
for (int c=0; c &amp;lt; n*m; c++) {&lt;br /&gt;
  int i = c/n;&lt;br /&gt;
  int j = c%n;&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Szigeti(AT)niif.hu</name></author>	</entry>

	<entry>
		<id>https://wiki.niif.hu/index.php?title=Intel_Xeon_Phi&amp;diff=3756</id>
		<title>Intel Xeon Phi</title>
		<link rel="alternate" type="text/html" href="https://wiki.niif.hu/index.php?title=Intel_Xeon_Phi&amp;diff=3756"/>
				<updated>2016-05-05T12:27:15Z</updated>
		
		<summary type="html">&lt;p&gt;Szigeti(AT)niif.hu: /* Szálak szinkronizációja */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Az oldalon található információk segítséget nyújtanak a HPC felhasználóinknak, hogy alkalmazásaikat miként portolhatják valamint optimalizálhatják a hatékonyabb futást szem előtt tartva Intel Xeon Phi koprocesszor kártyákon.&lt;br /&gt;
&lt;br /&gt;
==Hardver bemutatása==&lt;br /&gt;
Intel® Xeon Phi™ Coprocessor 7120P&lt;br /&gt;
&amp;lt;gallery&amp;gt;&lt;br /&gt;
Fájl:Xeon-phi-family-rwd.png.rendition.intel.web.720.405.png|&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-  align=&amp;quot;center&amp;quot;&lt;br /&gt;
! CPU&lt;br /&gt;
! threads/Core&lt;br /&gt;
!max threads&lt;br /&gt;
! CPU Freq&lt;br /&gt;
! Memória&lt;br /&gt;
! L1 cache&lt;br /&gt;
! L2 cache&lt;br /&gt;
! Interfész&lt;br /&gt;
! Ptot&lt;br /&gt;
! Számítási kapacitás&lt;br /&gt;
|-  align=&amp;quot;center&amp;quot;&lt;br /&gt;
| 61&lt;br /&gt;
| 4 Hardver&lt;br /&gt;
| 244&lt;br /&gt;
| 1238 GHz&lt;br /&gt;
| 16GB GDDR5&lt;br /&gt;
| 32KB/core&lt;br /&gt;
| 512KB/core&lt;br /&gt;
| PCIe&lt;br /&gt;
| 300W&lt;br /&gt;
| 1,2 TFlop/s dupla pontosság&lt;br /&gt;
|-  align=&amp;quot;center&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
| OpenMP-nél csak '''240'''&lt;br /&gt;
|&lt;br /&gt;
| ~100 cycles&lt;br /&gt;
~174 GB/s&lt;br /&gt;
| ~3 cycles&lt;br /&gt;
| ~10 cycles&lt;br /&gt;
| ~7 GB/s&lt;br /&gt;
~1 microsecond&lt;br /&gt;
|&lt;br /&gt;
| 2,4 TFlop/s egyszeres pontosság&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Az Intel® 1. generációs Xeon Phi™ koprocesszorában található 61 processzor ugyanazt a 16GB memóriát éri el, de semmilyen módon se fér hozzá közvetlenül a hoszt gép fizikai memóriájához.&amp;lt;br /&amp;gt;&lt;br /&gt;
[[Budapest2_klaszter|Budapest2]] és [[HPC#Szupersz.C3.A1m.C3.ADt.C3.B3g.C3.A9peink_.C3.B6sszehasonl.C3.ADt.C3.A1sa|Debrecen3-Phi]] gépeinkben számítási nodonként 2-2 Phi™ koprocesszor található. &amp;lt;br/&amp;gt;&lt;br /&gt;
A kártyákon speciális célú Linux kernel fut melyre a belépés limitáltan csak a rendszergazdák számára lehetséges SSH-n keresztül a hoszt gépről. Természetesen a kártyára belépve olyan érzésünk lehet, mintha egy sok processzoros gépen dolgoznánk, ami rendelkezik rengeteg memóriával. Ez így is van, de a hatékonyabb kihasználás érdekében az SSH-n keresztüli elérés nem lehetséges, csak a Slurm segítségével valamint a programozási nyelvek által támogatott csatornákon keresztül. Ennek részletezése lentebb megtalálható.&lt;br /&gt;
&lt;br /&gt;
==Használati módok==&lt;br /&gt;
A koprocesszor használatai módjait két csoportba lehet sorolni. A '''natív''' és az '''offload''' használatot.  &amp;lt;br/&amp;gt;&lt;br /&gt;
Ezen használati módokról a lenti fejezetekben több részletet is megtudhat. Az itt megfogalmazott &amp;quot;támogatás&amp;quot; csak a mi infrastruktúránkra érvényes! &amp;lt;br /&amp;gt;&lt;br /&gt;
A bemutatott példák ''c++'' nyelven vannak, de természetesen ''c'' és ''fortran'' esetén is megtalálható az ekvivalens módszer.&lt;br /&gt;
&lt;br /&gt;
===a) nem támogatott mód===&lt;br /&gt;
====közvetlen natív használat====&lt;br /&gt;
A natív mód arra szolgál, hogy a MIC kártyára  lefordított alkalmazásunkat felmásoljuk a kártyára, majd belépünk SSH-n a kártyára és azon futtatjuk a bináris kódunkat. &amp;lt;br /&amp;gt;&lt;br /&gt;
Ez a legegyszerűbb használati mód, ugyanis nincs más dolgunk, csak a '''-mmic''' kapcsolót használni a fordításnál és a fordító egy máris a MIC architektúrára kész bináris készít nekünk. &amp;lt;br/&amp;gt;&lt;br /&gt;
Workflow a következő lenne:&lt;br /&gt;
# Alkalmazás megírása. például [[Média:hello.cc|''hello.cc'']]&lt;br /&gt;
# Alkalmazás fordítása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;icpc -o hello-MIC -mmic hello.cc&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Másolás a MIC kártyára &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;scp hello-MIC mic0:&amp;lt;/pre&amp;gt;&lt;br /&gt;
# belépés a kártyára &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;ssh mic0&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Alkalmazás futtatása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;./hello-MIC&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===b) támogatott, de nem preferált használat===&lt;br /&gt;
====automatikus kódátvitel====&lt;br /&gt;
Ebben az esetben nem kell átmásolnunk a bináris alkalmazásunkat a kártyára sőt még csak be se kell lépnünk, mert ezt a két lépést egy beépített tool végzi el. &amp;lt;br/&amp;gt;&lt;br /&gt;
A tool eléréséhez szükséges parancs neve: '''micnativeloadex'''&lt;br /&gt;
&lt;br /&gt;
Ebben az esetben a workflow a következőképpen módosul:&lt;br /&gt;
# Alkalmazás megírása. például [[Média:hello.cc|''hello.cc'']]&lt;br /&gt;
# Alkalmazás fordítása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;icpc -o hello-MIC -mmic hello.cc&amp;lt;/pre&amp;gt;&lt;br /&gt;
# A tool futásához szükséges library-k megadása &amp;lt;br&amp;gt; &amp;lt;pre&amp;gt;export SINK_LD_LIBRARY_PATH=/opt/intel/composerxe/compiler/lib/mic&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Alkalmazás futtatása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;micnativeloadex hello-MIC&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| BUDAPEST2[cn10] phi (0)$ export SINK_LD_LIBRARY_PATH=/opt/intel/composerxe/compiler/lib/mic &amp;lt;br/&amp;gt;BUDAPEST2[cn10] phi (0)$ micnativeloadex hello-MIC &amp;lt;br/&amp;gt;Hello world! I have 244 logical cores.&amp;lt;br/&amp;gt;BUDAPEST2[cn10] phi (0)$&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===c) támogatott módok===&lt;br /&gt;
====Offload====&lt;br /&gt;
Az Offload modell esetén szükséges a forráskód módosítása, mégpedig úgy, hogy azon kódrészletet, amit a koprocesszoron szeretnénk futtatni, ki kell egészíteni egy fordítási direktívával. Az alkalmazás fordításakor elegendő csak a CPU-ra való binárist készíteni mivel a modell végzi a megfelelő forrás részlet mozgatását és futtatását.&amp;lt;br /&amp;gt;&lt;br /&gt;
[[Fájl:offload.png|Offload modell]] &amp;lt;br /&amp;gt;&lt;br /&gt;
Példánkban jól látható, hogy a második kiíratás már a koprocesszoron fut.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
int main(int argc, char * argv[]) {&lt;br /&gt;
  printf(&amp;quot;Hello world from host!\n&amp;quot;);&lt;br /&gt;
#pragma offload target(mic)&lt;br /&gt;
  {&lt;br /&gt;
    printf(&amp;quot;Hello world from coprocessor!\n&amp;quot;); fflush(stdout);&lt;br /&gt;
  }&lt;br /&gt;
  printf(&amp;quot;Bye\n&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ebben a példában az operációs rendszer választja ki, hogy melyik Phi kártyán fusson le a kódrészlet. Amennyiben mi szeretnénk explicite megmondani, hogy melyik kártyán fusson, akkor a fordítási direktívában a mic kulcsszó után kell tenni egy &amp;quot;:&amp;quot;-t és a kártya sorszámát [mi esetünkben 0 vagy 1]. Ekkor így nézne ki a direktíva: ''#pragma offload target(mic:1)''&lt;br /&gt;
&lt;br /&gt;
Amennyiben az offload szekcióban függvény hívás is szerepel, akkor szükségeltetik az függvény futásához szükséges változók adatterületének átmásolása a Phi kártyára, majd a lefutás után visszamásolni a hoszt gépre. Ezen másolást az ''offload_attribute'' ''push'' és ''pop'' paraméterei segítségével határozhatjuk meg. &amp;lt;br /&amp;gt;&lt;br /&gt;
A következő mátrix-vektor szorzás példánál az A mátrix globális változó, míg a b és c vektorok a függvény paraméterei.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//A változók felmásolásra kerülnek a Xeon Phi-re&lt;br /&gt;
#pragma offload_attribute(push, target(mic))&lt;br /&gt;
  double* A;&lt;br /&gt;
  void MatrixVector(double *b, double *c, double m, double n) {&lt;br /&gt;
    ...&lt;br /&gt;
    c[] = A[..]*b[]&lt;br /&gt;
  }&lt;br /&gt;
// felmásolt objektumok visszamásolása&lt;br /&gt;
#pragma offload_attribute(pop)&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
  ...&lt;br /&gt;
#pragma offload target(mic) in(A[0:n*m])&lt;br /&gt;
// így is meg lehetne adni: #pragma offload target(mic:0) in(A : length(n*m))	        &lt;br /&gt;
  {&lt;br /&gt;
    MatrixVector(b, c, m, n);&lt;br /&gt;
  }&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ekkor minden memória területet 2x kell másolni, egyszer fel a kártyára, majd másodszor vissza a hoszt gépre, pedig elegendő lenne csak az eredmény vektort visszamásolni. Ezen optimalizálással egy későbbi fejezetben foglalkozunk. &amp;lt;p /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Futás során kaphatunk részletes riportot is, hogy pontosan milyen adatmozgatások történnek az alkalmazás futása során. Ehhez nincs szükség másra csak az '''OFFLOAD_REPORT''' környezeti változó megfelelő beállítása. A beállított értékek 1, 2 vagy 3 lehetnek, attól függően mennyire részletes riportot szeretnénk kapni. &amp;lt;br /&amp;gt;&lt;br /&gt;
beállítás:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export OFFLOAD_REPORT=3&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
példa kimenet a hello alkalmazásra:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Hello world from host!&lt;br /&gt;
[Offload] [MIC 0] [File]                    hello_offload.cc&lt;br /&gt;
[Offload] [MIC 0] [Line]                    5&lt;br /&gt;
[Offload] [MIC 0] [Tag]                     Tag 0&lt;br /&gt;
Hello world from coprocessor!&lt;br /&gt;
[Offload] [HOST]  [Tag 0] [CPU Time]        0.554653(seconds)&lt;br /&gt;
[Offload] [MIC 0] [Tag 0] [CPU-&amp;gt;MIC Data]   0 (bytes)&lt;br /&gt;
[Offload] [MIC 0] [Tag 0] [MIC Time]        0.000140(seconds)&lt;br /&gt;
[Offload] [MIC 0] [Tag 0] [MIC-&amp;gt;CPU Data]   0 (bytes)&lt;br /&gt;
Bye&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Offload + OpenMP====&lt;br /&gt;
Amennyiben nem csak 1 processzort szeretnénk használni a Phi kártyánkon és az alkalmazásunkban használt számítás megengedi a párhuzamosítást, akkor az OpenMP segítségével párhuzamosíthatjuk a megfelelő számítást. &amp;lt;br /&amp;gt;&lt;br /&gt;
[[Fájl:Offload+openmp.png|Offload modell és OpenMP]] &amp;lt;br /&amp;gt;&lt;br /&gt;
Példánk maradjon ugyanúgy a mátrix-vektor szorzás. Amennyiben a teljes kártya adta kapacitást ki szeretnénk használni, akkor az OMP threadek számát 240-re kell beállítani. Azért nem 244-re, mivel 1 CPU-t dedikáltan csak a kommunikációra használ a rendszer és 1 CPU 4 hardver threaddel rendelkezik.&lt;br /&gt;
Ekkor a példánk így változik:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;omp.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#pragma offload_attribute(push, target(mic))&lt;br /&gt;
void multiply(int n, int m, double* A, double* b, double* c){&lt;br /&gt;
  const int nCPUs = omp_get_max_threads();   //240&lt;br /&gt;
  omp_set_num_threads(nCPUs);&lt;br /&gt;
#pragma omp parallel for schedule(dynamic, 1)&lt;br /&gt;
  for ( int i = 0 ; i &amp;lt; m ; i++)&lt;br /&gt;
     for ( int j = 0 ; j &amp;lt; n ; j++)&lt;br /&gt;
       c[i] += A[i*n+j] * b[j];&lt;br /&gt;
}&lt;br /&gt;
#pragma offload_attribute(pop)&lt;br /&gt;
&lt;br /&gt;
int main(){&lt;br /&gt;
  double * A = (double*) malloc(sizeof(double)*n*m);&lt;br /&gt;
  ...&lt;br /&gt;
#pragma offload target(mic) in(A:length(n*m))&lt;br /&gt;
     multiply(n, m, A, b, c);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Az alkalmazás fordításánál a fordítónak meg kell adni, hogy használhja az openmp library-ket is. Ezt a '''-qopenmp''' kapcsolóval tudjuk elérni.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
icpc -o matrix-CPU -qopenmp matrix.cc&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====MPI + Offload + OpenMP====&lt;br /&gt;
Abban az esetben, ha 1 számítási node nem elég, MPI segítségével össze tudunk kötni több nodet, heterogén rendszert alkotva, és azokon alkalmazható a fenti Offload + OpenMP modell. &amp;lt;br /&amp;gt;&lt;br /&gt;
[[Fájl:Mpi+offload+openmp.png|MPI elosztás valamint Offload modell és OpenMP]] &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Alkalmazások optimalizálása a kártyára==&lt;br /&gt;
Ebben a fejezetben optimalizálási lehetőségeket mutatunk be, melyek segítségül szolgálhatnak az egyes alkalmazások hatékonyabb futása érdekében ezzel jobban kihasználni a rendelkezésre álló erőforrások adta számítási kapacitás.&lt;br /&gt;
&lt;br /&gt;
===Környezeti változók===&lt;br /&gt;
Lehetséges csak a Phi kártyákon használt környezeti változók beállítása, amit az alkalmazásunk használni is tud. &amp;lt;br /&amp;gt;&lt;br /&gt;
Példánkban az OpenMP esetén használható szálak számát állíthatjuk be:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export MIC_ENV_PREFIX=XEONPHI&lt;br /&gt;
export OMP_NUM_THREADS=12&lt;br /&gt;
export XEONPHI_OMP_NUM_THREADS=240&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
A beállított prefix tetszőleges karaktersorozat lehet, a lényeg, hogy ezt a beállított prefixet használjuk a környezeti változók esetén is!&lt;br /&gt;
 &lt;br /&gt;
===Hosszredukció===&lt;br /&gt;
Többnyire a fordítók, ha nem mondjuk meg explicite, a dupla pontosságú változatot választják a lehetséges változó- és függvény típusok közül. Amennyiben a számításunk nem igényli a dupla pontosságot, akkor használjuk a forrásban mindenhol az adott típus vagy függvény egyszeres pontosságú változatát, mellyel jelentős teljesítmény növekedést tudunk elérni. Továbbá az egyes műveletek futási ideje nem egyforma, így a műveletet helyettesíthetjük más művelettel, ami gyorsabb, akkor ismét javíthatunk a hatékonyságon. &amp;lt;br /&amp;gt;&lt;br /&gt;
Íme pár példa, amit alkalmazni lehet:&lt;br /&gt;
* &amp;quot;''/''&amp;quot; művelet helyett &amp;quot;''*''&amp;quot; -t használjunk, mert azt gyorsabban végre tudja hajtani a processzor&lt;br /&gt;
* ''pow'' =&amp;gt; ''1.0 / sqrt()''    , tetszőleges hatványozás, pl.: 3/2-ik hatvány&lt;br /&gt;
* hardver áltatl támogatott műveletek:&lt;br /&gt;
** ''exp'' =&amp;gt; ''exp2''&lt;br /&gt;
** ''log'' =&amp;gt; ''log2''&lt;br /&gt;
* dupla pontosság -&amp;gt; egyszeres pontosság lebegőpontos számok és műveletek esetén &lt;br /&gt;
** ''1.0'' =&amp;gt; ''1.0f''&lt;br /&gt;
** ''pow'' =&amp;gt; ''powf''&lt;br /&gt;
&lt;br /&gt;
===Vektorizáció===&lt;br /&gt;
A vektorizáció azon fordítási időben eldönthető optimalizációs faktor, amely nagy mértékben függ attól a hardver felépítésétől, amire éppen a fordítás történni fog. Legfontosabb befolyásoló tényezők a processzor cache méretei illetve a vektor műveletekért felelős aritmetikai egységének jellemzői mivel ezeket használja fel a fordító az optimalizálási folyamata során. &amp;lt;br /&amp;gt;&lt;br /&gt;
Az automatikus vektorizáció minimum -O2 szinttől érvényes, de mivel általában ez a szint az alapértelmezett, ezért minden fordításnál alkalmazásra kerül. &amp;lt;br /&amp;gt;&lt;br /&gt;
A fordító programnak a következő kapcsolókat lehet megadni, melyek segítenek a minél jobb vektorizációban:&lt;br /&gt;
* ''-qopt-report=&amp;lt;szám&amp;gt;'' ; az optimalizációs jelentés részletességét lehet megadni 0-tól 5-ig, ahol az 5-s szint a legrészletesebbet jelenti&lt;br /&gt;
* ''-qopt-report-phase:vec'' ; a vektorizációról szeretnék jelentést kapni&lt;br /&gt;
* ''-opt-report-file=&amp;lt;fájlnév&amp;gt;'' ; megadható a riportfájl neve&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
icpc autovec.cc -mmic -qopt-report=5 -opt-report-file=vectorization.oMIC.optrpt -qopt-report-phase:vec&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====SIMD====&lt;br /&gt;
Az adatok hatékonyabb feldolgozására az egyik megvalósítás a SIMD (Single Instruction Multiple Data), amikor is a különálló adatokat úgy kezeli, mintha az vektorok volnának és azon végzi el a vektor-műveletet. Ezzel egy a hardver által támogatott műveletet végzünk egy lépésben egy nagyobb adatmennyiségen, ezzel párhuzamosítva a feldolgozási folyamatot. Ezzel a módszerrel nagyon jó adat szintű párhuzamosítást lehet elérni.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Fájl:Simd.png|SIMD]]&lt;br /&gt;
&lt;br /&gt;
Főbb támogatott műveletek:&lt;br /&gt;
* aritmetikai műveletek, bináris operátorok&lt;br /&gt;
* összehasonlítások&lt;br /&gt;
* konverziók és típus kasztolások&lt;br /&gt;
* bit-műveletek: NOT, AND, OR, XOR, XAND&lt;br /&gt;
&lt;br /&gt;
Használata: '''simd''' fordítási direktívával lehetséges &amp;lt;br /&amp;gt;&lt;br /&gt;
Például:&lt;br /&gt;
* egyszerű vektorizáció&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma simd&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
  A[i] += B[i];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* adat párhuzamosítás OpenMP segítségével&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for simd&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
  DoSomeWork(A[i]);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====AoS -&amp;gt; SoA====&lt;br /&gt;
A kiindulás probléma az, hogy a struktúrákat tartalmazó vektort nem lehet jól vektorizálni, mivel a cache-ben tárolt adatok nagy többsége feleslegesen van bent, mivel azon adatokat csak egy későbbi ciklus futás során fogja az alkalmazás feldolgozni. Tehát ezen adatok jelenleg feleslegesen foglalják a cache területet, ahelyett, hogy hasznos adatot tárolnánk ott.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
struct MyStructure {&lt;br /&gt;
  float x, y, z;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void My_Function(cons int nItem, MyStructure* const Items, const float delta) {&lt;br /&gt;
  for (int i = 0; i &amp;lt; nItem; i++) {&lt;br /&gt;
    ...&lt;br /&gt;
    for (int j = 0; j &amp;lt; nItem; j++) {&lt;br /&gt;
      const float dx = Items[j].x - Items[i].x;&lt;br /&gt;
      const float dy = Items[j].y - Items[i].y;&lt;br /&gt;
      const float dz = Items[j].z - Items[i].z;&lt;br /&gt;
      ...  &lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  ... &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A vektorban a struktúrák tárolása helyett minden elemnek egy dedikált vektort allokálunk és így könnyen tudja a fordító vektorizálni azt. Természetesen segítségként a vektor feldolgozását &amp;quot;tömbösítettük&amp;quot; a tileSize méret megadásával illetve a ''unroll'' fordítási direktíva segítségével.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
struct MyStructureSet {&lt;br /&gt;
  float *x, *y, *z;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void My_Function(cons int nItem, MyStructure* const Items, const float delta) {&lt;br /&gt;
  const int tileSize = 16;&lt;br /&gt;
#pragma omp parallel for schedule(guided)&lt;br /&gt;
  for (int ii = 0; ii &amp;lt; nItem; ii+= tileSize) {&lt;br /&gt;
    ...&lt;br /&gt;
#pragma unroll(tileSize)&lt;br /&gt;
    for (int j = 0; j &amp;lt; nItem; j++) {&lt;br /&gt;
#pragma unroll(tileSize)&lt;br /&gt;
      for (int i = ii; i &amp;lt; ii + tileSize; i++) {&lt;br /&gt;
        const float dx = Items.x[j] - Items.x[i];&lt;br /&gt;
        const float dy = Items.y[j] - Items.y[i];&lt;br /&gt;
        const float dz = Items.z[j] - Items.z[i];&lt;br /&gt;
        ...&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  ... &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Hatékony hossz választás====&lt;br /&gt;
Olyan esetben jelent előnyt, ha például az n*n-es mátrix esetén n nem osztható 16-val, mert akkor a vektorizáció nem teljesen hajtható végre. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// n%16 != 0&lt;br /&gt;
float* A = _mm_malloc(sizeof(float)*n*n, 64);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Ilyen esetben mindig marad egy kis vektor részlet, amire elemenként kell elvégezni a műveletet.&amp;lt;br /&amp;gt;&lt;br /&gt;
A megoldást az jelenti, hogy a memória allokálást kiterjesztjük és nagyobb memória területet foglalunk a változónak, így már alkalmazható lesz a vektorizáció, mivel a maradvány területen is el tudjuk végezni a műveletet egy lépésben.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int lda=n;&lt;br /&gt;
if (n % 16 != 0) lda += (16 - n%16); // most lda%16==0&lt;br /&gt;
float* A = _mm_malloc(sizeof(float)*n*lda, 64);&lt;br /&gt;
&lt;br /&gt;
// használat&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
  for (int j = 0; j &amp;lt; n; j++)&lt;br /&gt;
    A[i*lda + j] = ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====&amp;quot;Héj ciklusok&amp;quot; kiküszöbölése====&lt;br /&gt;
Ciklusok esetén lehetséges, hogy a vektorizációt nem teljes mértékben ismeri fel a fordító program. &lt;br /&gt;
Ekkor segíthetünk a fordítónak egy fordítási direktívával ('''#pragma vector aligned'''), ha teljes mértékben biztosak vagyunk, hogy a ciklusunk vektorizálható!&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma vector aligned&lt;br /&gt;
  for (int j = 0; j &amp;lt; n; j++)&lt;br /&gt;
    A[i*lda + j] -= ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Ettől jelentős futás idejű javulást érhetünk el, mivel ezzel a #pragma-val kikapcsoltuk a futás idejű ellenőrzést, de előfordulhatnak olyan esetek, főleg amikor nem jól mértük fel a ciklusunkat és nem sikerült a vektorizálás, hogy ''Segmentation fault'' hibára fogunk futni.&lt;br /&gt;
&lt;br /&gt;
====Regularizáció====&lt;br /&gt;
Van egy ciklusunk, ami egy vektor elemeit módosítja. A processzor az adatokat &amp;quot;csokorba szedve&amp;quot; emeli be a belső kis méretű cache-be. Egyszerre természetesen több vektor elem is bekerül a a cache-be ezzel gyorsítva a feldolgozást. Ezen vektor iterációk valahogyan ráilleszkedik a vektorra.&lt;br /&gt;
&lt;br /&gt;
[[Fájl:regularization.png|Vektor regularizáció]]&lt;br /&gt;
&lt;br /&gt;
Abban az esetben, amikor ez az illesztés nem teljesen tökéletes (lsd. piros elemek), keletkeznem héj ciklusok, melyeket a regularizációval is eliminálni lehet. &amp;lt;br /&amp;gt;&lt;br /&gt;
A feladat eredetileg valahogyan így nézhet ki:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
for (int i = 0; i&amp;lt;n; i++)&lt;br /&gt;
  A[i]=...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ez a következő átalakítással már alkalmas vektorizációra és nem lesz &amp;quot;peel loop&amp;quot; probléma.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
for (int b=0; b&amp;lt;n; b++) {&lt;br /&gt;
  const int jStart = b- b%16; // mindig osztható 16-val&lt;br /&gt;
  ...&lt;br /&gt;
#pragma simd&lt;br /&gt;
  for (int j = jStart; j&amp;lt;n; j++)&lt;br /&gt;
    A[j]=...&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Párhuzamosítás===&lt;br /&gt;
Több féle mód létezik a párhuzamosításra. Korábban említettük a SIMD technikát, ami adatpárhuzamosításra való 1 processzoron belül, de létezik még processzorok közti párhuzamosítás, amit az OpenMP segítségével tudunk hatékonyan megvalósítani. Amennyiben gépek közti párhuzamosításra, üzenek küldésre van szükség, akkor az MPI-t kell segítségül hívni. Általános megoldás nem létezik minden feladatra, mert MPI is alkalmas lehet gépen belüli párhuzamosításra, nem csak az OpenMP. Amit mérlegelni kell, hogy mi az amire az alkalmazásnak szükséges van illetve mekkora kommunikációs overhead keletkezik, ha lecseréljük az OpenMP-t MPI-ra. Általában az egymástól független számításokat OpenMP-vel, míg a kommunikációt igénylő feladatokra MPI-t szoktak használni.&lt;br /&gt;
&lt;br /&gt;
====Szálak párhuzamosítása====&lt;br /&gt;
Az OpenMP-nek környezeti változók segítségével megadható a párhuzamosan futtatható szálak száma. Ezt a következő paranccsal tudjuk megtenni, ahol 5-re állítjuk.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export OMP_NUM_THREADS=5&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Forrásban a párhuzamos szálakat a ''#pragma omp parallel'' segítségével adhatjuk meg.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for              	//külső for szálak párhuzamosítása&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
#pragma simd				// vektorizáció a belső cikluson&lt;br /&gt;
  for (int j = 0; j &amp;lt; m; j++)&lt;br /&gt;
    My_Function(A[i][j]);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A fordítási direktívák össze is vonhatóak, ilyenkor 1 ciklus is elég.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for simd&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
    My_Function(A[i]);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=====Változók láthatósága, megosztása=====&lt;br /&gt;
Változók hatókörét a programozási nyelven kívül pragma-k segítségével is beállíthatjuk, hogy megosztott legyen a változó vagy a szálra nézve saját példány. Amennyiben nem definiáljuk felül, akkor minden változó megosztott. A felül definiálást a private és a shared paraméterekkel lehet megadni az OpenMP-nek.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int A, B, C;&lt;br /&gt;
&lt;br /&gt;
#pragma omp parallel private(A) shared(B)&lt;br /&gt;
{...}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Az itteni példában az B és C megosztott változó a szálak között, míg A-t minden szálnak lemásolja a futás kezdetén.&lt;br /&gt;
&lt;br /&gt;
====Párhuzamos szálak ütemezése====&lt;br /&gt;
OpenMp a szálak ütemezésére három féle módod kínál fel.&lt;br /&gt;
Módok:&lt;br /&gt;
* '''static''' (alapértelmezett): egyenletesen arányban egymás után elosztja a processzeket az indított szálakon, ezért a futási idő tetszőlegesen elhúzódhat, ha egyes processzek lassabban futnak le.&lt;br /&gt;
* '''dynamic''': minden esetben az első szabad szálra kerül a következő processz ütemezése, folyamatos kihasználtságot elérve, de &amp;quot;költséges&amp;quot; a sok-sok ütemezés&lt;br /&gt;
* '''guided''': Az előző két mód ötvözése. Nagyobb procesz számot ütemez az éppen szabad szálakra, melyek számát folyamatosan csökkenti a futás előre haladtával, hogy körülbelül egyszerre fejeződjön be minden szál futása, ezzel csökkentve a dynamic esetén magad ütemezési időráfordítást.&lt;br /&gt;
&lt;br /&gt;
Mindhárom esetben megadható az egyszerre ütemezett processzek száma, amit a ''chunk'' értéke határoz meg. Ha megvan adva a chunk értéke, akkor az minden esetben annak megfelelő processzámot ütemez a szálakra.&lt;br /&gt;
Használata:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel [schedule (&amp;lt;mode&amp;gt;,&amp;lt;chunk&amp;gt;) {...}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Szálak szinkronizációja====&lt;br /&gt;
Szála szinkronizálását többféle módon lehet megvalósítani, de ezen megoldások sajno jelentősen lelassítják a program futását.&lt;br /&gt;
* mutexxel: nagyon lassú, állandóan várnak egymásra a párhuzamos szálak&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++) {&lt;br /&gt;
#pragma omp critical&lt;br /&gt;
  { // kritikus szekció, csak egy szál hajthatja végre&lt;br /&gt;
    total = total + i;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* atomi művelet definiálása: sajnos még ez is lassú, de itt már nem olyan &amp;quot;erős&amp;quot; a szinkronizáció, ezért rendkívül sok limitációja van az itt használható atomi műveleteknek.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++) {&lt;br /&gt;
#pragma omp atomic&lt;br /&gt;
   total += i;&lt;br /&gt;
}	&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=====Párhuzamos redukció=====&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
	     (it is fast)  BUT not for vector		int total = 0;		#pragma omp parallel for reduction(+: total)               //usage: reduction(operation: scalar)		for (int i = 0; i &amp;lt; n; i++) {		  total = total + i;		}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Ciklusok párhuzamosítása====&lt;br /&gt;
Ciklus párhuzamosítására a következő mintát szokás használni.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel&lt;br /&gt;
{&lt;br /&gt;
  // Az itt definiált forrást minden szál esetén végre fog hajtani&lt;br /&gt;
  ...&lt;br /&gt;
#pragma omp for [schedule (&amp;lt;mode&amp;gt;,&amp;lt;chunk&amp;gt;)&lt;br /&gt;
  for (int i = 0; i &amp;lt; n; i++) {&lt;br /&gt;
    // Ez a kódrész lesz párhuzamosítva&lt;br /&gt;
    ...&lt;br /&gt;
  }&lt;br /&gt;
  // ... és ez a köd is minden szál esetén végrehajtódik&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Ciklus kifejtés====&lt;br /&gt;
A technika már egy példa erejéig a vektorizációnál is látható, de ott nem került részletezésre. A megoldás lényege, hogy a kellően nagy ciklust szétdaraboljuk kisebb darabokra, amit már külön feldolgozhatóak, ezzel párhuzamosítva a folyamatokat. Az egy darab kiindulási ciklusból két darab egymásba ágyazott ciklust hozunk létre, ami ugyanazon a feladattéren dolgozik. &lt;br /&gt;
&lt;br /&gt;
Eredeti állapot:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++) {...}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Átírt változat:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int STRIP=1024;&lt;br /&gt;
for (int ii = 0; ii &amp;lt; n; ii += STRIP)&lt;br /&gt;
 for (int i = ii; i &amp;lt; ii+STRIP; i++) {...}&lt;br /&gt;
&lt;br /&gt;
// ha még van maradék elem, akkor azokon is végrehajtjuk a számításokat&lt;br /&gt;
if (n%STRIP != 0) {&lt;br /&gt;
  for (i=n-(n%STRIP)+1; i&amp;lt;n; i++) {...}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A darabok mérete (STRIP értéke) az adott rendszer és feladat függvényében változhat, tehát ez egyfajta &amp;quot;tuning&amp;quot; paraméter.&lt;br /&gt;
&lt;br /&gt;
====Automatikus ciklusösszevonás====&lt;br /&gt;
Az OpenMP támogatást nyújt több egymásba ágyazott ciklus &amp;quot;összevonására&amp;quot;. Ehhez csak a '''collaps''' fordítási direktívát és az összevonandó ciklusok számát kell megadni. &amp;lt;br /&amp;gt;&lt;br /&gt;
FONTOS: ekkor az automatikus vektorizáció kikapcsolásra kerül!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for collaps(2)&lt;br /&gt;
for (int i=0; i&amp;lt;n; i++)&lt;br /&gt;
  for( int j=0; j&amp;lt;m; j++){...}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A fneti példában a 2db for ciklis kerül párhuzamosításra úgy, hogy a fordítás során 1db ciklus generálódik belőle, ami a következő paraméterezéssel ekvivalens:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for&lt;br /&gt;
for (int c=0; c &amp;lt; n*m; c++) {&lt;br /&gt;
  int i = c/n;&lt;br /&gt;
  int j = c%n;&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Szigeti(AT)niif.hu</name></author>	</entry>

	<entry>
		<id>https://wiki.niif.hu/index.php?title=Intel_Xeon_Phi&amp;diff=3755</id>
		<title>Intel Xeon Phi</title>
		<link rel="alternate" type="text/html" href="https://wiki.niif.hu/index.php?title=Intel_Xeon_Phi&amp;diff=3755"/>
				<updated>2016-05-05T12:19:21Z</updated>
		
		<summary type="html">&lt;p&gt;Szigeti(AT)niif.hu: /* Párhuzamos szálak ütemezése */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Az oldalon található információk segítséget nyújtanak a HPC felhasználóinknak, hogy alkalmazásaikat miként portolhatják valamint optimalizálhatják a hatékonyabb futást szem előtt tartva Intel Xeon Phi koprocesszor kártyákon.&lt;br /&gt;
&lt;br /&gt;
==Hardver bemutatása==&lt;br /&gt;
Intel® Xeon Phi™ Coprocessor 7120P&lt;br /&gt;
&amp;lt;gallery&amp;gt;&lt;br /&gt;
Fájl:Xeon-phi-family-rwd.png.rendition.intel.web.720.405.png|&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-  align=&amp;quot;center&amp;quot;&lt;br /&gt;
! CPU&lt;br /&gt;
! threads/Core&lt;br /&gt;
!max threads&lt;br /&gt;
! CPU Freq&lt;br /&gt;
! Memória&lt;br /&gt;
! L1 cache&lt;br /&gt;
! L2 cache&lt;br /&gt;
! Interfész&lt;br /&gt;
! Ptot&lt;br /&gt;
! Számítási kapacitás&lt;br /&gt;
|-  align=&amp;quot;center&amp;quot;&lt;br /&gt;
| 61&lt;br /&gt;
| 4 Hardver&lt;br /&gt;
| 244&lt;br /&gt;
| 1238 GHz&lt;br /&gt;
| 16GB GDDR5&lt;br /&gt;
| 32KB/core&lt;br /&gt;
| 512KB/core&lt;br /&gt;
| PCIe&lt;br /&gt;
| 300W&lt;br /&gt;
| 1,2 TFlop/s dupla pontosság&lt;br /&gt;
|-  align=&amp;quot;center&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
| OpenMP-nél csak '''240'''&lt;br /&gt;
|&lt;br /&gt;
| ~100 cycles&lt;br /&gt;
~174 GB/s&lt;br /&gt;
| ~3 cycles&lt;br /&gt;
| ~10 cycles&lt;br /&gt;
| ~7 GB/s&lt;br /&gt;
~1 microsecond&lt;br /&gt;
|&lt;br /&gt;
| 2,4 TFlop/s egyszeres pontosság&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Az Intel® 1. generációs Xeon Phi™ koprocesszorában található 61 processzor ugyanazt a 16GB memóriát éri el, de semmilyen módon se fér hozzá közvetlenül a hoszt gép fizikai memóriájához.&amp;lt;br /&amp;gt;&lt;br /&gt;
[[Budapest2_klaszter|Budapest2]] és [[HPC#Szupersz.C3.A1m.C3.ADt.C3.B3g.C3.A9peink_.C3.B6sszehasonl.C3.ADt.C3.A1sa|Debrecen3-Phi]] gépeinkben számítási nodonként 2-2 Phi™ koprocesszor található. &amp;lt;br/&amp;gt;&lt;br /&gt;
A kártyákon speciális célú Linux kernel fut melyre a belépés limitáltan csak a rendszergazdák számára lehetséges SSH-n keresztül a hoszt gépről. Természetesen a kártyára belépve olyan érzésünk lehet, mintha egy sok processzoros gépen dolgoznánk, ami rendelkezik rengeteg memóriával. Ez így is van, de a hatékonyabb kihasználás érdekében az SSH-n keresztüli elérés nem lehetséges, csak a Slurm segítségével valamint a programozási nyelvek által támogatott csatornákon keresztül. Ennek részletezése lentebb megtalálható.&lt;br /&gt;
&lt;br /&gt;
==Használati módok==&lt;br /&gt;
A koprocesszor használatai módjait két csoportba lehet sorolni. A '''natív''' és az '''offload''' használatot.  &amp;lt;br/&amp;gt;&lt;br /&gt;
Ezen használati módokról a lenti fejezetekben több részletet is megtudhat. Az itt megfogalmazott &amp;quot;támogatás&amp;quot; csak a mi infrastruktúránkra érvényes! &amp;lt;br /&amp;gt;&lt;br /&gt;
A bemutatott példák ''c++'' nyelven vannak, de természetesen ''c'' és ''fortran'' esetén is megtalálható az ekvivalens módszer.&lt;br /&gt;
&lt;br /&gt;
===a) nem támogatott mód===&lt;br /&gt;
====közvetlen natív használat====&lt;br /&gt;
A natív mód arra szolgál, hogy a MIC kártyára  lefordított alkalmazásunkat felmásoljuk a kártyára, majd belépünk SSH-n a kártyára és azon futtatjuk a bináris kódunkat. &amp;lt;br /&amp;gt;&lt;br /&gt;
Ez a legegyszerűbb használati mód, ugyanis nincs más dolgunk, csak a '''-mmic''' kapcsolót használni a fordításnál és a fordító egy máris a MIC architektúrára kész bináris készít nekünk. &amp;lt;br/&amp;gt;&lt;br /&gt;
Workflow a következő lenne:&lt;br /&gt;
# Alkalmazás megírása. például [[Média:hello.cc|''hello.cc'']]&lt;br /&gt;
# Alkalmazás fordítása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;icpc -o hello-MIC -mmic hello.cc&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Másolás a MIC kártyára &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;scp hello-MIC mic0:&amp;lt;/pre&amp;gt;&lt;br /&gt;
# belépés a kártyára &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;ssh mic0&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Alkalmazás futtatása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;./hello-MIC&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===b) támogatott, de nem preferált használat===&lt;br /&gt;
====automatikus kódátvitel====&lt;br /&gt;
Ebben az esetben nem kell átmásolnunk a bináris alkalmazásunkat a kártyára sőt még csak be se kell lépnünk, mert ezt a két lépést egy beépített tool végzi el. &amp;lt;br/&amp;gt;&lt;br /&gt;
A tool eléréséhez szükséges parancs neve: '''micnativeloadex'''&lt;br /&gt;
&lt;br /&gt;
Ebben az esetben a workflow a következőképpen módosul:&lt;br /&gt;
# Alkalmazás megírása. például [[Média:hello.cc|''hello.cc'']]&lt;br /&gt;
# Alkalmazás fordítása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;icpc -o hello-MIC -mmic hello.cc&amp;lt;/pre&amp;gt;&lt;br /&gt;
# A tool futásához szükséges library-k megadása &amp;lt;br&amp;gt; &amp;lt;pre&amp;gt;export SINK_LD_LIBRARY_PATH=/opt/intel/composerxe/compiler/lib/mic&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Alkalmazás futtatása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;micnativeloadex hello-MIC&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| BUDAPEST2[cn10] phi (0)$ export SINK_LD_LIBRARY_PATH=/opt/intel/composerxe/compiler/lib/mic &amp;lt;br/&amp;gt;BUDAPEST2[cn10] phi (0)$ micnativeloadex hello-MIC &amp;lt;br/&amp;gt;Hello world! I have 244 logical cores.&amp;lt;br/&amp;gt;BUDAPEST2[cn10] phi (0)$&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===c) támogatott módok===&lt;br /&gt;
====Offload====&lt;br /&gt;
Az Offload modell esetén szükséges a forráskód módosítása, mégpedig úgy, hogy azon kódrészletet, amit a koprocesszoron szeretnénk futtatni, ki kell egészíteni egy fordítási direktívával. Az alkalmazás fordításakor elegendő csak a CPU-ra való binárist készíteni mivel a modell végzi a megfelelő forrás részlet mozgatását és futtatását.&amp;lt;br /&amp;gt;&lt;br /&gt;
[[Fájl:offload.png|Offload modell]] &amp;lt;br /&amp;gt;&lt;br /&gt;
Példánkban jól látható, hogy a második kiíratás már a koprocesszoron fut.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
int main(int argc, char * argv[]) {&lt;br /&gt;
  printf(&amp;quot;Hello world from host!\n&amp;quot;);&lt;br /&gt;
#pragma offload target(mic)&lt;br /&gt;
  {&lt;br /&gt;
    printf(&amp;quot;Hello world from coprocessor!\n&amp;quot;); fflush(stdout);&lt;br /&gt;
  }&lt;br /&gt;
  printf(&amp;quot;Bye\n&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ebben a példában az operációs rendszer választja ki, hogy melyik Phi kártyán fusson le a kódrészlet. Amennyiben mi szeretnénk explicite megmondani, hogy melyik kártyán fusson, akkor a fordítási direktívában a mic kulcsszó után kell tenni egy &amp;quot;:&amp;quot;-t és a kártya sorszámát [mi esetünkben 0 vagy 1]. Ekkor így nézne ki a direktíva: ''#pragma offload target(mic:1)''&lt;br /&gt;
&lt;br /&gt;
Amennyiben az offload szekcióban függvény hívás is szerepel, akkor szükségeltetik az függvény futásához szükséges változók adatterületének átmásolása a Phi kártyára, majd a lefutás után visszamásolni a hoszt gépre. Ezen másolást az ''offload_attribute'' ''push'' és ''pop'' paraméterei segítségével határozhatjuk meg. &amp;lt;br /&amp;gt;&lt;br /&gt;
A következő mátrix-vektor szorzás példánál az A mátrix globális változó, míg a b és c vektorok a függvény paraméterei.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//A változók felmásolásra kerülnek a Xeon Phi-re&lt;br /&gt;
#pragma offload_attribute(push, target(mic))&lt;br /&gt;
  double* A;&lt;br /&gt;
  void MatrixVector(double *b, double *c, double m, double n) {&lt;br /&gt;
    ...&lt;br /&gt;
    c[] = A[..]*b[]&lt;br /&gt;
  }&lt;br /&gt;
// felmásolt objektumok visszamásolása&lt;br /&gt;
#pragma offload_attribute(pop)&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
  ...&lt;br /&gt;
#pragma offload target(mic) in(A[0:n*m])&lt;br /&gt;
// így is meg lehetne adni: #pragma offload target(mic:0) in(A : length(n*m))	        &lt;br /&gt;
  {&lt;br /&gt;
    MatrixVector(b, c, m, n);&lt;br /&gt;
  }&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ekkor minden memória területet 2x kell másolni, egyszer fel a kártyára, majd másodszor vissza a hoszt gépre, pedig elegendő lenne csak az eredmény vektort visszamásolni. Ezen optimalizálással egy későbbi fejezetben foglalkozunk. &amp;lt;p /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Futás során kaphatunk részletes riportot is, hogy pontosan milyen adatmozgatások történnek az alkalmazás futása során. Ehhez nincs szükség másra csak az '''OFFLOAD_REPORT''' környezeti változó megfelelő beállítása. A beállított értékek 1, 2 vagy 3 lehetnek, attól függően mennyire részletes riportot szeretnénk kapni. &amp;lt;br /&amp;gt;&lt;br /&gt;
beállítás:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export OFFLOAD_REPORT=3&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
példa kimenet a hello alkalmazásra:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Hello world from host!&lt;br /&gt;
[Offload] [MIC 0] [File]                    hello_offload.cc&lt;br /&gt;
[Offload] [MIC 0] [Line]                    5&lt;br /&gt;
[Offload] [MIC 0] [Tag]                     Tag 0&lt;br /&gt;
Hello world from coprocessor!&lt;br /&gt;
[Offload] [HOST]  [Tag 0] [CPU Time]        0.554653(seconds)&lt;br /&gt;
[Offload] [MIC 0] [Tag 0] [CPU-&amp;gt;MIC Data]   0 (bytes)&lt;br /&gt;
[Offload] [MIC 0] [Tag 0] [MIC Time]        0.000140(seconds)&lt;br /&gt;
[Offload] [MIC 0] [Tag 0] [MIC-&amp;gt;CPU Data]   0 (bytes)&lt;br /&gt;
Bye&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Offload + OpenMP====&lt;br /&gt;
Amennyiben nem csak 1 processzort szeretnénk használni a Phi kártyánkon és az alkalmazásunkban használt számítás megengedi a párhuzamosítást, akkor az OpenMP segítségével párhuzamosíthatjuk a megfelelő számítást. &amp;lt;br /&amp;gt;&lt;br /&gt;
[[Fájl:Offload+openmp.png|Offload modell és OpenMP]] &amp;lt;br /&amp;gt;&lt;br /&gt;
Példánk maradjon ugyanúgy a mátrix-vektor szorzás. Amennyiben a teljes kártya adta kapacitást ki szeretnénk használni, akkor az OMP threadek számát 240-re kell beállítani. Azért nem 244-re, mivel 1 CPU-t dedikáltan csak a kommunikációra használ a rendszer és 1 CPU 4 hardver threaddel rendelkezik.&lt;br /&gt;
Ekkor a példánk így változik:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;omp.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#pragma offload_attribute(push, target(mic))&lt;br /&gt;
void multiply(int n, int m, double* A, double* b, double* c){&lt;br /&gt;
  const int nCPUs = omp_get_max_threads();   //240&lt;br /&gt;
  omp_set_num_threads(nCPUs);&lt;br /&gt;
#pragma omp parallel for schedule(dynamic, 1)&lt;br /&gt;
  for ( int i = 0 ; i &amp;lt; m ; i++)&lt;br /&gt;
     for ( int j = 0 ; j &amp;lt; n ; j++)&lt;br /&gt;
       c[i] += A[i*n+j] * b[j];&lt;br /&gt;
}&lt;br /&gt;
#pragma offload_attribute(pop)&lt;br /&gt;
&lt;br /&gt;
int main(){&lt;br /&gt;
  double * A = (double*) malloc(sizeof(double)*n*m);&lt;br /&gt;
  ...&lt;br /&gt;
#pragma offload target(mic) in(A:length(n*m))&lt;br /&gt;
     multiply(n, m, A, b, c);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Az alkalmazás fordításánál a fordítónak meg kell adni, hogy használhja az openmp library-ket is. Ezt a '''-qopenmp''' kapcsolóval tudjuk elérni.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
icpc -o matrix-CPU -qopenmp matrix.cc&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====MPI + Offload + OpenMP====&lt;br /&gt;
Abban az esetben, ha 1 számítási node nem elég, MPI segítségével össze tudunk kötni több nodet, heterogén rendszert alkotva, és azokon alkalmazható a fenti Offload + OpenMP modell. &amp;lt;br /&amp;gt;&lt;br /&gt;
[[Fájl:Mpi+offload+openmp.png|MPI elosztás valamint Offload modell és OpenMP]] &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Alkalmazások optimalizálása a kártyára==&lt;br /&gt;
Ebben a fejezetben optimalizálási lehetőségeket mutatunk be, melyek segítségül szolgálhatnak az egyes alkalmazások hatékonyabb futása érdekében ezzel jobban kihasználni a rendelkezésre álló erőforrások adta számítási kapacitás.&lt;br /&gt;
&lt;br /&gt;
===Környezeti változók===&lt;br /&gt;
Lehetséges csak a Phi kártyákon használt környezeti változók beállítása, amit az alkalmazásunk használni is tud. &amp;lt;br /&amp;gt;&lt;br /&gt;
Példánkban az OpenMP esetén használható szálak számát állíthatjuk be:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export MIC_ENV_PREFIX=XEONPHI&lt;br /&gt;
export OMP_NUM_THREADS=12&lt;br /&gt;
export XEONPHI_OMP_NUM_THREADS=240&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
A beállított prefix tetszőleges karaktersorozat lehet, a lényeg, hogy ezt a beállított prefixet használjuk a környezeti változók esetén is!&lt;br /&gt;
 &lt;br /&gt;
===Hosszredukció===&lt;br /&gt;
Többnyire a fordítók, ha nem mondjuk meg explicite, a dupla pontosságú változatot választják a lehetséges változó- és függvény típusok közül. Amennyiben a számításunk nem igényli a dupla pontosságot, akkor használjuk a forrásban mindenhol az adott típus vagy függvény egyszeres pontosságú változatát, mellyel jelentős teljesítmény növekedést tudunk elérni. Továbbá az egyes műveletek futási ideje nem egyforma, így a műveletet helyettesíthetjük más művelettel, ami gyorsabb, akkor ismét javíthatunk a hatékonyságon. &amp;lt;br /&amp;gt;&lt;br /&gt;
Íme pár példa, amit alkalmazni lehet:&lt;br /&gt;
* &amp;quot;''/''&amp;quot; művelet helyett &amp;quot;''*''&amp;quot; -t használjunk, mert azt gyorsabban végre tudja hajtani a processzor&lt;br /&gt;
* ''pow'' =&amp;gt; ''1.0 / sqrt()''    , tetszőleges hatványozás, pl.: 3/2-ik hatvány&lt;br /&gt;
* hardver áltatl támogatott műveletek:&lt;br /&gt;
** ''exp'' =&amp;gt; ''exp2''&lt;br /&gt;
** ''log'' =&amp;gt; ''log2''&lt;br /&gt;
* dupla pontosság -&amp;gt; egyszeres pontosság lebegőpontos számok és műveletek esetén &lt;br /&gt;
** ''1.0'' =&amp;gt; ''1.0f''&lt;br /&gt;
** ''pow'' =&amp;gt; ''powf''&lt;br /&gt;
&lt;br /&gt;
===Vektorizáció===&lt;br /&gt;
A vektorizáció azon fordítási időben eldönthető optimalizációs faktor, amely nagy mértékben függ attól a hardver felépítésétől, amire éppen a fordítás történni fog. Legfontosabb befolyásoló tényezők a processzor cache méretei illetve a vektor műveletekért felelős aritmetikai egységének jellemzői mivel ezeket használja fel a fordító az optimalizálási folyamata során. &amp;lt;br /&amp;gt;&lt;br /&gt;
Az automatikus vektorizáció minimum -O2 szinttől érvényes, de mivel általában ez a szint az alapértelmezett, ezért minden fordításnál alkalmazásra kerül. &amp;lt;br /&amp;gt;&lt;br /&gt;
A fordító programnak a következő kapcsolókat lehet megadni, melyek segítenek a minél jobb vektorizációban:&lt;br /&gt;
* ''-qopt-report=&amp;lt;szám&amp;gt;'' ; az optimalizációs jelentés részletességét lehet megadni 0-tól 5-ig, ahol az 5-s szint a legrészletesebbet jelenti&lt;br /&gt;
* ''-qopt-report-phase:vec'' ; a vektorizációról szeretnék jelentést kapni&lt;br /&gt;
* ''-opt-report-file=&amp;lt;fájlnév&amp;gt;'' ; megadható a riportfájl neve&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
icpc autovec.cc -mmic -qopt-report=5 -opt-report-file=vectorization.oMIC.optrpt -qopt-report-phase:vec&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====SIMD====&lt;br /&gt;
Az adatok hatékonyabb feldolgozására az egyik megvalósítás a SIMD (Single Instruction Multiple Data), amikor is a különálló adatokat úgy kezeli, mintha az vektorok volnának és azon végzi el a vektor-műveletet. Ezzel egy a hardver által támogatott műveletet végzünk egy lépésben egy nagyobb adatmennyiségen, ezzel párhuzamosítva a feldolgozási folyamatot. Ezzel a módszerrel nagyon jó adat szintű párhuzamosítást lehet elérni.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Fájl:Simd.png|SIMD]]&lt;br /&gt;
&lt;br /&gt;
Főbb támogatott műveletek:&lt;br /&gt;
* aritmetikai műveletek, bináris operátorok&lt;br /&gt;
* összehasonlítások&lt;br /&gt;
* konverziók és típus kasztolások&lt;br /&gt;
* bit-műveletek: NOT, AND, OR, XOR, XAND&lt;br /&gt;
&lt;br /&gt;
Használata: '''simd''' fordítási direktívával lehetséges &amp;lt;br /&amp;gt;&lt;br /&gt;
Például:&lt;br /&gt;
* egyszerű vektorizáció&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma simd&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
  A[i] += B[i];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* adat párhuzamosítás OpenMP segítségével&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for simd&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
  DoSomeWork(A[i]);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====AoS -&amp;gt; SoA====&lt;br /&gt;
A kiindulás probléma az, hogy a struktúrákat tartalmazó vektort nem lehet jól vektorizálni, mivel a cache-ben tárolt adatok nagy többsége feleslegesen van bent, mivel azon adatokat csak egy későbbi ciklus futás során fogja az alkalmazás feldolgozni. Tehát ezen adatok jelenleg feleslegesen foglalják a cache területet, ahelyett, hogy hasznos adatot tárolnánk ott.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
struct MyStructure {&lt;br /&gt;
  float x, y, z;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void My_Function(cons int nItem, MyStructure* const Items, const float delta) {&lt;br /&gt;
  for (int i = 0; i &amp;lt; nItem; i++) {&lt;br /&gt;
    ...&lt;br /&gt;
    for (int j = 0; j &amp;lt; nItem; j++) {&lt;br /&gt;
      const float dx = Items[j].x - Items[i].x;&lt;br /&gt;
      const float dy = Items[j].y - Items[i].y;&lt;br /&gt;
      const float dz = Items[j].z - Items[i].z;&lt;br /&gt;
      ...  &lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  ... &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A vektorban a struktúrák tárolása helyett minden elemnek egy dedikált vektort allokálunk és így könnyen tudja a fordító vektorizálni azt. Természetesen segítségként a vektor feldolgozását &amp;quot;tömbösítettük&amp;quot; a tileSize méret megadásával illetve a ''unroll'' fordítási direktíva segítségével.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
struct MyStructureSet {&lt;br /&gt;
  float *x, *y, *z;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void My_Function(cons int nItem, MyStructure* const Items, const float delta) {&lt;br /&gt;
  const int tileSize = 16;&lt;br /&gt;
#pragma omp parallel for schedule(guided)&lt;br /&gt;
  for (int ii = 0; ii &amp;lt; nItem; ii+= tileSize) {&lt;br /&gt;
    ...&lt;br /&gt;
#pragma unroll(tileSize)&lt;br /&gt;
    for (int j = 0; j &amp;lt; nItem; j++) {&lt;br /&gt;
#pragma unroll(tileSize)&lt;br /&gt;
      for (int i = ii; i &amp;lt; ii + tileSize; i++) {&lt;br /&gt;
        const float dx = Items.x[j] - Items.x[i];&lt;br /&gt;
        const float dy = Items.y[j] - Items.y[i];&lt;br /&gt;
        const float dz = Items.z[j] - Items.z[i];&lt;br /&gt;
        ...&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  ... &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Hatékony hossz választás====&lt;br /&gt;
Olyan esetben jelent előnyt, ha például az n*n-es mátrix esetén n nem osztható 16-val, mert akkor a vektorizáció nem teljesen hajtható végre. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// n%16 != 0&lt;br /&gt;
float* A = _mm_malloc(sizeof(float)*n*n, 64);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Ilyen esetben mindig marad egy kis vektor részlet, amire elemenként kell elvégezni a műveletet.&amp;lt;br /&amp;gt;&lt;br /&gt;
A megoldást az jelenti, hogy a memória allokálást kiterjesztjük és nagyobb memória területet foglalunk a változónak, így már alkalmazható lesz a vektorizáció, mivel a maradvány területen is el tudjuk végezni a műveletet egy lépésben.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int lda=n;&lt;br /&gt;
if (n % 16 != 0) lda += (16 - n%16); // most lda%16==0&lt;br /&gt;
float* A = _mm_malloc(sizeof(float)*n*lda, 64);&lt;br /&gt;
&lt;br /&gt;
// használat&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
  for (int j = 0; j &amp;lt; n; j++)&lt;br /&gt;
    A[i*lda + j] = ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====&amp;quot;Héj ciklusok&amp;quot; kiküszöbölése====&lt;br /&gt;
Ciklusok esetén lehetséges, hogy a vektorizációt nem teljes mértékben ismeri fel a fordító program. &lt;br /&gt;
Ekkor segíthetünk a fordítónak egy fordítási direktívával ('''#pragma vector aligned'''), ha teljes mértékben biztosak vagyunk, hogy a ciklusunk vektorizálható!&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma vector aligned&lt;br /&gt;
  for (int j = 0; j &amp;lt; n; j++)&lt;br /&gt;
    A[i*lda + j] -= ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Ettől jelentős futás idejű javulást érhetünk el, mivel ezzel a #pragma-val kikapcsoltuk a futás idejű ellenőrzést, de előfordulhatnak olyan esetek, főleg amikor nem jól mértük fel a ciklusunkat és nem sikerült a vektorizálás, hogy ''Segmentation fault'' hibára fogunk futni.&lt;br /&gt;
&lt;br /&gt;
====Regularizáció====&lt;br /&gt;
Van egy ciklusunk, ami egy vektor elemeit módosítja. A processzor az adatokat &amp;quot;csokorba szedve&amp;quot; emeli be a belső kis méretű cache-be. Egyszerre természetesen több vektor elem is bekerül a a cache-be ezzel gyorsítva a feldolgozást. Ezen vektor iterációk valahogyan ráilleszkedik a vektorra.&lt;br /&gt;
&lt;br /&gt;
[[Fájl:regularization.png|Vektor regularizáció]]&lt;br /&gt;
&lt;br /&gt;
Abban az esetben, amikor ez az illesztés nem teljesen tökéletes (lsd. piros elemek), keletkeznem héj ciklusok, melyeket a regularizációval is eliminálni lehet. &amp;lt;br /&amp;gt;&lt;br /&gt;
A feladat eredetileg valahogyan így nézhet ki:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
for (int i = 0; i&amp;lt;n; i++)&lt;br /&gt;
  A[i]=...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ez a következő átalakítással már alkalmas vektorizációra és nem lesz &amp;quot;peel loop&amp;quot; probléma.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
for (int b=0; b&amp;lt;n; b++) {&lt;br /&gt;
  const int jStart = b- b%16; // mindig osztható 16-val&lt;br /&gt;
  ...&lt;br /&gt;
#pragma simd&lt;br /&gt;
  for (int j = jStart; j&amp;lt;n; j++)&lt;br /&gt;
    A[j]=...&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Párhuzamosítás===&lt;br /&gt;
Több féle mód létezik a párhuzamosításra. Korábban említettük a SIMD technikát, ami adatpárhuzamosításra való 1 processzoron belül, de létezik még processzorok közti párhuzamosítás, amit az OpenMP segítségével tudunk hatékonyan megvalósítani. Amennyiben gépek közti párhuzamosításra, üzenek küldésre van szükség, akkor az MPI-t kell segítségül hívni. Általános megoldás nem létezik minden feladatra, mert MPI is alkalmas lehet gépen belüli párhuzamosításra, nem csak az OpenMP. Amit mérlegelni kell, hogy mi az amire az alkalmazásnak szükséges van illetve mekkora kommunikációs overhead keletkezik, ha lecseréljük az OpenMP-t MPI-ra. Általában az egymástól független számításokat OpenMP-vel, míg a kommunikációt igénylő feladatokra MPI-t szoktak használni.&lt;br /&gt;
&lt;br /&gt;
====Szálak párhuzamosítása====&lt;br /&gt;
Az OpenMP-nek környezeti változók segítségével megadható a párhuzamosan futtatható szálak száma. Ezt a következő paranccsal tudjuk megtenni, ahol 5-re állítjuk.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export OMP_NUM_THREADS=5&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Forrásban a párhuzamos szálakat a ''#pragma omp parallel'' segítségével adhatjuk meg.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for              	//külső for szálak párhuzamosítása&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
#pragma simd				// vektorizáció a belső cikluson&lt;br /&gt;
  for (int j = 0; j &amp;lt; m; j++)&lt;br /&gt;
    My_Function(A[i][j]);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A fordítási direktívák össze is vonhatóak, ilyenkor 1 ciklus is elég.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for simd&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
    My_Function(A[i]);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=====Változók láthatósága, megosztása=====&lt;br /&gt;
Változók hatókörét a programozási nyelven kívül pragma-k segítségével is beállíthatjuk, hogy megosztott legyen a változó vagy a szálra nézve saját példány. Amennyiben nem definiáljuk felül, akkor minden változó megosztott. A felül definiálást a private és a shared paraméterekkel lehet megadni az OpenMP-nek.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int A, B, C;&lt;br /&gt;
&lt;br /&gt;
#pragma omp parallel private(A) shared(B)&lt;br /&gt;
{...}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Az itteni példában az B és C megosztott változó a szálak között, míg A-t minden szálnak lemásolja a futás kezdetén.&lt;br /&gt;
&lt;br /&gt;
====Párhuzamos szálak ütemezése====&lt;br /&gt;
OpenMp a szálak ütemezésére három féle módod kínál fel.&lt;br /&gt;
Módok:&lt;br /&gt;
* '''static''' (alapértelmezett): egyenletesen arányban egymás után elosztja a processzeket az indított szálakon, ezért a futási idő tetszőlegesen elhúzódhat, ha egyes processzek lassabban futnak le.&lt;br /&gt;
* '''dynamic''': minden esetben az első szabad szálra kerül a következő processz ütemezése, folyamatos kihasználtságot elérve, de &amp;quot;költséges&amp;quot; a sok-sok ütemezés&lt;br /&gt;
* '''guided''': Az előző két mód ötvözése. Nagyobb procesz számot ütemez az éppen szabad szálakra, melyek számát folyamatosan csökkenti a futás előre haladtával, hogy körülbelül egyszerre fejeződjön be minden szál futása, ezzel csökkentve a dynamic esetén magad ütemezési időráfordítást.&lt;br /&gt;
&lt;br /&gt;
Mindhárom esetben megadható az egyszerre ütemezett processzek száma, amit a ''chunk'' értéke határoz meg. Ha megvan adva a chunk értéke, akkor az minden esetben annak megfelelő processzámot ütemez a szálakra.&lt;br /&gt;
Használata:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel [schedule (&amp;lt;mode&amp;gt;,&amp;lt;chunk&amp;gt;) {...}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Szálak szinkronizációja====&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
	   * with mutexes		(it is very slow, waiting always)		#pragma omp parallel for		  for (int i = 0; i &amp;lt; n; i++) {		#pragma omp critical		    { // Only one thread at a time can execute this section		      total = total + i;		    }		  }	   * or with atomic operation		(it also [very] slow, lot of limitations)		#pragma omp parallel for		for (int i = 0; i &amp;lt; n; i++)		  { // Lightweight synchronization		#pragma omp atomic		    total += i;		  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=====Párhuzamos redukció=====&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
	     (it is fast)  BUT not for vector		int total = 0;		#pragma omp parallel for reduction(+: total)               //usage: reduction(operation: scalar)		for (int i = 0; i &amp;lt; n; i++) {		  total = total + i;		}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Ciklusok párhuzamosítása====&lt;br /&gt;
Ciklus párhuzamosítására a következő mintát szokás használni.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel&lt;br /&gt;
{&lt;br /&gt;
  // Az itt definiált forrást minden szál esetén végre fog hajtani&lt;br /&gt;
  ...&lt;br /&gt;
#pragma omp for [schedule (&amp;lt;mode&amp;gt;,&amp;lt;chunk&amp;gt;)&lt;br /&gt;
  for (int i = 0; i &amp;lt; n; i++) {&lt;br /&gt;
    // Ez a kódrész lesz párhuzamosítva&lt;br /&gt;
    ...&lt;br /&gt;
  }&lt;br /&gt;
  // ... és ez a köd is minden szál esetén végrehajtódik&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Ciklus kifejtés====&lt;br /&gt;
A technika már egy példa erejéig a vektorizációnál is látható, de ott nem került részletezésre. A megoldás lényege, hogy a kellően nagy ciklust szétdaraboljuk kisebb darabokra, amit már külön feldolgozhatóak, ezzel párhuzamosítva a folyamatokat. Az egy darab kiindulási ciklusból két darab egymásba ágyazott ciklust hozunk létre, ami ugyanazon a feladattéren dolgozik. &lt;br /&gt;
&lt;br /&gt;
Eredeti állapot:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++) {...}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Átírt változat:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int STRIP=1024;&lt;br /&gt;
for (int ii = 0; ii &amp;lt; n; ii += STRIP)&lt;br /&gt;
 for (int i = ii; i &amp;lt; ii+STRIP; i++) {...}&lt;br /&gt;
&lt;br /&gt;
// ha még van maradék elem, akkor azokon is végrehajtjuk a számításokat&lt;br /&gt;
if (n%STRIP != 0) {&lt;br /&gt;
  for (i=n-(n%STRIP)+1; i&amp;lt;n; i++) {...}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A darabok mérete (STRIP értéke) az adott rendszer és feladat függvényében változhat, tehát ez egyfajta &amp;quot;tuning&amp;quot; paraméter.&lt;br /&gt;
&lt;br /&gt;
====Automatikus ciklusösszevonás====&lt;br /&gt;
Az OpenMP támogatást nyújt több egymásba ágyazott ciklus &amp;quot;összevonására&amp;quot;. Ehhez csak a '''collaps''' fordítási direktívát és az összevonandó ciklusok számát kell megadni. &amp;lt;br /&amp;gt;&lt;br /&gt;
FONTOS: ekkor az automatikus vektorizáció kikapcsolásra kerül!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for collaps(2)&lt;br /&gt;
for (int i=0; i&amp;lt;n; i++)&lt;br /&gt;
  for( int j=0; j&amp;lt;m; j++){...}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A fneti példában a 2db for ciklis kerül párhuzamosításra úgy, hogy a fordítás során 1db ciklus generálódik belőle, ami a következő paraméterezéssel ekvivalens:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for&lt;br /&gt;
for (int c=0; c &amp;lt; n*m; c++) {&lt;br /&gt;
  int i = c/n;&lt;br /&gt;
  int j = c%n;&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Szigeti(AT)niif.hu</name></author>	</entry>

	<entry>
		<id>https://wiki.niif.hu/index.php?title=Intel_Xeon_Phi&amp;diff=3754</id>
		<title>Intel Xeon Phi</title>
		<link rel="alternate" type="text/html" href="https://wiki.niif.hu/index.php?title=Intel_Xeon_Phi&amp;diff=3754"/>
				<updated>2016-05-05T12:05:14Z</updated>
		
		<summary type="html">&lt;p&gt;Szigeti(AT)niif.hu: /* Ciklusok párhuzamosítása */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Az oldalon található információk segítséget nyújtanak a HPC felhasználóinknak, hogy alkalmazásaikat miként portolhatják valamint optimalizálhatják a hatékonyabb futást szem előtt tartva Intel Xeon Phi koprocesszor kártyákon.&lt;br /&gt;
&lt;br /&gt;
==Hardver bemutatása==&lt;br /&gt;
Intel® Xeon Phi™ Coprocessor 7120P&lt;br /&gt;
&amp;lt;gallery&amp;gt;&lt;br /&gt;
Fájl:Xeon-phi-family-rwd.png.rendition.intel.web.720.405.png|&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-  align=&amp;quot;center&amp;quot;&lt;br /&gt;
! CPU&lt;br /&gt;
! threads/Core&lt;br /&gt;
!max threads&lt;br /&gt;
! CPU Freq&lt;br /&gt;
! Memória&lt;br /&gt;
! L1 cache&lt;br /&gt;
! L2 cache&lt;br /&gt;
! Interfész&lt;br /&gt;
! Ptot&lt;br /&gt;
! Számítási kapacitás&lt;br /&gt;
|-  align=&amp;quot;center&amp;quot;&lt;br /&gt;
| 61&lt;br /&gt;
| 4 Hardver&lt;br /&gt;
| 244&lt;br /&gt;
| 1238 GHz&lt;br /&gt;
| 16GB GDDR5&lt;br /&gt;
| 32KB/core&lt;br /&gt;
| 512KB/core&lt;br /&gt;
| PCIe&lt;br /&gt;
| 300W&lt;br /&gt;
| 1,2 TFlop/s dupla pontosság&lt;br /&gt;
|-  align=&amp;quot;center&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
| OpenMP-nél csak '''240'''&lt;br /&gt;
|&lt;br /&gt;
| ~100 cycles&lt;br /&gt;
~174 GB/s&lt;br /&gt;
| ~3 cycles&lt;br /&gt;
| ~10 cycles&lt;br /&gt;
| ~7 GB/s&lt;br /&gt;
~1 microsecond&lt;br /&gt;
|&lt;br /&gt;
| 2,4 TFlop/s egyszeres pontosság&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Az Intel® 1. generációs Xeon Phi™ koprocesszorában található 61 processzor ugyanazt a 16GB memóriát éri el, de semmilyen módon se fér hozzá közvetlenül a hoszt gép fizikai memóriájához.&amp;lt;br /&amp;gt;&lt;br /&gt;
[[Budapest2_klaszter|Budapest2]] és [[HPC#Szupersz.C3.A1m.C3.ADt.C3.B3g.C3.A9peink_.C3.B6sszehasonl.C3.ADt.C3.A1sa|Debrecen3-Phi]] gépeinkben számítási nodonként 2-2 Phi™ koprocesszor található. &amp;lt;br/&amp;gt;&lt;br /&gt;
A kártyákon speciális célú Linux kernel fut melyre a belépés limitáltan csak a rendszergazdák számára lehetséges SSH-n keresztül a hoszt gépről. Természetesen a kártyára belépve olyan érzésünk lehet, mintha egy sok processzoros gépen dolgoznánk, ami rendelkezik rengeteg memóriával. Ez így is van, de a hatékonyabb kihasználás érdekében az SSH-n keresztüli elérés nem lehetséges, csak a Slurm segítségével valamint a programozási nyelvek által támogatott csatornákon keresztül. Ennek részletezése lentebb megtalálható.&lt;br /&gt;
&lt;br /&gt;
==Használati módok==&lt;br /&gt;
A koprocesszor használatai módjait két csoportba lehet sorolni. A '''natív''' és az '''offload''' használatot.  &amp;lt;br/&amp;gt;&lt;br /&gt;
Ezen használati módokról a lenti fejezetekben több részletet is megtudhat. Az itt megfogalmazott &amp;quot;támogatás&amp;quot; csak a mi infrastruktúránkra érvényes! &amp;lt;br /&amp;gt;&lt;br /&gt;
A bemutatott példák ''c++'' nyelven vannak, de természetesen ''c'' és ''fortran'' esetén is megtalálható az ekvivalens módszer.&lt;br /&gt;
&lt;br /&gt;
===a) nem támogatott mód===&lt;br /&gt;
====közvetlen natív használat====&lt;br /&gt;
A natív mód arra szolgál, hogy a MIC kártyára  lefordított alkalmazásunkat felmásoljuk a kártyára, majd belépünk SSH-n a kártyára és azon futtatjuk a bináris kódunkat. &amp;lt;br /&amp;gt;&lt;br /&gt;
Ez a legegyszerűbb használati mód, ugyanis nincs más dolgunk, csak a '''-mmic''' kapcsolót használni a fordításnál és a fordító egy máris a MIC architektúrára kész bináris készít nekünk. &amp;lt;br/&amp;gt;&lt;br /&gt;
Workflow a következő lenne:&lt;br /&gt;
# Alkalmazás megírása. például [[Média:hello.cc|''hello.cc'']]&lt;br /&gt;
# Alkalmazás fordítása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;icpc -o hello-MIC -mmic hello.cc&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Másolás a MIC kártyára &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;scp hello-MIC mic0:&amp;lt;/pre&amp;gt;&lt;br /&gt;
# belépés a kártyára &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;ssh mic0&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Alkalmazás futtatása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;./hello-MIC&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===b) támogatott, de nem preferált használat===&lt;br /&gt;
====automatikus kódátvitel====&lt;br /&gt;
Ebben az esetben nem kell átmásolnunk a bináris alkalmazásunkat a kártyára sőt még csak be se kell lépnünk, mert ezt a két lépést egy beépített tool végzi el. &amp;lt;br/&amp;gt;&lt;br /&gt;
A tool eléréséhez szükséges parancs neve: '''micnativeloadex'''&lt;br /&gt;
&lt;br /&gt;
Ebben az esetben a workflow a következőképpen módosul:&lt;br /&gt;
# Alkalmazás megírása. például [[Média:hello.cc|''hello.cc'']]&lt;br /&gt;
# Alkalmazás fordítása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;icpc -o hello-MIC -mmic hello.cc&amp;lt;/pre&amp;gt;&lt;br /&gt;
# A tool futásához szükséges library-k megadása &amp;lt;br&amp;gt; &amp;lt;pre&amp;gt;export SINK_LD_LIBRARY_PATH=/opt/intel/composerxe/compiler/lib/mic&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Alkalmazás futtatása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;micnativeloadex hello-MIC&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| BUDAPEST2[cn10] phi (0)$ export SINK_LD_LIBRARY_PATH=/opt/intel/composerxe/compiler/lib/mic &amp;lt;br/&amp;gt;BUDAPEST2[cn10] phi (0)$ micnativeloadex hello-MIC &amp;lt;br/&amp;gt;Hello world! I have 244 logical cores.&amp;lt;br/&amp;gt;BUDAPEST2[cn10] phi (0)$&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===c) támogatott módok===&lt;br /&gt;
====Offload====&lt;br /&gt;
Az Offload modell esetén szükséges a forráskód módosítása, mégpedig úgy, hogy azon kódrészletet, amit a koprocesszoron szeretnénk futtatni, ki kell egészíteni egy fordítási direktívával. Az alkalmazás fordításakor elegendő csak a CPU-ra való binárist készíteni mivel a modell végzi a megfelelő forrás részlet mozgatását és futtatását.&amp;lt;br /&amp;gt;&lt;br /&gt;
[[Fájl:offload.png|Offload modell]] &amp;lt;br /&amp;gt;&lt;br /&gt;
Példánkban jól látható, hogy a második kiíratás már a koprocesszoron fut.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
int main(int argc, char * argv[]) {&lt;br /&gt;
  printf(&amp;quot;Hello world from host!\n&amp;quot;);&lt;br /&gt;
#pragma offload target(mic)&lt;br /&gt;
  {&lt;br /&gt;
    printf(&amp;quot;Hello world from coprocessor!\n&amp;quot;); fflush(stdout);&lt;br /&gt;
  }&lt;br /&gt;
  printf(&amp;quot;Bye\n&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ebben a példában az operációs rendszer választja ki, hogy melyik Phi kártyán fusson le a kódrészlet. Amennyiben mi szeretnénk explicite megmondani, hogy melyik kártyán fusson, akkor a fordítási direktívában a mic kulcsszó után kell tenni egy &amp;quot;:&amp;quot;-t és a kártya sorszámát [mi esetünkben 0 vagy 1]. Ekkor így nézne ki a direktíva: ''#pragma offload target(mic:1)''&lt;br /&gt;
&lt;br /&gt;
Amennyiben az offload szekcióban függvény hívás is szerepel, akkor szükségeltetik az függvény futásához szükséges változók adatterületének átmásolása a Phi kártyára, majd a lefutás után visszamásolni a hoszt gépre. Ezen másolást az ''offload_attribute'' ''push'' és ''pop'' paraméterei segítségével határozhatjuk meg. &amp;lt;br /&amp;gt;&lt;br /&gt;
A következő mátrix-vektor szorzás példánál az A mátrix globális változó, míg a b és c vektorok a függvény paraméterei.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//A változók felmásolásra kerülnek a Xeon Phi-re&lt;br /&gt;
#pragma offload_attribute(push, target(mic))&lt;br /&gt;
  double* A;&lt;br /&gt;
  void MatrixVector(double *b, double *c, double m, double n) {&lt;br /&gt;
    ...&lt;br /&gt;
    c[] = A[..]*b[]&lt;br /&gt;
  }&lt;br /&gt;
// felmásolt objektumok visszamásolása&lt;br /&gt;
#pragma offload_attribute(pop)&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
  ...&lt;br /&gt;
#pragma offload target(mic) in(A[0:n*m])&lt;br /&gt;
// így is meg lehetne adni: #pragma offload target(mic:0) in(A : length(n*m))	        &lt;br /&gt;
  {&lt;br /&gt;
    MatrixVector(b, c, m, n);&lt;br /&gt;
  }&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ekkor minden memória területet 2x kell másolni, egyszer fel a kártyára, majd másodszor vissza a hoszt gépre, pedig elegendő lenne csak az eredmény vektort visszamásolni. Ezen optimalizálással egy későbbi fejezetben foglalkozunk. &amp;lt;p /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Futás során kaphatunk részletes riportot is, hogy pontosan milyen adatmozgatások történnek az alkalmazás futása során. Ehhez nincs szükség másra csak az '''OFFLOAD_REPORT''' környezeti változó megfelelő beállítása. A beállított értékek 1, 2 vagy 3 lehetnek, attól függően mennyire részletes riportot szeretnénk kapni. &amp;lt;br /&amp;gt;&lt;br /&gt;
beállítás:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export OFFLOAD_REPORT=3&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
példa kimenet a hello alkalmazásra:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Hello world from host!&lt;br /&gt;
[Offload] [MIC 0] [File]                    hello_offload.cc&lt;br /&gt;
[Offload] [MIC 0] [Line]                    5&lt;br /&gt;
[Offload] [MIC 0] [Tag]                     Tag 0&lt;br /&gt;
Hello world from coprocessor!&lt;br /&gt;
[Offload] [HOST]  [Tag 0] [CPU Time]        0.554653(seconds)&lt;br /&gt;
[Offload] [MIC 0] [Tag 0] [CPU-&amp;gt;MIC Data]   0 (bytes)&lt;br /&gt;
[Offload] [MIC 0] [Tag 0] [MIC Time]        0.000140(seconds)&lt;br /&gt;
[Offload] [MIC 0] [Tag 0] [MIC-&amp;gt;CPU Data]   0 (bytes)&lt;br /&gt;
Bye&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Offload + OpenMP====&lt;br /&gt;
Amennyiben nem csak 1 processzort szeretnénk használni a Phi kártyánkon és az alkalmazásunkban használt számítás megengedi a párhuzamosítást, akkor az OpenMP segítségével párhuzamosíthatjuk a megfelelő számítást. &amp;lt;br /&amp;gt;&lt;br /&gt;
[[Fájl:Offload+openmp.png|Offload modell és OpenMP]] &amp;lt;br /&amp;gt;&lt;br /&gt;
Példánk maradjon ugyanúgy a mátrix-vektor szorzás. Amennyiben a teljes kártya adta kapacitást ki szeretnénk használni, akkor az OMP threadek számát 240-re kell beállítani. Azért nem 244-re, mivel 1 CPU-t dedikáltan csak a kommunikációra használ a rendszer és 1 CPU 4 hardver threaddel rendelkezik.&lt;br /&gt;
Ekkor a példánk így változik:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;omp.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#pragma offload_attribute(push, target(mic))&lt;br /&gt;
void multiply(int n, int m, double* A, double* b, double* c){&lt;br /&gt;
  const int nCPUs = omp_get_max_threads();   //240&lt;br /&gt;
  omp_set_num_threads(nCPUs);&lt;br /&gt;
#pragma omp parallel for schedule(dynamic, 1)&lt;br /&gt;
  for ( int i = 0 ; i &amp;lt; m ; i++)&lt;br /&gt;
     for ( int j = 0 ; j &amp;lt; n ; j++)&lt;br /&gt;
       c[i] += A[i*n+j] * b[j];&lt;br /&gt;
}&lt;br /&gt;
#pragma offload_attribute(pop)&lt;br /&gt;
&lt;br /&gt;
int main(){&lt;br /&gt;
  double * A = (double*) malloc(sizeof(double)*n*m);&lt;br /&gt;
  ...&lt;br /&gt;
#pragma offload target(mic) in(A:length(n*m))&lt;br /&gt;
     multiply(n, m, A, b, c);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Az alkalmazás fordításánál a fordítónak meg kell adni, hogy használhja az openmp library-ket is. Ezt a '''-qopenmp''' kapcsolóval tudjuk elérni.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
icpc -o matrix-CPU -qopenmp matrix.cc&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====MPI + Offload + OpenMP====&lt;br /&gt;
Abban az esetben, ha 1 számítási node nem elég, MPI segítségével össze tudunk kötni több nodet, heterogén rendszert alkotva, és azokon alkalmazható a fenti Offload + OpenMP modell. &amp;lt;br /&amp;gt;&lt;br /&gt;
[[Fájl:Mpi+offload+openmp.png|MPI elosztás valamint Offload modell és OpenMP]] &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Alkalmazások optimalizálása a kártyára==&lt;br /&gt;
Ebben a fejezetben optimalizálási lehetőségeket mutatunk be, melyek segítségül szolgálhatnak az egyes alkalmazások hatékonyabb futása érdekében ezzel jobban kihasználni a rendelkezésre álló erőforrások adta számítási kapacitás.&lt;br /&gt;
&lt;br /&gt;
===Környezeti változók===&lt;br /&gt;
Lehetséges csak a Phi kártyákon használt környezeti változók beállítása, amit az alkalmazásunk használni is tud. &amp;lt;br /&amp;gt;&lt;br /&gt;
Példánkban az OpenMP esetén használható szálak számát állíthatjuk be:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export MIC_ENV_PREFIX=XEONPHI&lt;br /&gt;
export OMP_NUM_THREADS=12&lt;br /&gt;
export XEONPHI_OMP_NUM_THREADS=240&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
A beállított prefix tetszőleges karaktersorozat lehet, a lényeg, hogy ezt a beállított prefixet használjuk a környezeti változók esetén is!&lt;br /&gt;
 &lt;br /&gt;
===Hosszredukció===&lt;br /&gt;
Többnyire a fordítók, ha nem mondjuk meg explicite, a dupla pontosságú változatot választják a lehetséges változó- és függvény típusok közül. Amennyiben a számításunk nem igényli a dupla pontosságot, akkor használjuk a forrásban mindenhol az adott típus vagy függvény egyszeres pontosságú változatát, mellyel jelentős teljesítmény növekedést tudunk elérni. Továbbá az egyes műveletek futási ideje nem egyforma, így a műveletet helyettesíthetjük más művelettel, ami gyorsabb, akkor ismét javíthatunk a hatékonyságon. &amp;lt;br /&amp;gt;&lt;br /&gt;
Íme pár példa, amit alkalmazni lehet:&lt;br /&gt;
* &amp;quot;''/''&amp;quot; művelet helyett &amp;quot;''*''&amp;quot; -t használjunk, mert azt gyorsabban végre tudja hajtani a processzor&lt;br /&gt;
* ''pow'' =&amp;gt; ''1.0 / sqrt()''    , tetszőleges hatványozás, pl.: 3/2-ik hatvány&lt;br /&gt;
* hardver áltatl támogatott műveletek:&lt;br /&gt;
** ''exp'' =&amp;gt; ''exp2''&lt;br /&gt;
** ''log'' =&amp;gt; ''log2''&lt;br /&gt;
* dupla pontosság -&amp;gt; egyszeres pontosság lebegőpontos számok és műveletek esetén &lt;br /&gt;
** ''1.0'' =&amp;gt; ''1.0f''&lt;br /&gt;
** ''pow'' =&amp;gt; ''powf''&lt;br /&gt;
&lt;br /&gt;
===Vektorizáció===&lt;br /&gt;
A vektorizáció azon fordítási időben eldönthető optimalizációs faktor, amely nagy mértékben függ attól a hardver felépítésétől, amire éppen a fordítás történni fog. Legfontosabb befolyásoló tényezők a processzor cache méretei illetve a vektor műveletekért felelős aritmetikai egységének jellemzői mivel ezeket használja fel a fordító az optimalizálási folyamata során. &amp;lt;br /&amp;gt;&lt;br /&gt;
Az automatikus vektorizáció minimum -O2 szinttől érvényes, de mivel általában ez a szint az alapértelmezett, ezért minden fordításnál alkalmazásra kerül. &amp;lt;br /&amp;gt;&lt;br /&gt;
A fordító programnak a következő kapcsolókat lehet megadni, melyek segítenek a minél jobb vektorizációban:&lt;br /&gt;
* ''-qopt-report=&amp;lt;szám&amp;gt;'' ; az optimalizációs jelentés részletességét lehet megadni 0-tól 5-ig, ahol az 5-s szint a legrészletesebbet jelenti&lt;br /&gt;
* ''-qopt-report-phase:vec'' ; a vektorizációról szeretnék jelentést kapni&lt;br /&gt;
* ''-opt-report-file=&amp;lt;fájlnév&amp;gt;'' ; megadható a riportfájl neve&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
icpc autovec.cc -mmic -qopt-report=5 -opt-report-file=vectorization.oMIC.optrpt -qopt-report-phase:vec&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====SIMD====&lt;br /&gt;
Az adatok hatékonyabb feldolgozására az egyik megvalósítás a SIMD (Single Instruction Multiple Data), amikor is a különálló adatokat úgy kezeli, mintha az vektorok volnának és azon végzi el a vektor-műveletet. Ezzel egy a hardver által támogatott műveletet végzünk egy lépésben egy nagyobb adatmennyiségen, ezzel párhuzamosítva a feldolgozási folyamatot. Ezzel a módszerrel nagyon jó adat szintű párhuzamosítást lehet elérni.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Fájl:Simd.png|SIMD]]&lt;br /&gt;
&lt;br /&gt;
Főbb támogatott műveletek:&lt;br /&gt;
* aritmetikai műveletek, bináris operátorok&lt;br /&gt;
* összehasonlítások&lt;br /&gt;
* konverziók és típus kasztolások&lt;br /&gt;
* bit-műveletek: NOT, AND, OR, XOR, XAND&lt;br /&gt;
&lt;br /&gt;
Használata: '''simd''' fordítási direktívával lehetséges &amp;lt;br /&amp;gt;&lt;br /&gt;
Például:&lt;br /&gt;
* egyszerű vektorizáció&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma simd&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
  A[i] += B[i];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* adat párhuzamosítás OpenMP segítségével&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for simd&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
  DoSomeWork(A[i]);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====AoS -&amp;gt; SoA====&lt;br /&gt;
A kiindulás probléma az, hogy a struktúrákat tartalmazó vektort nem lehet jól vektorizálni, mivel a cache-ben tárolt adatok nagy többsége feleslegesen van bent, mivel azon adatokat csak egy későbbi ciklus futás során fogja az alkalmazás feldolgozni. Tehát ezen adatok jelenleg feleslegesen foglalják a cache területet, ahelyett, hogy hasznos adatot tárolnánk ott.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
struct MyStructure {&lt;br /&gt;
  float x, y, z;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void My_Function(cons int nItem, MyStructure* const Items, const float delta) {&lt;br /&gt;
  for (int i = 0; i &amp;lt; nItem; i++) {&lt;br /&gt;
    ...&lt;br /&gt;
    for (int j = 0; j &amp;lt; nItem; j++) {&lt;br /&gt;
      const float dx = Items[j].x - Items[i].x;&lt;br /&gt;
      const float dy = Items[j].y - Items[i].y;&lt;br /&gt;
      const float dz = Items[j].z - Items[i].z;&lt;br /&gt;
      ...  &lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  ... &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A vektorban a struktúrák tárolása helyett minden elemnek egy dedikált vektort allokálunk és így könnyen tudja a fordító vektorizálni azt. Természetesen segítségként a vektor feldolgozását &amp;quot;tömbösítettük&amp;quot; a tileSize méret megadásával illetve a ''unroll'' fordítási direktíva segítségével.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
struct MyStructureSet {&lt;br /&gt;
  float *x, *y, *z;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void My_Function(cons int nItem, MyStructure* const Items, const float delta) {&lt;br /&gt;
  const int tileSize = 16;&lt;br /&gt;
#pragma omp parallel for schedule(guided)&lt;br /&gt;
  for (int ii = 0; ii &amp;lt; nItem; ii+= tileSize) {&lt;br /&gt;
    ...&lt;br /&gt;
#pragma unroll(tileSize)&lt;br /&gt;
    for (int j = 0; j &amp;lt; nItem; j++) {&lt;br /&gt;
#pragma unroll(tileSize)&lt;br /&gt;
      for (int i = ii; i &amp;lt; ii + tileSize; i++) {&lt;br /&gt;
        const float dx = Items.x[j] - Items.x[i];&lt;br /&gt;
        const float dy = Items.y[j] - Items.y[i];&lt;br /&gt;
        const float dz = Items.z[j] - Items.z[i];&lt;br /&gt;
        ...&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  ... &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Hatékony hossz választás====&lt;br /&gt;
Olyan esetben jelent előnyt, ha például az n*n-es mátrix esetén n nem osztható 16-val, mert akkor a vektorizáció nem teljesen hajtható végre. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// n%16 != 0&lt;br /&gt;
float* A = _mm_malloc(sizeof(float)*n*n, 64);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Ilyen esetben mindig marad egy kis vektor részlet, amire elemenként kell elvégezni a műveletet.&amp;lt;br /&amp;gt;&lt;br /&gt;
A megoldást az jelenti, hogy a memória allokálást kiterjesztjük és nagyobb memória területet foglalunk a változónak, így már alkalmazható lesz a vektorizáció, mivel a maradvány területen is el tudjuk végezni a műveletet egy lépésben.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int lda=n;&lt;br /&gt;
if (n % 16 != 0) lda += (16 - n%16); // most lda%16==0&lt;br /&gt;
float* A = _mm_malloc(sizeof(float)*n*lda, 64);&lt;br /&gt;
&lt;br /&gt;
// használat&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
  for (int j = 0; j &amp;lt; n; j++)&lt;br /&gt;
    A[i*lda + j] = ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====&amp;quot;Héj ciklusok&amp;quot; kiküszöbölése====&lt;br /&gt;
Ciklusok esetén lehetséges, hogy a vektorizációt nem teljes mértékben ismeri fel a fordító program. &lt;br /&gt;
Ekkor segíthetünk a fordítónak egy fordítási direktívával ('''#pragma vector aligned'''), ha teljes mértékben biztosak vagyunk, hogy a ciklusunk vektorizálható!&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma vector aligned&lt;br /&gt;
  for (int j = 0; j &amp;lt; n; j++)&lt;br /&gt;
    A[i*lda + j] -= ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Ettől jelentős futás idejű javulást érhetünk el, mivel ezzel a #pragma-val kikapcsoltuk a futás idejű ellenőrzést, de előfordulhatnak olyan esetek, főleg amikor nem jól mértük fel a ciklusunkat és nem sikerült a vektorizálás, hogy ''Segmentation fault'' hibára fogunk futni.&lt;br /&gt;
&lt;br /&gt;
====Regularizáció====&lt;br /&gt;
Van egy ciklusunk, ami egy vektor elemeit módosítja. A processzor az adatokat &amp;quot;csokorba szedve&amp;quot; emeli be a belső kis méretű cache-be. Egyszerre természetesen több vektor elem is bekerül a a cache-be ezzel gyorsítva a feldolgozást. Ezen vektor iterációk valahogyan ráilleszkedik a vektorra.&lt;br /&gt;
&lt;br /&gt;
[[Fájl:regularization.png|Vektor regularizáció]]&lt;br /&gt;
&lt;br /&gt;
Abban az esetben, amikor ez az illesztés nem teljesen tökéletes (lsd. piros elemek), keletkeznem héj ciklusok, melyeket a regularizációval is eliminálni lehet. &amp;lt;br /&amp;gt;&lt;br /&gt;
A feladat eredetileg valahogyan így nézhet ki:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
for (int i = 0; i&amp;lt;n; i++)&lt;br /&gt;
  A[i]=...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ez a következő átalakítással már alkalmas vektorizációra és nem lesz &amp;quot;peel loop&amp;quot; probléma.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
for (int b=0; b&amp;lt;n; b++) {&lt;br /&gt;
  const int jStart = b- b%16; // mindig osztható 16-val&lt;br /&gt;
  ...&lt;br /&gt;
#pragma simd&lt;br /&gt;
  for (int j = jStart; j&amp;lt;n; j++)&lt;br /&gt;
    A[j]=...&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Párhuzamosítás===&lt;br /&gt;
Több féle mód létezik a párhuzamosításra. Korábban említettük a SIMD technikát, ami adatpárhuzamosításra való 1 processzoron belül, de létezik még processzorok közti párhuzamosítás, amit az OpenMP segítségével tudunk hatékonyan megvalósítani. Amennyiben gépek közti párhuzamosításra, üzenek küldésre van szükség, akkor az MPI-t kell segítségül hívni. Általános megoldás nem létezik minden feladatra, mert MPI is alkalmas lehet gépen belüli párhuzamosításra, nem csak az OpenMP. Amit mérlegelni kell, hogy mi az amire az alkalmazásnak szükséges van illetve mekkora kommunikációs overhead keletkezik, ha lecseréljük az OpenMP-t MPI-ra. Általában az egymástól független számításokat OpenMP-vel, míg a kommunikációt igénylő feladatokra MPI-t szoktak használni.&lt;br /&gt;
&lt;br /&gt;
====Szálak párhuzamosítása====&lt;br /&gt;
Az OpenMP-nek környezeti változók segítségével megadható a párhuzamosan futtatható szálak száma. Ezt a következő paranccsal tudjuk megtenni, ahol 5-re állítjuk.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export OMP_NUM_THREADS=5&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Forrásban a párhuzamos szálakat a ''#pragma omp parallel'' segítségével adhatjuk meg.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for              	//külső for szálak párhuzamosítása&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
#pragma simd				// vektorizáció a belső cikluson&lt;br /&gt;
  for (int j = 0; j &amp;lt; m; j++)&lt;br /&gt;
    My_Function(A[i][j]);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A fordítási direktívák össze is vonhatóak, ilyenkor 1 ciklus is elég.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for simd&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
    My_Function(A[i]);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=====Változók láthatósága, megosztása=====&lt;br /&gt;
Változók hatókörét a programozási nyelven kívül pragma-k segítségével is beállíthatjuk, hogy megosztott legyen a változó vagy a szálra nézve saját példány. Amennyiben nem definiáljuk felül, akkor minden változó megosztott. A felül definiálást a private és a shared paraméterekkel lehet megadni az OpenMP-nek.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int A, B, C;&lt;br /&gt;
&lt;br /&gt;
#pragma omp parallel private(A) shared(B)&lt;br /&gt;
{...}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Az itteni példában az B és C megosztott változó a szálak között, míg A-t minden szálnak lemásolja a futás kezdetén.&lt;br /&gt;
&lt;br /&gt;
====Párhuzamos szálak ütemezése====&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Szálak szinkronizációja====&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
	   * with mutexes		(it is very slow, waiting always)		#pragma omp parallel for		  for (int i = 0; i &amp;lt; n; i++) {		#pragma omp critical		    { // Only one thread at a time can execute this section		      total = total + i;		    }		  }	   * or with atomic operation		(it also [very] slow, lot of limitations)		#pragma omp parallel for		for (int i = 0; i &amp;lt; n; i++)		  { // Lightweight synchronization		#pragma omp atomic		    total += i;		  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=====Párhuzamos redukció=====&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
	     (it is fast)  BUT not for vector		int total = 0;		#pragma omp parallel for reduction(+: total)               //usage: reduction(operation: scalar)		for (int i = 0; i &amp;lt; n; i++) {		  total = total + i;		}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Ciklusok párhuzamosítása====&lt;br /&gt;
Ciklus párhuzamosítására a következő mintát szokás használni.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel&lt;br /&gt;
{&lt;br /&gt;
  // Az itt definiált forrást minden szál esetén végre fog hajtani&lt;br /&gt;
  ...&lt;br /&gt;
#pragma omp for [schedule (&amp;lt;mode&amp;gt;,&amp;lt;chunk&amp;gt;)&lt;br /&gt;
  for (int i = 0; i &amp;lt; n; i++) {&lt;br /&gt;
    // Ez a kódrész lesz párhuzamosítva&lt;br /&gt;
    ...&lt;br /&gt;
  }&lt;br /&gt;
  // ... és ez a köd is minden szál esetén végrehajtódik&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Ciklus kifejtés====&lt;br /&gt;
A technika már egy példa erejéig a vektorizációnál is látható, de ott nem került részletezésre. A megoldás lényege, hogy a kellően nagy ciklust szétdaraboljuk kisebb darabokra, amit már külön feldolgozhatóak, ezzel párhuzamosítva a folyamatokat. Az egy darab kiindulási ciklusból két darab egymásba ágyazott ciklust hozunk létre, ami ugyanazon a feladattéren dolgozik. &lt;br /&gt;
&lt;br /&gt;
Eredeti állapot:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++) {...}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Átírt változat:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int STRIP=1024;&lt;br /&gt;
for (int ii = 0; ii &amp;lt; n; ii += STRIP)&lt;br /&gt;
 for (int i = ii; i &amp;lt; ii+STRIP; i++) {...}&lt;br /&gt;
&lt;br /&gt;
// ha még van maradék elem, akkor azokon is végrehajtjuk a számításokat&lt;br /&gt;
if (n%STRIP != 0) {&lt;br /&gt;
  for (i=n-(n%STRIP)+1; i&amp;lt;n; i++) {...}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A darabok mérete (STRIP értéke) az adott rendszer és feladat függvényében változhat, tehát ez egyfajta &amp;quot;tuning&amp;quot; paraméter.&lt;br /&gt;
&lt;br /&gt;
====Automatikus ciklusösszevonás====&lt;br /&gt;
Az OpenMP támogatást nyújt több egymásba ágyazott ciklus &amp;quot;összevonására&amp;quot;. Ehhez csak a '''collaps''' fordítási direktívát és az összevonandó ciklusok számát kell megadni. &amp;lt;br /&amp;gt;&lt;br /&gt;
FONTOS: ekkor az automatikus vektorizáció kikapcsolásra kerül!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for collaps(2)&lt;br /&gt;
for (int i=0; i&amp;lt;n; i++)&lt;br /&gt;
  for( int j=0; j&amp;lt;m; j++){...}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A fneti példában a 2db for ciklis kerül párhuzamosításra úgy, hogy a fordítás során 1db ciklus generálódik belőle, ami a következő paraméterezéssel ekvivalens:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for&lt;br /&gt;
for (int c=0; c &amp;lt; n*m; c++) {&lt;br /&gt;
  int i = c/n;&lt;br /&gt;
  int j = c%n;&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Szigeti(AT)niif.hu</name></author>	</entry>

	<entry>
		<id>https://wiki.niif.hu/index.php?title=Intel_Xeon_Phi&amp;diff=3753</id>
		<title>Intel Xeon Phi</title>
		<link rel="alternate" type="text/html" href="https://wiki.niif.hu/index.php?title=Intel_Xeon_Phi&amp;diff=3753"/>
				<updated>2016-05-05T12:00:26Z</updated>
		
		<summary type="html">&lt;p&gt;Szigeti(AT)niif.hu: /* Változók láthatósága, megosztása */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Az oldalon található információk segítséget nyújtanak a HPC felhasználóinknak, hogy alkalmazásaikat miként portolhatják valamint optimalizálhatják a hatékonyabb futást szem előtt tartva Intel Xeon Phi koprocesszor kártyákon.&lt;br /&gt;
&lt;br /&gt;
==Hardver bemutatása==&lt;br /&gt;
Intel® Xeon Phi™ Coprocessor 7120P&lt;br /&gt;
&amp;lt;gallery&amp;gt;&lt;br /&gt;
Fájl:Xeon-phi-family-rwd.png.rendition.intel.web.720.405.png|&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-  align=&amp;quot;center&amp;quot;&lt;br /&gt;
! CPU&lt;br /&gt;
! threads/Core&lt;br /&gt;
!max threads&lt;br /&gt;
! CPU Freq&lt;br /&gt;
! Memória&lt;br /&gt;
! L1 cache&lt;br /&gt;
! L2 cache&lt;br /&gt;
! Interfész&lt;br /&gt;
! Ptot&lt;br /&gt;
! Számítási kapacitás&lt;br /&gt;
|-  align=&amp;quot;center&amp;quot;&lt;br /&gt;
| 61&lt;br /&gt;
| 4 Hardver&lt;br /&gt;
| 244&lt;br /&gt;
| 1238 GHz&lt;br /&gt;
| 16GB GDDR5&lt;br /&gt;
| 32KB/core&lt;br /&gt;
| 512KB/core&lt;br /&gt;
| PCIe&lt;br /&gt;
| 300W&lt;br /&gt;
| 1,2 TFlop/s dupla pontosság&lt;br /&gt;
|-  align=&amp;quot;center&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
| OpenMP-nél csak '''240'''&lt;br /&gt;
|&lt;br /&gt;
| ~100 cycles&lt;br /&gt;
~174 GB/s&lt;br /&gt;
| ~3 cycles&lt;br /&gt;
| ~10 cycles&lt;br /&gt;
| ~7 GB/s&lt;br /&gt;
~1 microsecond&lt;br /&gt;
|&lt;br /&gt;
| 2,4 TFlop/s egyszeres pontosság&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Az Intel® 1. generációs Xeon Phi™ koprocesszorában található 61 processzor ugyanazt a 16GB memóriát éri el, de semmilyen módon se fér hozzá közvetlenül a hoszt gép fizikai memóriájához.&amp;lt;br /&amp;gt;&lt;br /&gt;
[[Budapest2_klaszter|Budapest2]] és [[HPC#Szupersz.C3.A1m.C3.ADt.C3.B3g.C3.A9peink_.C3.B6sszehasonl.C3.ADt.C3.A1sa|Debrecen3-Phi]] gépeinkben számítási nodonként 2-2 Phi™ koprocesszor található. &amp;lt;br/&amp;gt;&lt;br /&gt;
A kártyákon speciális célú Linux kernel fut melyre a belépés limitáltan csak a rendszergazdák számára lehetséges SSH-n keresztül a hoszt gépről. Természetesen a kártyára belépve olyan érzésünk lehet, mintha egy sok processzoros gépen dolgoznánk, ami rendelkezik rengeteg memóriával. Ez így is van, de a hatékonyabb kihasználás érdekében az SSH-n keresztüli elérés nem lehetséges, csak a Slurm segítségével valamint a programozási nyelvek által támogatott csatornákon keresztül. Ennek részletezése lentebb megtalálható.&lt;br /&gt;
&lt;br /&gt;
==Használati módok==&lt;br /&gt;
A koprocesszor használatai módjait két csoportba lehet sorolni. A '''natív''' és az '''offload''' használatot.  &amp;lt;br/&amp;gt;&lt;br /&gt;
Ezen használati módokról a lenti fejezetekben több részletet is megtudhat. Az itt megfogalmazott &amp;quot;támogatás&amp;quot; csak a mi infrastruktúránkra érvényes! &amp;lt;br /&amp;gt;&lt;br /&gt;
A bemutatott példák ''c++'' nyelven vannak, de természetesen ''c'' és ''fortran'' esetén is megtalálható az ekvivalens módszer.&lt;br /&gt;
&lt;br /&gt;
===a) nem támogatott mód===&lt;br /&gt;
====közvetlen natív használat====&lt;br /&gt;
A natív mód arra szolgál, hogy a MIC kártyára  lefordított alkalmazásunkat felmásoljuk a kártyára, majd belépünk SSH-n a kártyára és azon futtatjuk a bináris kódunkat. &amp;lt;br /&amp;gt;&lt;br /&gt;
Ez a legegyszerűbb használati mód, ugyanis nincs más dolgunk, csak a '''-mmic''' kapcsolót használni a fordításnál és a fordító egy máris a MIC architektúrára kész bináris készít nekünk. &amp;lt;br/&amp;gt;&lt;br /&gt;
Workflow a következő lenne:&lt;br /&gt;
# Alkalmazás megírása. például [[Média:hello.cc|''hello.cc'']]&lt;br /&gt;
# Alkalmazás fordítása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;icpc -o hello-MIC -mmic hello.cc&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Másolás a MIC kártyára &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;scp hello-MIC mic0:&amp;lt;/pre&amp;gt;&lt;br /&gt;
# belépés a kártyára &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;ssh mic0&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Alkalmazás futtatása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;./hello-MIC&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===b) támogatott, de nem preferált használat===&lt;br /&gt;
====automatikus kódátvitel====&lt;br /&gt;
Ebben az esetben nem kell átmásolnunk a bináris alkalmazásunkat a kártyára sőt még csak be se kell lépnünk, mert ezt a két lépést egy beépített tool végzi el. &amp;lt;br/&amp;gt;&lt;br /&gt;
A tool eléréséhez szükséges parancs neve: '''micnativeloadex'''&lt;br /&gt;
&lt;br /&gt;
Ebben az esetben a workflow a következőképpen módosul:&lt;br /&gt;
# Alkalmazás megírása. például [[Média:hello.cc|''hello.cc'']]&lt;br /&gt;
# Alkalmazás fordítása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;icpc -o hello-MIC -mmic hello.cc&amp;lt;/pre&amp;gt;&lt;br /&gt;
# A tool futásához szükséges library-k megadása &amp;lt;br&amp;gt; &amp;lt;pre&amp;gt;export SINK_LD_LIBRARY_PATH=/opt/intel/composerxe/compiler/lib/mic&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Alkalmazás futtatása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;micnativeloadex hello-MIC&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| BUDAPEST2[cn10] phi (0)$ export SINK_LD_LIBRARY_PATH=/opt/intel/composerxe/compiler/lib/mic &amp;lt;br/&amp;gt;BUDAPEST2[cn10] phi (0)$ micnativeloadex hello-MIC &amp;lt;br/&amp;gt;Hello world! I have 244 logical cores.&amp;lt;br/&amp;gt;BUDAPEST2[cn10] phi (0)$&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===c) támogatott módok===&lt;br /&gt;
====Offload====&lt;br /&gt;
Az Offload modell esetén szükséges a forráskód módosítása, mégpedig úgy, hogy azon kódrészletet, amit a koprocesszoron szeretnénk futtatni, ki kell egészíteni egy fordítási direktívával. Az alkalmazás fordításakor elegendő csak a CPU-ra való binárist készíteni mivel a modell végzi a megfelelő forrás részlet mozgatását és futtatását.&amp;lt;br /&amp;gt;&lt;br /&gt;
[[Fájl:offload.png|Offload modell]] &amp;lt;br /&amp;gt;&lt;br /&gt;
Példánkban jól látható, hogy a második kiíratás már a koprocesszoron fut.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
int main(int argc, char * argv[]) {&lt;br /&gt;
  printf(&amp;quot;Hello world from host!\n&amp;quot;);&lt;br /&gt;
#pragma offload target(mic)&lt;br /&gt;
  {&lt;br /&gt;
    printf(&amp;quot;Hello world from coprocessor!\n&amp;quot;); fflush(stdout);&lt;br /&gt;
  }&lt;br /&gt;
  printf(&amp;quot;Bye\n&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ebben a példában az operációs rendszer választja ki, hogy melyik Phi kártyán fusson le a kódrészlet. Amennyiben mi szeretnénk explicite megmondani, hogy melyik kártyán fusson, akkor a fordítási direktívában a mic kulcsszó után kell tenni egy &amp;quot;:&amp;quot;-t és a kártya sorszámát [mi esetünkben 0 vagy 1]. Ekkor így nézne ki a direktíva: ''#pragma offload target(mic:1)''&lt;br /&gt;
&lt;br /&gt;
Amennyiben az offload szekcióban függvény hívás is szerepel, akkor szükségeltetik az függvény futásához szükséges változók adatterületének átmásolása a Phi kártyára, majd a lefutás után visszamásolni a hoszt gépre. Ezen másolást az ''offload_attribute'' ''push'' és ''pop'' paraméterei segítségével határozhatjuk meg. &amp;lt;br /&amp;gt;&lt;br /&gt;
A következő mátrix-vektor szorzás példánál az A mátrix globális változó, míg a b és c vektorok a függvény paraméterei.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//A változók felmásolásra kerülnek a Xeon Phi-re&lt;br /&gt;
#pragma offload_attribute(push, target(mic))&lt;br /&gt;
  double* A;&lt;br /&gt;
  void MatrixVector(double *b, double *c, double m, double n) {&lt;br /&gt;
    ...&lt;br /&gt;
    c[] = A[..]*b[]&lt;br /&gt;
  }&lt;br /&gt;
// felmásolt objektumok visszamásolása&lt;br /&gt;
#pragma offload_attribute(pop)&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
  ...&lt;br /&gt;
#pragma offload target(mic) in(A[0:n*m])&lt;br /&gt;
// így is meg lehetne adni: #pragma offload target(mic:0) in(A : length(n*m))	        &lt;br /&gt;
  {&lt;br /&gt;
    MatrixVector(b, c, m, n);&lt;br /&gt;
  }&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ekkor minden memória területet 2x kell másolni, egyszer fel a kártyára, majd másodszor vissza a hoszt gépre, pedig elegendő lenne csak az eredmény vektort visszamásolni. Ezen optimalizálással egy későbbi fejezetben foglalkozunk. &amp;lt;p /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Futás során kaphatunk részletes riportot is, hogy pontosan milyen adatmozgatások történnek az alkalmazás futása során. Ehhez nincs szükség másra csak az '''OFFLOAD_REPORT''' környezeti változó megfelelő beállítása. A beállított értékek 1, 2 vagy 3 lehetnek, attól függően mennyire részletes riportot szeretnénk kapni. &amp;lt;br /&amp;gt;&lt;br /&gt;
beállítás:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export OFFLOAD_REPORT=3&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
példa kimenet a hello alkalmazásra:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Hello world from host!&lt;br /&gt;
[Offload] [MIC 0] [File]                    hello_offload.cc&lt;br /&gt;
[Offload] [MIC 0] [Line]                    5&lt;br /&gt;
[Offload] [MIC 0] [Tag]                     Tag 0&lt;br /&gt;
Hello world from coprocessor!&lt;br /&gt;
[Offload] [HOST]  [Tag 0] [CPU Time]        0.554653(seconds)&lt;br /&gt;
[Offload] [MIC 0] [Tag 0] [CPU-&amp;gt;MIC Data]   0 (bytes)&lt;br /&gt;
[Offload] [MIC 0] [Tag 0] [MIC Time]        0.000140(seconds)&lt;br /&gt;
[Offload] [MIC 0] [Tag 0] [MIC-&amp;gt;CPU Data]   0 (bytes)&lt;br /&gt;
Bye&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Offload + OpenMP====&lt;br /&gt;
Amennyiben nem csak 1 processzort szeretnénk használni a Phi kártyánkon és az alkalmazásunkban használt számítás megengedi a párhuzamosítást, akkor az OpenMP segítségével párhuzamosíthatjuk a megfelelő számítást. &amp;lt;br /&amp;gt;&lt;br /&gt;
[[Fájl:Offload+openmp.png|Offload modell és OpenMP]] &amp;lt;br /&amp;gt;&lt;br /&gt;
Példánk maradjon ugyanúgy a mátrix-vektor szorzás. Amennyiben a teljes kártya adta kapacitást ki szeretnénk használni, akkor az OMP threadek számát 240-re kell beállítani. Azért nem 244-re, mivel 1 CPU-t dedikáltan csak a kommunikációra használ a rendszer és 1 CPU 4 hardver threaddel rendelkezik.&lt;br /&gt;
Ekkor a példánk így változik:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;omp.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#pragma offload_attribute(push, target(mic))&lt;br /&gt;
void multiply(int n, int m, double* A, double* b, double* c){&lt;br /&gt;
  const int nCPUs = omp_get_max_threads();   //240&lt;br /&gt;
  omp_set_num_threads(nCPUs);&lt;br /&gt;
#pragma omp parallel for schedule(dynamic, 1)&lt;br /&gt;
  for ( int i = 0 ; i &amp;lt; m ; i++)&lt;br /&gt;
     for ( int j = 0 ; j &amp;lt; n ; j++)&lt;br /&gt;
       c[i] += A[i*n+j] * b[j];&lt;br /&gt;
}&lt;br /&gt;
#pragma offload_attribute(pop)&lt;br /&gt;
&lt;br /&gt;
int main(){&lt;br /&gt;
  double * A = (double*) malloc(sizeof(double)*n*m);&lt;br /&gt;
  ...&lt;br /&gt;
#pragma offload target(mic) in(A:length(n*m))&lt;br /&gt;
     multiply(n, m, A, b, c);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Az alkalmazás fordításánál a fordítónak meg kell adni, hogy használhja az openmp library-ket is. Ezt a '''-qopenmp''' kapcsolóval tudjuk elérni.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
icpc -o matrix-CPU -qopenmp matrix.cc&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====MPI + Offload + OpenMP====&lt;br /&gt;
Abban az esetben, ha 1 számítási node nem elég, MPI segítségével össze tudunk kötni több nodet, heterogén rendszert alkotva, és azokon alkalmazható a fenti Offload + OpenMP modell. &amp;lt;br /&amp;gt;&lt;br /&gt;
[[Fájl:Mpi+offload+openmp.png|MPI elosztás valamint Offload modell és OpenMP]] &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Alkalmazások optimalizálása a kártyára==&lt;br /&gt;
Ebben a fejezetben optimalizálási lehetőségeket mutatunk be, melyek segítségül szolgálhatnak az egyes alkalmazások hatékonyabb futása érdekében ezzel jobban kihasználni a rendelkezésre álló erőforrások adta számítási kapacitás.&lt;br /&gt;
&lt;br /&gt;
===Környezeti változók===&lt;br /&gt;
Lehetséges csak a Phi kártyákon használt környezeti változók beállítása, amit az alkalmazásunk használni is tud. &amp;lt;br /&amp;gt;&lt;br /&gt;
Példánkban az OpenMP esetén használható szálak számát állíthatjuk be:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export MIC_ENV_PREFIX=XEONPHI&lt;br /&gt;
export OMP_NUM_THREADS=12&lt;br /&gt;
export XEONPHI_OMP_NUM_THREADS=240&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
A beállított prefix tetszőleges karaktersorozat lehet, a lényeg, hogy ezt a beállított prefixet használjuk a környezeti változók esetén is!&lt;br /&gt;
 &lt;br /&gt;
===Hosszredukció===&lt;br /&gt;
Többnyire a fordítók, ha nem mondjuk meg explicite, a dupla pontosságú változatot választják a lehetséges változó- és függvény típusok közül. Amennyiben a számításunk nem igényli a dupla pontosságot, akkor használjuk a forrásban mindenhol az adott típus vagy függvény egyszeres pontosságú változatát, mellyel jelentős teljesítmény növekedést tudunk elérni. Továbbá az egyes műveletek futási ideje nem egyforma, így a műveletet helyettesíthetjük más művelettel, ami gyorsabb, akkor ismét javíthatunk a hatékonyságon. &amp;lt;br /&amp;gt;&lt;br /&gt;
Íme pár példa, amit alkalmazni lehet:&lt;br /&gt;
* &amp;quot;''/''&amp;quot; művelet helyett &amp;quot;''*''&amp;quot; -t használjunk, mert azt gyorsabban végre tudja hajtani a processzor&lt;br /&gt;
* ''pow'' =&amp;gt; ''1.0 / sqrt()''    , tetszőleges hatványozás, pl.: 3/2-ik hatvány&lt;br /&gt;
* hardver áltatl támogatott műveletek:&lt;br /&gt;
** ''exp'' =&amp;gt; ''exp2''&lt;br /&gt;
** ''log'' =&amp;gt; ''log2''&lt;br /&gt;
* dupla pontosság -&amp;gt; egyszeres pontosság lebegőpontos számok és műveletek esetén &lt;br /&gt;
** ''1.0'' =&amp;gt; ''1.0f''&lt;br /&gt;
** ''pow'' =&amp;gt; ''powf''&lt;br /&gt;
&lt;br /&gt;
===Vektorizáció===&lt;br /&gt;
A vektorizáció azon fordítási időben eldönthető optimalizációs faktor, amely nagy mértékben függ attól a hardver felépítésétől, amire éppen a fordítás történni fog. Legfontosabb befolyásoló tényezők a processzor cache méretei illetve a vektor műveletekért felelős aritmetikai egységének jellemzői mivel ezeket használja fel a fordító az optimalizálási folyamata során. &amp;lt;br /&amp;gt;&lt;br /&gt;
Az automatikus vektorizáció minimum -O2 szinttől érvényes, de mivel általában ez a szint az alapértelmezett, ezért minden fordításnál alkalmazásra kerül. &amp;lt;br /&amp;gt;&lt;br /&gt;
A fordító programnak a következő kapcsolókat lehet megadni, melyek segítenek a minél jobb vektorizációban:&lt;br /&gt;
* ''-qopt-report=&amp;lt;szám&amp;gt;'' ; az optimalizációs jelentés részletességét lehet megadni 0-tól 5-ig, ahol az 5-s szint a legrészletesebbet jelenti&lt;br /&gt;
* ''-qopt-report-phase:vec'' ; a vektorizációról szeretnék jelentést kapni&lt;br /&gt;
* ''-opt-report-file=&amp;lt;fájlnév&amp;gt;'' ; megadható a riportfájl neve&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
icpc autovec.cc -mmic -qopt-report=5 -opt-report-file=vectorization.oMIC.optrpt -qopt-report-phase:vec&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====SIMD====&lt;br /&gt;
Az adatok hatékonyabb feldolgozására az egyik megvalósítás a SIMD (Single Instruction Multiple Data), amikor is a különálló adatokat úgy kezeli, mintha az vektorok volnának és azon végzi el a vektor-műveletet. Ezzel egy a hardver által támogatott műveletet végzünk egy lépésben egy nagyobb adatmennyiségen, ezzel párhuzamosítva a feldolgozási folyamatot. Ezzel a módszerrel nagyon jó adat szintű párhuzamosítást lehet elérni.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Fájl:Simd.png|SIMD]]&lt;br /&gt;
&lt;br /&gt;
Főbb támogatott műveletek:&lt;br /&gt;
* aritmetikai műveletek, bináris operátorok&lt;br /&gt;
* összehasonlítások&lt;br /&gt;
* konverziók és típus kasztolások&lt;br /&gt;
* bit-műveletek: NOT, AND, OR, XOR, XAND&lt;br /&gt;
&lt;br /&gt;
Használata: '''simd''' fordítási direktívával lehetséges &amp;lt;br /&amp;gt;&lt;br /&gt;
Például:&lt;br /&gt;
* egyszerű vektorizáció&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma simd&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
  A[i] += B[i];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* adat párhuzamosítás OpenMP segítségével&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for simd&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
  DoSomeWork(A[i]);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====AoS -&amp;gt; SoA====&lt;br /&gt;
A kiindulás probléma az, hogy a struktúrákat tartalmazó vektort nem lehet jól vektorizálni, mivel a cache-ben tárolt adatok nagy többsége feleslegesen van bent, mivel azon adatokat csak egy későbbi ciklus futás során fogja az alkalmazás feldolgozni. Tehát ezen adatok jelenleg feleslegesen foglalják a cache területet, ahelyett, hogy hasznos adatot tárolnánk ott.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
struct MyStructure {&lt;br /&gt;
  float x, y, z;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void My_Function(cons int nItem, MyStructure* const Items, const float delta) {&lt;br /&gt;
  for (int i = 0; i &amp;lt; nItem; i++) {&lt;br /&gt;
    ...&lt;br /&gt;
    for (int j = 0; j &amp;lt; nItem; j++) {&lt;br /&gt;
      const float dx = Items[j].x - Items[i].x;&lt;br /&gt;
      const float dy = Items[j].y - Items[i].y;&lt;br /&gt;
      const float dz = Items[j].z - Items[i].z;&lt;br /&gt;
      ...  &lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  ... &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A vektorban a struktúrák tárolása helyett minden elemnek egy dedikált vektort allokálunk és így könnyen tudja a fordító vektorizálni azt. Természetesen segítségként a vektor feldolgozását &amp;quot;tömbösítettük&amp;quot; a tileSize méret megadásával illetve a ''unroll'' fordítási direktíva segítségével.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
struct MyStructureSet {&lt;br /&gt;
  float *x, *y, *z;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void My_Function(cons int nItem, MyStructure* const Items, const float delta) {&lt;br /&gt;
  const int tileSize = 16;&lt;br /&gt;
#pragma omp parallel for schedule(guided)&lt;br /&gt;
  for (int ii = 0; ii &amp;lt; nItem; ii+= tileSize) {&lt;br /&gt;
    ...&lt;br /&gt;
#pragma unroll(tileSize)&lt;br /&gt;
    for (int j = 0; j &amp;lt; nItem; j++) {&lt;br /&gt;
#pragma unroll(tileSize)&lt;br /&gt;
      for (int i = ii; i &amp;lt; ii + tileSize; i++) {&lt;br /&gt;
        const float dx = Items.x[j] - Items.x[i];&lt;br /&gt;
        const float dy = Items.y[j] - Items.y[i];&lt;br /&gt;
        const float dz = Items.z[j] - Items.z[i];&lt;br /&gt;
        ...&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  ... &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Hatékony hossz választás====&lt;br /&gt;
Olyan esetben jelent előnyt, ha például az n*n-es mátrix esetén n nem osztható 16-val, mert akkor a vektorizáció nem teljesen hajtható végre. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// n%16 != 0&lt;br /&gt;
float* A = _mm_malloc(sizeof(float)*n*n, 64);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Ilyen esetben mindig marad egy kis vektor részlet, amire elemenként kell elvégezni a műveletet.&amp;lt;br /&amp;gt;&lt;br /&gt;
A megoldást az jelenti, hogy a memória allokálást kiterjesztjük és nagyobb memória területet foglalunk a változónak, így már alkalmazható lesz a vektorizáció, mivel a maradvány területen is el tudjuk végezni a műveletet egy lépésben.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int lda=n;&lt;br /&gt;
if (n % 16 != 0) lda += (16 - n%16); // most lda%16==0&lt;br /&gt;
float* A = _mm_malloc(sizeof(float)*n*lda, 64);&lt;br /&gt;
&lt;br /&gt;
// használat&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
  for (int j = 0; j &amp;lt; n; j++)&lt;br /&gt;
    A[i*lda + j] = ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====&amp;quot;Héj ciklusok&amp;quot; kiküszöbölése====&lt;br /&gt;
Ciklusok esetén lehetséges, hogy a vektorizációt nem teljes mértékben ismeri fel a fordító program. &lt;br /&gt;
Ekkor segíthetünk a fordítónak egy fordítási direktívával ('''#pragma vector aligned'''), ha teljes mértékben biztosak vagyunk, hogy a ciklusunk vektorizálható!&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma vector aligned&lt;br /&gt;
  for (int j = 0; j &amp;lt; n; j++)&lt;br /&gt;
    A[i*lda + j] -= ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Ettől jelentős futás idejű javulást érhetünk el, mivel ezzel a #pragma-val kikapcsoltuk a futás idejű ellenőrzést, de előfordulhatnak olyan esetek, főleg amikor nem jól mértük fel a ciklusunkat és nem sikerült a vektorizálás, hogy ''Segmentation fault'' hibára fogunk futni.&lt;br /&gt;
&lt;br /&gt;
====Regularizáció====&lt;br /&gt;
Van egy ciklusunk, ami egy vektor elemeit módosítja. A processzor az adatokat &amp;quot;csokorba szedve&amp;quot; emeli be a belső kis méretű cache-be. Egyszerre természetesen több vektor elem is bekerül a a cache-be ezzel gyorsítva a feldolgozást. Ezen vektor iterációk valahogyan ráilleszkedik a vektorra.&lt;br /&gt;
&lt;br /&gt;
[[Fájl:regularization.png|Vektor regularizáció]]&lt;br /&gt;
&lt;br /&gt;
Abban az esetben, amikor ez az illesztés nem teljesen tökéletes (lsd. piros elemek), keletkeznem héj ciklusok, melyeket a regularizációval is eliminálni lehet. &amp;lt;br /&amp;gt;&lt;br /&gt;
A feladat eredetileg valahogyan így nézhet ki:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
for (int i = 0; i&amp;lt;n; i++)&lt;br /&gt;
  A[i]=...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ez a következő átalakítással már alkalmas vektorizációra és nem lesz &amp;quot;peel loop&amp;quot; probléma.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
for (int b=0; b&amp;lt;n; b++) {&lt;br /&gt;
  const int jStart = b- b%16; // mindig osztható 16-val&lt;br /&gt;
  ...&lt;br /&gt;
#pragma simd&lt;br /&gt;
  for (int j = jStart; j&amp;lt;n; j++)&lt;br /&gt;
    A[j]=...&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Párhuzamosítás===&lt;br /&gt;
Több féle mód létezik a párhuzamosításra. Korábban említettük a SIMD technikát, ami adatpárhuzamosításra való 1 processzoron belül, de létezik még processzorok közti párhuzamosítás, amit az OpenMP segítségével tudunk hatékonyan megvalósítani. Amennyiben gépek közti párhuzamosításra, üzenek küldésre van szükség, akkor az MPI-t kell segítségül hívni. Általános megoldás nem létezik minden feladatra, mert MPI is alkalmas lehet gépen belüli párhuzamosításra, nem csak az OpenMP. Amit mérlegelni kell, hogy mi az amire az alkalmazásnak szükséges van illetve mekkora kommunikációs overhead keletkezik, ha lecseréljük az OpenMP-t MPI-ra. Általában az egymástól független számításokat OpenMP-vel, míg a kommunikációt igénylő feladatokra MPI-t szoktak használni.&lt;br /&gt;
&lt;br /&gt;
====Szálak párhuzamosítása====&lt;br /&gt;
Az OpenMP-nek környezeti változók segítségével megadható a párhuzamosan futtatható szálak száma. Ezt a következő paranccsal tudjuk megtenni, ahol 5-re állítjuk.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export OMP_NUM_THREADS=5&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Forrásban a párhuzamos szálakat a ''#pragma omp parallel'' segítségével adhatjuk meg.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for              	//külső for szálak párhuzamosítása&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
#pragma simd				// vektorizáció a belső cikluson&lt;br /&gt;
  for (int j = 0; j &amp;lt; m; j++)&lt;br /&gt;
    My_Function(A[i][j]);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A fordítási direktívák össze is vonhatóak, ilyenkor 1 ciklus is elég.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for simd&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
    My_Function(A[i]);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=====Változók láthatósága, megosztása=====&lt;br /&gt;
Változók hatókörét a programozási nyelven kívül pragma-k segítségével is beállíthatjuk, hogy megosztott legyen a változó vagy a szálra nézve saját példány. Amennyiben nem definiáljuk felül, akkor minden változó megosztott. A felül definiálást a private és a shared paraméterekkel lehet megadni az OpenMP-nek.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int A, B, C;&lt;br /&gt;
&lt;br /&gt;
#pragma omp parallel private(A) shared(B)&lt;br /&gt;
{...}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Az itteni példában az B és C megosztott változó a szálak között, míg A-t minden szálnak lemásolja a futás kezdetén.&lt;br /&gt;
&lt;br /&gt;
====Párhuzamos szálak ütemezése====&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Szálak szinkronizációja====&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
	   * with mutexes		(it is very slow, waiting always)		#pragma omp parallel for		  for (int i = 0; i &amp;lt; n; i++) {		#pragma omp critical		    { // Only one thread at a time can execute this section		      total = total + i;		    }		  }	   * or with atomic operation		(it also [very] slow, lot of limitations)		#pragma omp parallel for		for (int i = 0; i &amp;lt; n; i++)		  { // Lightweight synchronization		#pragma omp atomic		    total += i;		  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=====Párhuzamos redukció=====&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
	     (it is fast)  BUT not for vector		int total = 0;		#pragma omp parallel for reduction(+: total)               //usage: reduction(operation: scalar)		for (int i = 0; i &amp;lt; n; i++) {		  total = total + i;		}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Ciklusok párhuzamosítása====&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
		#pragma omp parallel		{		  // Code placed here will be executed by all threads.		  ...		#pragma omp for [schedule (&amp;lt;mode&amp;gt;,&amp;lt;chunk&amp;gt;)		  for (int i = 0; i &amp;lt; n; i++) {		    // ... iterations will be distributed across available threads...		  }		  // ... code placed here will be executed by all threads		}	    modes: static, dynamic, guided	    chunk: integer value&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Ciklus kifejtés====&lt;br /&gt;
A technika már egy példa erejéig a vektorizációnál is látható, de ott nem került részletezésre. A megoldás lényege, hogy a kellően nagy ciklust szétdaraboljuk kisebb darabokra, amit már külön feldolgozhatóak, ezzel párhuzamosítva a folyamatokat. Az egy darab kiindulási ciklusból két darab egymásba ágyazott ciklust hozunk létre, ami ugyanazon a feladattéren dolgozik. &lt;br /&gt;
&lt;br /&gt;
Eredeti állapot:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++) {...}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Átírt változat:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int STRIP=1024;&lt;br /&gt;
for (int ii = 0; ii &amp;lt; n; ii += STRIP)&lt;br /&gt;
 for (int i = ii; i &amp;lt; ii+STRIP; i++) {...}&lt;br /&gt;
&lt;br /&gt;
// ha még van maradék elem, akkor azokon is végrehajtjuk a számításokat&lt;br /&gt;
if (n%STRIP != 0) {&lt;br /&gt;
  for (i=n-(n%STRIP)+1; i&amp;lt;n; i++) {...}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A darabok mérete (STRIP értéke) az adott rendszer és feladat függvényében változhat, tehát ez egyfajta &amp;quot;tuning&amp;quot; paraméter.&lt;br /&gt;
&lt;br /&gt;
====Automatikus ciklusösszevonás====&lt;br /&gt;
Az OpenMP támogatást nyújt több egymásba ágyazott ciklus &amp;quot;összevonására&amp;quot;. Ehhez csak a '''collaps''' fordítási direktívát és az összevonandó ciklusok számát kell megadni. &amp;lt;br /&amp;gt;&lt;br /&gt;
FONTOS: ekkor az automatikus vektorizáció kikapcsolásra kerül!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for collaps(2)&lt;br /&gt;
for (int i=0; i&amp;lt;n; i++)&lt;br /&gt;
  for( int j=0; j&amp;lt;m; j++){...}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A fneti példában a 2db for ciklis kerül párhuzamosításra úgy, hogy a fordítás során 1db ciklus generálódik belőle, ami a következő paraméterezéssel ekvivalens:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for&lt;br /&gt;
for (int c=0; c &amp;lt; n*m; c++) {&lt;br /&gt;
  int i = c/n;&lt;br /&gt;
  int j = c%n;&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Szigeti(AT)niif.hu</name></author>	</entry>

	<entry>
		<id>https://wiki.niif.hu/index.php?title=Intel_Xeon_Phi&amp;diff=3752</id>
		<title>Intel Xeon Phi</title>
		<link rel="alternate" type="text/html" href="https://wiki.niif.hu/index.php?title=Intel_Xeon_Phi&amp;diff=3752"/>
				<updated>2016-05-05T11:54:23Z</updated>
		
		<summary type="html">&lt;p&gt;Szigeti(AT)niif.hu: /* =Szálak párhuzamosítása */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Az oldalon található információk segítséget nyújtanak a HPC felhasználóinknak, hogy alkalmazásaikat miként portolhatják valamint optimalizálhatják a hatékonyabb futást szem előtt tartva Intel Xeon Phi koprocesszor kártyákon.&lt;br /&gt;
&lt;br /&gt;
==Hardver bemutatása==&lt;br /&gt;
Intel® Xeon Phi™ Coprocessor 7120P&lt;br /&gt;
&amp;lt;gallery&amp;gt;&lt;br /&gt;
Fájl:Xeon-phi-family-rwd.png.rendition.intel.web.720.405.png|&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-  align=&amp;quot;center&amp;quot;&lt;br /&gt;
! CPU&lt;br /&gt;
! threads/Core&lt;br /&gt;
!max threads&lt;br /&gt;
! CPU Freq&lt;br /&gt;
! Memória&lt;br /&gt;
! L1 cache&lt;br /&gt;
! L2 cache&lt;br /&gt;
! Interfész&lt;br /&gt;
! Ptot&lt;br /&gt;
! Számítási kapacitás&lt;br /&gt;
|-  align=&amp;quot;center&amp;quot;&lt;br /&gt;
| 61&lt;br /&gt;
| 4 Hardver&lt;br /&gt;
| 244&lt;br /&gt;
| 1238 GHz&lt;br /&gt;
| 16GB GDDR5&lt;br /&gt;
| 32KB/core&lt;br /&gt;
| 512KB/core&lt;br /&gt;
| PCIe&lt;br /&gt;
| 300W&lt;br /&gt;
| 1,2 TFlop/s dupla pontosság&lt;br /&gt;
|-  align=&amp;quot;center&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
| OpenMP-nél csak '''240'''&lt;br /&gt;
|&lt;br /&gt;
| ~100 cycles&lt;br /&gt;
~174 GB/s&lt;br /&gt;
| ~3 cycles&lt;br /&gt;
| ~10 cycles&lt;br /&gt;
| ~7 GB/s&lt;br /&gt;
~1 microsecond&lt;br /&gt;
|&lt;br /&gt;
| 2,4 TFlop/s egyszeres pontosság&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Az Intel® 1. generációs Xeon Phi™ koprocesszorában található 61 processzor ugyanazt a 16GB memóriát éri el, de semmilyen módon se fér hozzá közvetlenül a hoszt gép fizikai memóriájához.&amp;lt;br /&amp;gt;&lt;br /&gt;
[[Budapest2_klaszter|Budapest2]] és [[HPC#Szupersz.C3.A1m.C3.ADt.C3.B3g.C3.A9peink_.C3.B6sszehasonl.C3.ADt.C3.A1sa|Debrecen3-Phi]] gépeinkben számítási nodonként 2-2 Phi™ koprocesszor található. &amp;lt;br/&amp;gt;&lt;br /&gt;
A kártyákon speciális célú Linux kernel fut melyre a belépés limitáltan csak a rendszergazdák számára lehetséges SSH-n keresztül a hoszt gépről. Természetesen a kártyára belépve olyan érzésünk lehet, mintha egy sok processzoros gépen dolgoznánk, ami rendelkezik rengeteg memóriával. Ez így is van, de a hatékonyabb kihasználás érdekében az SSH-n keresztüli elérés nem lehetséges, csak a Slurm segítségével valamint a programozási nyelvek által támogatott csatornákon keresztül. Ennek részletezése lentebb megtalálható.&lt;br /&gt;
&lt;br /&gt;
==Használati módok==&lt;br /&gt;
A koprocesszor használatai módjait két csoportba lehet sorolni. A '''natív''' és az '''offload''' használatot.  &amp;lt;br/&amp;gt;&lt;br /&gt;
Ezen használati módokról a lenti fejezetekben több részletet is megtudhat. Az itt megfogalmazott &amp;quot;támogatás&amp;quot; csak a mi infrastruktúránkra érvényes! &amp;lt;br /&amp;gt;&lt;br /&gt;
A bemutatott példák ''c++'' nyelven vannak, de természetesen ''c'' és ''fortran'' esetén is megtalálható az ekvivalens módszer.&lt;br /&gt;
&lt;br /&gt;
===a) nem támogatott mód===&lt;br /&gt;
====közvetlen natív használat====&lt;br /&gt;
A natív mód arra szolgál, hogy a MIC kártyára  lefordított alkalmazásunkat felmásoljuk a kártyára, majd belépünk SSH-n a kártyára és azon futtatjuk a bináris kódunkat. &amp;lt;br /&amp;gt;&lt;br /&gt;
Ez a legegyszerűbb használati mód, ugyanis nincs más dolgunk, csak a '''-mmic''' kapcsolót használni a fordításnál és a fordító egy máris a MIC architektúrára kész bináris készít nekünk. &amp;lt;br/&amp;gt;&lt;br /&gt;
Workflow a következő lenne:&lt;br /&gt;
# Alkalmazás megírása. például [[Média:hello.cc|''hello.cc'']]&lt;br /&gt;
# Alkalmazás fordítása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;icpc -o hello-MIC -mmic hello.cc&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Másolás a MIC kártyára &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;scp hello-MIC mic0:&amp;lt;/pre&amp;gt;&lt;br /&gt;
# belépés a kártyára &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;ssh mic0&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Alkalmazás futtatása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;./hello-MIC&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===b) támogatott, de nem preferált használat===&lt;br /&gt;
====automatikus kódátvitel====&lt;br /&gt;
Ebben az esetben nem kell átmásolnunk a bináris alkalmazásunkat a kártyára sőt még csak be se kell lépnünk, mert ezt a két lépést egy beépített tool végzi el. &amp;lt;br/&amp;gt;&lt;br /&gt;
A tool eléréséhez szükséges parancs neve: '''micnativeloadex'''&lt;br /&gt;
&lt;br /&gt;
Ebben az esetben a workflow a következőképpen módosul:&lt;br /&gt;
# Alkalmazás megírása. például [[Média:hello.cc|''hello.cc'']]&lt;br /&gt;
# Alkalmazás fordítása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;icpc -o hello-MIC -mmic hello.cc&amp;lt;/pre&amp;gt;&lt;br /&gt;
# A tool futásához szükséges library-k megadása &amp;lt;br&amp;gt; &amp;lt;pre&amp;gt;export SINK_LD_LIBRARY_PATH=/opt/intel/composerxe/compiler/lib/mic&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Alkalmazás futtatása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;micnativeloadex hello-MIC&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| BUDAPEST2[cn10] phi (0)$ export SINK_LD_LIBRARY_PATH=/opt/intel/composerxe/compiler/lib/mic &amp;lt;br/&amp;gt;BUDAPEST2[cn10] phi (0)$ micnativeloadex hello-MIC &amp;lt;br/&amp;gt;Hello world! I have 244 logical cores.&amp;lt;br/&amp;gt;BUDAPEST2[cn10] phi (0)$&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===c) támogatott módok===&lt;br /&gt;
====Offload====&lt;br /&gt;
Az Offload modell esetén szükséges a forráskód módosítása, mégpedig úgy, hogy azon kódrészletet, amit a koprocesszoron szeretnénk futtatni, ki kell egészíteni egy fordítási direktívával. Az alkalmazás fordításakor elegendő csak a CPU-ra való binárist készíteni mivel a modell végzi a megfelelő forrás részlet mozgatását és futtatását.&amp;lt;br /&amp;gt;&lt;br /&gt;
[[Fájl:offload.png|Offload modell]] &amp;lt;br /&amp;gt;&lt;br /&gt;
Példánkban jól látható, hogy a második kiíratás már a koprocesszoron fut.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
int main(int argc, char * argv[]) {&lt;br /&gt;
  printf(&amp;quot;Hello world from host!\n&amp;quot;);&lt;br /&gt;
#pragma offload target(mic)&lt;br /&gt;
  {&lt;br /&gt;
    printf(&amp;quot;Hello world from coprocessor!\n&amp;quot;); fflush(stdout);&lt;br /&gt;
  }&lt;br /&gt;
  printf(&amp;quot;Bye\n&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ebben a példában az operációs rendszer választja ki, hogy melyik Phi kártyán fusson le a kódrészlet. Amennyiben mi szeretnénk explicite megmondani, hogy melyik kártyán fusson, akkor a fordítási direktívában a mic kulcsszó után kell tenni egy &amp;quot;:&amp;quot;-t és a kártya sorszámát [mi esetünkben 0 vagy 1]. Ekkor így nézne ki a direktíva: ''#pragma offload target(mic:1)''&lt;br /&gt;
&lt;br /&gt;
Amennyiben az offload szekcióban függvény hívás is szerepel, akkor szükségeltetik az függvény futásához szükséges változók adatterületének átmásolása a Phi kártyára, majd a lefutás után visszamásolni a hoszt gépre. Ezen másolást az ''offload_attribute'' ''push'' és ''pop'' paraméterei segítségével határozhatjuk meg. &amp;lt;br /&amp;gt;&lt;br /&gt;
A következő mátrix-vektor szorzás példánál az A mátrix globális változó, míg a b és c vektorok a függvény paraméterei.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//A változók felmásolásra kerülnek a Xeon Phi-re&lt;br /&gt;
#pragma offload_attribute(push, target(mic))&lt;br /&gt;
  double* A;&lt;br /&gt;
  void MatrixVector(double *b, double *c, double m, double n) {&lt;br /&gt;
    ...&lt;br /&gt;
    c[] = A[..]*b[]&lt;br /&gt;
  }&lt;br /&gt;
// felmásolt objektumok visszamásolása&lt;br /&gt;
#pragma offload_attribute(pop)&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
  ...&lt;br /&gt;
#pragma offload target(mic) in(A[0:n*m])&lt;br /&gt;
// így is meg lehetne adni: #pragma offload target(mic:0) in(A : length(n*m))	        &lt;br /&gt;
  {&lt;br /&gt;
    MatrixVector(b, c, m, n);&lt;br /&gt;
  }&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ekkor minden memória területet 2x kell másolni, egyszer fel a kártyára, majd másodszor vissza a hoszt gépre, pedig elegendő lenne csak az eredmény vektort visszamásolni. Ezen optimalizálással egy későbbi fejezetben foglalkozunk. &amp;lt;p /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Futás során kaphatunk részletes riportot is, hogy pontosan milyen adatmozgatások történnek az alkalmazás futása során. Ehhez nincs szükség másra csak az '''OFFLOAD_REPORT''' környezeti változó megfelelő beállítása. A beállított értékek 1, 2 vagy 3 lehetnek, attól függően mennyire részletes riportot szeretnénk kapni. &amp;lt;br /&amp;gt;&lt;br /&gt;
beállítás:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export OFFLOAD_REPORT=3&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
példa kimenet a hello alkalmazásra:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Hello world from host!&lt;br /&gt;
[Offload] [MIC 0] [File]                    hello_offload.cc&lt;br /&gt;
[Offload] [MIC 0] [Line]                    5&lt;br /&gt;
[Offload] [MIC 0] [Tag]                     Tag 0&lt;br /&gt;
Hello world from coprocessor!&lt;br /&gt;
[Offload] [HOST]  [Tag 0] [CPU Time]        0.554653(seconds)&lt;br /&gt;
[Offload] [MIC 0] [Tag 0] [CPU-&amp;gt;MIC Data]   0 (bytes)&lt;br /&gt;
[Offload] [MIC 0] [Tag 0] [MIC Time]        0.000140(seconds)&lt;br /&gt;
[Offload] [MIC 0] [Tag 0] [MIC-&amp;gt;CPU Data]   0 (bytes)&lt;br /&gt;
Bye&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Offload + OpenMP====&lt;br /&gt;
Amennyiben nem csak 1 processzort szeretnénk használni a Phi kártyánkon és az alkalmazásunkban használt számítás megengedi a párhuzamosítást, akkor az OpenMP segítségével párhuzamosíthatjuk a megfelelő számítást. &amp;lt;br /&amp;gt;&lt;br /&gt;
[[Fájl:Offload+openmp.png|Offload modell és OpenMP]] &amp;lt;br /&amp;gt;&lt;br /&gt;
Példánk maradjon ugyanúgy a mátrix-vektor szorzás. Amennyiben a teljes kártya adta kapacitást ki szeretnénk használni, akkor az OMP threadek számát 240-re kell beállítani. Azért nem 244-re, mivel 1 CPU-t dedikáltan csak a kommunikációra használ a rendszer és 1 CPU 4 hardver threaddel rendelkezik.&lt;br /&gt;
Ekkor a példánk így változik:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;omp.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#pragma offload_attribute(push, target(mic))&lt;br /&gt;
void multiply(int n, int m, double* A, double* b, double* c){&lt;br /&gt;
  const int nCPUs = omp_get_max_threads();   //240&lt;br /&gt;
  omp_set_num_threads(nCPUs);&lt;br /&gt;
#pragma omp parallel for schedule(dynamic, 1)&lt;br /&gt;
  for ( int i = 0 ; i &amp;lt; m ; i++)&lt;br /&gt;
     for ( int j = 0 ; j &amp;lt; n ; j++)&lt;br /&gt;
       c[i] += A[i*n+j] * b[j];&lt;br /&gt;
}&lt;br /&gt;
#pragma offload_attribute(pop)&lt;br /&gt;
&lt;br /&gt;
int main(){&lt;br /&gt;
  double * A = (double*) malloc(sizeof(double)*n*m);&lt;br /&gt;
  ...&lt;br /&gt;
#pragma offload target(mic) in(A:length(n*m))&lt;br /&gt;
     multiply(n, m, A, b, c);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Az alkalmazás fordításánál a fordítónak meg kell adni, hogy használhja az openmp library-ket is. Ezt a '''-qopenmp''' kapcsolóval tudjuk elérni.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
icpc -o matrix-CPU -qopenmp matrix.cc&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====MPI + Offload + OpenMP====&lt;br /&gt;
Abban az esetben, ha 1 számítási node nem elég, MPI segítségével össze tudunk kötni több nodet, heterogén rendszert alkotva, és azokon alkalmazható a fenti Offload + OpenMP modell. &amp;lt;br /&amp;gt;&lt;br /&gt;
[[Fájl:Mpi+offload+openmp.png|MPI elosztás valamint Offload modell és OpenMP]] &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Alkalmazások optimalizálása a kártyára==&lt;br /&gt;
Ebben a fejezetben optimalizálási lehetőségeket mutatunk be, melyek segítségül szolgálhatnak az egyes alkalmazások hatékonyabb futása érdekében ezzel jobban kihasználni a rendelkezésre álló erőforrások adta számítási kapacitás.&lt;br /&gt;
&lt;br /&gt;
===Környezeti változók===&lt;br /&gt;
Lehetséges csak a Phi kártyákon használt környezeti változók beállítása, amit az alkalmazásunk használni is tud. &amp;lt;br /&amp;gt;&lt;br /&gt;
Példánkban az OpenMP esetén használható szálak számát állíthatjuk be:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export MIC_ENV_PREFIX=XEONPHI&lt;br /&gt;
export OMP_NUM_THREADS=12&lt;br /&gt;
export XEONPHI_OMP_NUM_THREADS=240&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
A beállított prefix tetszőleges karaktersorozat lehet, a lényeg, hogy ezt a beállított prefixet használjuk a környezeti változók esetén is!&lt;br /&gt;
 &lt;br /&gt;
===Hosszredukció===&lt;br /&gt;
Többnyire a fordítók, ha nem mondjuk meg explicite, a dupla pontosságú változatot választják a lehetséges változó- és függvény típusok közül. Amennyiben a számításunk nem igényli a dupla pontosságot, akkor használjuk a forrásban mindenhol az adott típus vagy függvény egyszeres pontosságú változatát, mellyel jelentős teljesítmény növekedést tudunk elérni. Továbbá az egyes műveletek futási ideje nem egyforma, így a műveletet helyettesíthetjük más művelettel, ami gyorsabb, akkor ismét javíthatunk a hatékonyságon. &amp;lt;br /&amp;gt;&lt;br /&gt;
Íme pár példa, amit alkalmazni lehet:&lt;br /&gt;
* &amp;quot;''/''&amp;quot; művelet helyett &amp;quot;''*''&amp;quot; -t használjunk, mert azt gyorsabban végre tudja hajtani a processzor&lt;br /&gt;
* ''pow'' =&amp;gt; ''1.0 / sqrt()''    , tetszőleges hatványozás, pl.: 3/2-ik hatvány&lt;br /&gt;
* hardver áltatl támogatott műveletek:&lt;br /&gt;
** ''exp'' =&amp;gt; ''exp2''&lt;br /&gt;
** ''log'' =&amp;gt; ''log2''&lt;br /&gt;
* dupla pontosság -&amp;gt; egyszeres pontosság lebegőpontos számok és műveletek esetén &lt;br /&gt;
** ''1.0'' =&amp;gt; ''1.0f''&lt;br /&gt;
** ''pow'' =&amp;gt; ''powf''&lt;br /&gt;
&lt;br /&gt;
===Vektorizáció===&lt;br /&gt;
A vektorizáció azon fordítási időben eldönthető optimalizációs faktor, amely nagy mértékben függ attól a hardver felépítésétől, amire éppen a fordítás történni fog. Legfontosabb befolyásoló tényezők a processzor cache méretei illetve a vektor műveletekért felelős aritmetikai egységének jellemzői mivel ezeket használja fel a fordító az optimalizálási folyamata során. &amp;lt;br /&amp;gt;&lt;br /&gt;
Az automatikus vektorizáció minimum -O2 szinttől érvényes, de mivel általában ez a szint az alapértelmezett, ezért minden fordításnál alkalmazásra kerül. &amp;lt;br /&amp;gt;&lt;br /&gt;
A fordító programnak a következő kapcsolókat lehet megadni, melyek segítenek a minél jobb vektorizációban:&lt;br /&gt;
* ''-qopt-report=&amp;lt;szám&amp;gt;'' ; az optimalizációs jelentés részletességét lehet megadni 0-tól 5-ig, ahol az 5-s szint a legrészletesebbet jelenti&lt;br /&gt;
* ''-qopt-report-phase:vec'' ; a vektorizációról szeretnék jelentést kapni&lt;br /&gt;
* ''-opt-report-file=&amp;lt;fájlnév&amp;gt;'' ; megadható a riportfájl neve&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
icpc autovec.cc -mmic -qopt-report=5 -opt-report-file=vectorization.oMIC.optrpt -qopt-report-phase:vec&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====SIMD====&lt;br /&gt;
Az adatok hatékonyabb feldolgozására az egyik megvalósítás a SIMD (Single Instruction Multiple Data), amikor is a különálló adatokat úgy kezeli, mintha az vektorok volnának és azon végzi el a vektor-műveletet. Ezzel egy a hardver által támogatott műveletet végzünk egy lépésben egy nagyobb adatmennyiségen, ezzel párhuzamosítva a feldolgozási folyamatot. Ezzel a módszerrel nagyon jó adat szintű párhuzamosítást lehet elérni.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Fájl:Simd.png|SIMD]]&lt;br /&gt;
&lt;br /&gt;
Főbb támogatott műveletek:&lt;br /&gt;
* aritmetikai műveletek, bináris operátorok&lt;br /&gt;
* összehasonlítások&lt;br /&gt;
* konverziók és típus kasztolások&lt;br /&gt;
* bit-műveletek: NOT, AND, OR, XOR, XAND&lt;br /&gt;
&lt;br /&gt;
Használata: '''simd''' fordítási direktívával lehetséges &amp;lt;br /&amp;gt;&lt;br /&gt;
Például:&lt;br /&gt;
* egyszerű vektorizáció&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma simd&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
  A[i] += B[i];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* adat párhuzamosítás OpenMP segítségével&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for simd&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
  DoSomeWork(A[i]);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====AoS -&amp;gt; SoA====&lt;br /&gt;
A kiindulás probléma az, hogy a struktúrákat tartalmazó vektort nem lehet jól vektorizálni, mivel a cache-ben tárolt adatok nagy többsége feleslegesen van bent, mivel azon adatokat csak egy későbbi ciklus futás során fogja az alkalmazás feldolgozni. Tehát ezen adatok jelenleg feleslegesen foglalják a cache területet, ahelyett, hogy hasznos adatot tárolnánk ott.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
struct MyStructure {&lt;br /&gt;
  float x, y, z;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void My_Function(cons int nItem, MyStructure* const Items, const float delta) {&lt;br /&gt;
  for (int i = 0; i &amp;lt; nItem; i++) {&lt;br /&gt;
    ...&lt;br /&gt;
    for (int j = 0; j &amp;lt; nItem; j++) {&lt;br /&gt;
      const float dx = Items[j].x - Items[i].x;&lt;br /&gt;
      const float dy = Items[j].y - Items[i].y;&lt;br /&gt;
      const float dz = Items[j].z - Items[i].z;&lt;br /&gt;
      ...  &lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  ... &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A vektorban a struktúrák tárolása helyett minden elemnek egy dedikált vektort allokálunk és így könnyen tudja a fordító vektorizálni azt. Természetesen segítségként a vektor feldolgozását &amp;quot;tömbösítettük&amp;quot; a tileSize méret megadásával illetve a ''unroll'' fordítási direktíva segítségével.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
struct MyStructureSet {&lt;br /&gt;
  float *x, *y, *z;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void My_Function(cons int nItem, MyStructure* const Items, const float delta) {&lt;br /&gt;
  const int tileSize = 16;&lt;br /&gt;
#pragma omp parallel for schedule(guided)&lt;br /&gt;
  for (int ii = 0; ii &amp;lt; nItem; ii+= tileSize) {&lt;br /&gt;
    ...&lt;br /&gt;
#pragma unroll(tileSize)&lt;br /&gt;
    for (int j = 0; j &amp;lt; nItem; j++) {&lt;br /&gt;
#pragma unroll(tileSize)&lt;br /&gt;
      for (int i = ii; i &amp;lt; ii + tileSize; i++) {&lt;br /&gt;
        const float dx = Items.x[j] - Items.x[i];&lt;br /&gt;
        const float dy = Items.y[j] - Items.y[i];&lt;br /&gt;
        const float dz = Items.z[j] - Items.z[i];&lt;br /&gt;
        ...&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  ... &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Hatékony hossz választás====&lt;br /&gt;
Olyan esetben jelent előnyt, ha például az n*n-es mátrix esetén n nem osztható 16-val, mert akkor a vektorizáció nem teljesen hajtható végre. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// n%16 != 0&lt;br /&gt;
float* A = _mm_malloc(sizeof(float)*n*n, 64);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Ilyen esetben mindig marad egy kis vektor részlet, amire elemenként kell elvégezni a műveletet.&amp;lt;br /&amp;gt;&lt;br /&gt;
A megoldást az jelenti, hogy a memória allokálást kiterjesztjük és nagyobb memória területet foglalunk a változónak, így már alkalmazható lesz a vektorizáció, mivel a maradvány területen is el tudjuk végezni a műveletet egy lépésben.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int lda=n;&lt;br /&gt;
if (n % 16 != 0) lda += (16 - n%16); // most lda%16==0&lt;br /&gt;
float* A = _mm_malloc(sizeof(float)*n*lda, 64);&lt;br /&gt;
&lt;br /&gt;
// használat&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
  for (int j = 0; j &amp;lt; n; j++)&lt;br /&gt;
    A[i*lda + j] = ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====&amp;quot;Héj ciklusok&amp;quot; kiküszöbölése====&lt;br /&gt;
Ciklusok esetén lehetséges, hogy a vektorizációt nem teljes mértékben ismeri fel a fordító program. &lt;br /&gt;
Ekkor segíthetünk a fordítónak egy fordítási direktívával ('''#pragma vector aligned'''), ha teljes mértékben biztosak vagyunk, hogy a ciklusunk vektorizálható!&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma vector aligned&lt;br /&gt;
  for (int j = 0; j &amp;lt; n; j++)&lt;br /&gt;
    A[i*lda + j] -= ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Ettől jelentős futás idejű javulást érhetünk el, mivel ezzel a #pragma-val kikapcsoltuk a futás idejű ellenőrzést, de előfordulhatnak olyan esetek, főleg amikor nem jól mértük fel a ciklusunkat és nem sikerült a vektorizálás, hogy ''Segmentation fault'' hibára fogunk futni.&lt;br /&gt;
&lt;br /&gt;
====Regularizáció====&lt;br /&gt;
Van egy ciklusunk, ami egy vektor elemeit módosítja. A processzor az adatokat &amp;quot;csokorba szedve&amp;quot; emeli be a belső kis méretű cache-be. Egyszerre természetesen több vektor elem is bekerül a a cache-be ezzel gyorsítva a feldolgozást. Ezen vektor iterációk valahogyan ráilleszkedik a vektorra.&lt;br /&gt;
&lt;br /&gt;
[[Fájl:regularization.png|Vektor regularizáció]]&lt;br /&gt;
&lt;br /&gt;
Abban az esetben, amikor ez az illesztés nem teljesen tökéletes (lsd. piros elemek), keletkeznem héj ciklusok, melyeket a regularizációval is eliminálni lehet. &amp;lt;br /&amp;gt;&lt;br /&gt;
A feladat eredetileg valahogyan így nézhet ki:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
for (int i = 0; i&amp;lt;n; i++)&lt;br /&gt;
  A[i]=...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ez a következő átalakítással már alkalmas vektorizációra és nem lesz &amp;quot;peel loop&amp;quot; probléma.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
for (int b=0; b&amp;lt;n; b++) {&lt;br /&gt;
  const int jStart = b- b%16; // mindig osztható 16-val&lt;br /&gt;
  ...&lt;br /&gt;
#pragma simd&lt;br /&gt;
  for (int j = jStart; j&amp;lt;n; j++)&lt;br /&gt;
    A[j]=...&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Párhuzamosítás===&lt;br /&gt;
Több féle mód létezik a párhuzamosításra. Korábban említettük a SIMD technikát, ami adatpárhuzamosításra való 1 processzoron belül, de létezik még processzorok közti párhuzamosítás, amit az OpenMP segítségével tudunk hatékonyan megvalósítani. Amennyiben gépek közti párhuzamosításra, üzenek küldésre van szükség, akkor az MPI-t kell segítségül hívni. Általános megoldás nem létezik minden feladatra, mert MPI is alkalmas lehet gépen belüli párhuzamosításra, nem csak az OpenMP. Amit mérlegelni kell, hogy mi az amire az alkalmazásnak szükséges van illetve mekkora kommunikációs overhead keletkezik, ha lecseréljük az OpenMP-t MPI-ra. Általában az egymástól független számításokat OpenMP-vel, míg a kommunikációt igénylő feladatokra MPI-t szoktak használni.&lt;br /&gt;
&lt;br /&gt;
====Szálak párhuzamosítása====&lt;br /&gt;
Az OpenMP-nek környezeti változók segítségével megadható a párhuzamosan futtatható szálak száma. Ezt a következő paranccsal tudjuk megtenni, ahol 5-re állítjuk.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export OMP_NUM_THREADS=5&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Forrásban a párhuzamos szálakat a ''#pragma omp parallel'' segítségével adhatjuk meg.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for              	//külső for szálak párhuzamosítása&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
#pragma simd				// vektorizáció a belső cikluson&lt;br /&gt;
  for (int j = 0; j &amp;lt; m; j++)&lt;br /&gt;
    My_Function(A[i][j]);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A fordítási direktívák össze is vonhatóak, ilyenkor 1 ciklus is elég.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for simd&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
    My_Function(A[i]);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=====Változók láthatósága, megosztása=====&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int A, B; // Variables declared at the beginning of a function		#pragma omp parallel private(A) shared(B)		{...}		or		int B; // Variable declared outside of parallel scope - shared by default		#pragma omp parallel		{...}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Párhuzamos szálak ütemezése====&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Szálak szinkronizációja====&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
	   * with mutexes		(it is very slow, waiting always)		#pragma omp parallel for		  for (int i = 0; i &amp;lt; n; i++) {		#pragma omp critical		    { // Only one thread at a time can execute this section		      total = total + i;		    }		  }	   * or with atomic operation		(it also [very] slow, lot of limitations)		#pragma omp parallel for		for (int i = 0; i &amp;lt; n; i++)		  { // Lightweight synchronization		#pragma omp atomic		    total += i;		  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=====Párhuzamos redukció=====&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
	     (it is fast)  BUT not for vector		int total = 0;		#pragma omp parallel for reduction(+: total)               //usage: reduction(operation: scalar)		for (int i = 0; i &amp;lt; n; i++) {		  total = total + i;		}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Ciklusok párhuzamosítása====&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
		#pragma omp parallel		{		  // Code placed here will be executed by all threads.		  ...		#pragma omp for [schedule (&amp;lt;mode&amp;gt;,&amp;lt;chunk&amp;gt;)		  for (int i = 0; i &amp;lt; n; i++) {		    // ... iterations will be distributed across available threads...		  }		  // ... code placed here will be executed by all threads		}	    modes: static, dynamic, guided	    chunk: integer value&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Ciklus kifejtés====&lt;br /&gt;
A technika már egy példa erejéig a vektorizációnál is látható, de ott nem került részletezésre. A megoldás lényege, hogy a kellően nagy ciklust szétdaraboljuk kisebb darabokra, amit már külön feldolgozhatóak, ezzel párhuzamosítva a folyamatokat. Az egy darab kiindulási ciklusból két darab egymásba ágyazott ciklust hozunk létre, ami ugyanazon a feladattéren dolgozik. &lt;br /&gt;
&lt;br /&gt;
Eredeti állapot:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++) {...}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Átírt változat:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int STRIP=1024;&lt;br /&gt;
for (int ii = 0; ii &amp;lt; n; ii += STRIP)&lt;br /&gt;
 for (int i = ii; i &amp;lt; ii+STRIP; i++) {...}&lt;br /&gt;
&lt;br /&gt;
// ha még van maradék elem, akkor azokon is végrehajtjuk a számításokat&lt;br /&gt;
if (n%STRIP != 0) {&lt;br /&gt;
  for (i=n-(n%STRIP)+1; i&amp;lt;n; i++) {...}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A darabok mérete (STRIP értéke) az adott rendszer és feladat függvényében változhat, tehát ez egyfajta &amp;quot;tuning&amp;quot; paraméter.&lt;br /&gt;
&lt;br /&gt;
====Automatikus ciklusösszevonás====&lt;br /&gt;
Az OpenMP támogatást nyújt több egymásba ágyazott ciklus &amp;quot;összevonására&amp;quot;. Ehhez csak a '''collaps''' fordítási direktívát és az összevonandó ciklusok számát kell megadni. &amp;lt;br /&amp;gt;&lt;br /&gt;
FONTOS: ekkor az automatikus vektorizáció kikapcsolásra kerül!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for collaps(2)&lt;br /&gt;
for (int i=0; i&amp;lt;n; i++)&lt;br /&gt;
  for( int j=0; j&amp;lt;m; j++){...}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A fneti példában a 2db for ciklis kerül párhuzamosításra úgy, hogy a fordítás során 1db ciklus generálódik belőle, ami a következő paraméterezéssel ekvivalens:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for&lt;br /&gt;
for (int c=0; c &amp;lt; n*m; c++) {&lt;br /&gt;
  int i = c/n;&lt;br /&gt;
  int j = c%n;&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Szigeti(AT)niif.hu</name></author>	</entry>

	<entry>
		<id>https://wiki.niif.hu/index.php?title=Intel_Xeon_Phi&amp;diff=3751</id>
		<title>Intel Xeon Phi</title>
		<link rel="alternate" type="text/html" href="https://wiki.niif.hu/index.php?title=Intel_Xeon_Phi&amp;diff=3751"/>
				<updated>2016-05-05T09:42:45Z</updated>
		
		<summary type="html">&lt;p&gt;Szigeti(AT)niif.hu: /* Párhuzamosítás */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Az oldalon található információk segítséget nyújtanak a HPC felhasználóinknak, hogy alkalmazásaikat miként portolhatják valamint optimalizálhatják a hatékonyabb futást szem előtt tartva Intel Xeon Phi koprocesszor kártyákon.&lt;br /&gt;
&lt;br /&gt;
==Hardver bemutatása==&lt;br /&gt;
Intel® Xeon Phi™ Coprocessor 7120P&lt;br /&gt;
&amp;lt;gallery&amp;gt;&lt;br /&gt;
Fájl:Xeon-phi-family-rwd.png.rendition.intel.web.720.405.png|&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-  align=&amp;quot;center&amp;quot;&lt;br /&gt;
! CPU&lt;br /&gt;
! threads/Core&lt;br /&gt;
!max threads&lt;br /&gt;
! CPU Freq&lt;br /&gt;
! Memória&lt;br /&gt;
! L1 cache&lt;br /&gt;
! L2 cache&lt;br /&gt;
! Interfész&lt;br /&gt;
! Ptot&lt;br /&gt;
! Számítási kapacitás&lt;br /&gt;
|-  align=&amp;quot;center&amp;quot;&lt;br /&gt;
| 61&lt;br /&gt;
| 4 Hardver&lt;br /&gt;
| 244&lt;br /&gt;
| 1238 GHz&lt;br /&gt;
| 16GB GDDR5&lt;br /&gt;
| 32KB/core&lt;br /&gt;
| 512KB/core&lt;br /&gt;
| PCIe&lt;br /&gt;
| 300W&lt;br /&gt;
| 1,2 TFlop/s dupla pontosság&lt;br /&gt;
|-  align=&amp;quot;center&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
| OpenMP-nél csak '''240'''&lt;br /&gt;
|&lt;br /&gt;
| ~100 cycles&lt;br /&gt;
~174 GB/s&lt;br /&gt;
| ~3 cycles&lt;br /&gt;
| ~10 cycles&lt;br /&gt;
| ~7 GB/s&lt;br /&gt;
~1 microsecond&lt;br /&gt;
|&lt;br /&gt;
| 2,4 TFlop/s egyszeres pontosság&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Az Intel® 1. generációs Xeon Phi™ koprocesszorában található 61 processzor ugyanazt a 16GB memóriát éri el, de semmilyen módon se fér hozzá közvetlenül a hoszt gép fizikai memóriájához.&amp;lt;br /&amp;gt;&lt;br /&gt;
[[Budapest2_klaszter|Budapest2]] és [[HPC#Szupersz.C3.A1m.C3.ADt.C3.B3g.C3.A9peink_.C3.B6sszehasonl.C3.ADt.C3.A1sa|Debrecen3-Phi]] gépeinkben számítási nodonként 2-2 Phi™ koprocesszor található. &amp;lt;br/&amp;gt;&lt;br /&gt;
A kártyákon speciális célú Linux kernel fut melyre a belépés limitáltan csak a rendszergazdák számára lehetséges SSH-n keresztül a hoszt gépről. Természetesen a kártyára belépve olyan érzésünk lehet, mintha egy sok processzoros gépen dolgoznánk, ami rendelkezik rengeteg memóriával. Ez így is van, de a hatékonyabb kihasználás érdekében az SSH-n keresztüli elérés nem lehetséges, csak a Slurm segítségével valamint a programozási nyelvek által támogatott csatornákon keresztül. Ennek részletezése lentebb megtalálható.&lt;br /&gt;
&lt;br /&gt;
==Használati módok==&lt;br /&gt;
A koprocesszor használatai módjait két csoportba lehet sorolni. A '''natív''' és az '''offload''' használatot.  &amp;lt;br/&amp;gt;&lt;br /&gt;
Ezen használati módokról a lenti fejezetekben több részletet is megtudhat. Az itt megfogalmazott &amp;quot;támogatás&amp;quot; csak a mi infrastruktúránkra érvényes! &amp;lt;br /&amp;gt;&lt;br /&gt;
A bemutatott példák ''c++'' nyelven vannak, de természetesen ''c'' és ''fortran'' esetén is megtalálható az ekvivalens módszer.&lt;br /&gt;
&lt;br /&gt;
===a) nem támogatott mód===&lt;br /&gt;
====közvetlen natív használat====&lt;br /&gt;
A natív mód arra szolgál, hogy a MIC kártyára  lefordított alkalmazásunkat felmásoljuk a kártyára, majd belépünk SSH-n a kártyára és azon futtatjuk a bináris kódunkat. &amp;lt;br /&amp;gt;&lt;br /&gt;
Ez a legegyszerűbb használati mód, ugyanis nincs más dolgunk, csak a '''-mmic''' kapcsolót használni a fordításnál és a fordító egy máris a MIC architektúrára kész bináris készít nekünk. &amp;lt;br/&amp;gt;&lt;br /&gt;
Workflow a következő lenne:&lt;br /&gt;
# Alkalmazás megírása. például [[Média:hello.cc|''hello.cc'']]&lt;br /&gt;
# Alkalmazás fordítása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;icpc -o hello-MIC -mmic hello.cc&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Másolás a MIC kártyára &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;scp hello-MIC mic0:&amp;lt;/pre&amp;gt;&lt;br /&gt;
# belépés a kártyára &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;ssh mic0&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Alkalmazás futtatása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;./hello-MIC&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===b) támogatott, de nem preferált használat===&lt;br /&gt;
====automatikus kódátvitel====&lt;br /&gt;
Ebben az esetben nem kell átmásolnunk a bináris alkalmazásunkat a kártyára sőt még csak be se kell lépnünk, mert ezt a két lépést egy beépített tool végzi el. &amp;lt;br/&amp;gt;&lt;br /&gt;
A tool eléréséhez szükséges parancs neve: '''micnativeloadex'''&lt;br /&gt;
&lt;br /&gt;
Ebben az esetben a workflow a következőképpen módosul:&lt;br /&gt;
# Alkalmazás megírása. például [[Média:hello.cc|''hello.cc'']]&lt;br /&gt;
# Alkalmazás fordítása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;icpc -o hello-MIC -mmic hello.cc&amp;lt;/pre&amp;gt;&lt;br /&gt;
# A tool futásához szükséges library-k megadása &amp;lt;br&amp;gt; &amp;lt;pre&amp;gt;export SINK_LD_LIBRARY_PATH=/opt/intel/composerxe/compiler/lib/mic&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Alkalmazás futtatása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;micnativeloadex hello-MIC&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| BUDAPEST2[cn10] phi (0)$ export SINK_LD_LIBRARY_PATH=/opt/intel/composerxe/compiler/lib/mic &amp;lt;br/&amp;gt;BUDAPEST2[cn10] phi (0)$ micnativeloadex hello-MIC &amp;lt;br/&amp;gt;Hello world! I have 244 logical cores.&amp;lt;br/&amp;gt;BUDAPEST2[cn10] phi (0)$&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===c) támogatott módok===&lt;br /&gt;
====Offload====&lt;br /&gt;
Az Offload modell esetén szükséges a forráskód módosítása, mégpedig úgy, hogy azon kódrészletet, amit a koprocesszoron szeretnénk futtatni, ki kell egészíteni egy fordítási direktívával. Az alkalmazás fordításakor elegendő csak a CPU-ra való binárist készíteni mivel a modell végzi a megfelelő forrás részlet mozgatását és futtatását.&amp;lt;br /&amp;gt;&lt;br /&gt;
[[Fájl:offload.png|Offload modell]] &amp;lt;br /&amp;gt;&lt;br /&gt;
Példánkban jól látható, hogy a második kiíratás már a koprocesszoron fut.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
int main(int argc, char * argv[]) {&lt;br /&gt;
  printf(&amp;quot;Hello world from host!\n&amp;quot;);&lt;br /&gt;
#pragma offload target(mic)&lt;br /&gt;
  {&lt;br /&gt;
    printf(&amp;quot;Hello world from coprocessor!\n&amp;quot;); fflush(stdout);&lt;br /&gt;
  }&lt;br /&gt;
  printf(&amp;quot;Bye\n&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ebben a példában az operációs rendszer választja ki, hogy melyik Phi kártyán fusson le a kódrészlet. Amennyiben mi szeretnénk explicite megmondani, hogy melyik kártyán fusson, akkor a fordítási direktívában a mic kulcsszó után kell tenni egy &amp;quot;:&amp;quot;-t és a kártya sorszámát [mi esetünkben 0 vagy 1]. Ekkor így nézne ki a direktíva: ''#pragma offload target(mic:1)''&lt;br /&gt;
&lt;br /&gt;
Amennyiben az offload szekcióban függvény hívás is szerepel, akkor szükségeltetik az függvény futásához szükséges változók adatterületének átmásolása a Phi kártyára, majd a lefutás után visszamásolni a hoszt gépre. Ezen másolást az ''offload_attribute'' ''push'' és ''pop'' paraméterei segítségével határozhatjuk meg. &amp;lt;br /&amp;gt;&lt;br /&gt;
A következő mátrix-vektor szorzás példánál az A mátrix globális változó, míg a b és c vektorok a függvény paraméterei.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//A változók felmásolásra kerülnek a Xeon Phi-re&lt;br /&gt;
#pragma offload_attribute(push, target(mic))&lt;br /&gt;
  double* A;&lt;br /&gt;
  void MatrixVector(double *b, double *c, double m, double n) {&lt;br /&gt;
    ...&lt;br /&gt;
    c[] = A[..]*b[]&lt;br /&gt;
  }&lt;br /&gt;
// felmásolt objektumok visszamásolása&lt;br /&gt;
#pragma offload_attribute(pop)&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
  ...&lt;br /&gt;
#pragma offload target(mic) in(A[0:n*m])&lt;br /&gt;
// így is meg lehetne adni: #pragma offload target(mic:0) in(A : length(n*m))	        &lt;br /&gt;
  {&lt;br /&gt;
    MatrixVector(b, c, m, n);&lt;br /&gt;
  }&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ekkor minden memória területet 2x kell másolni, egyszer fel a kártyára, majd másodszor vissza a hoszt gépre, pedig elegendő lenne csak az eredmény vektort visszamásolni. Ezen optimalizálással egy későbbi fejezetben foglalkozunk. &amp;lt;p /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Futás során kaphatunk részletes riportot is, hogy pontosan milyen adatmozgatások történnek az alkalmazás futása során. Ehhez nincs szükség másra csak az '''OFFLOAD_REPORT''' környezeti változó megfelelő beállítása. A beállított értékek 1, 2 vagy 3 lehetnek, attól függően mennyire részletes riportot szeretnénk kapni. &amp;lt;br /&amp;gt;&lt;br /&gt;
beállítás:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export OFFLOAD_REPORT=3&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
példa kimenet a hello alkalmazásra:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Hello world from host!&lt;br /&gt;
[Offload] [MIC 0] [File]                    hello_offload.cc&lt;br /&gt;
[Offload] [MIC 0] [Line]                    5&lt;br /&gt;
[Offload] [MIC 0] [Tag]                     Tag 0&lt;br /&gt;
Hello world from coprocessor!&lt;br /&gt;
[Offload] [HOST]  [Tag 0] [CPU Time]        0.554653(seconds)&lt;br /&gt;
[Offload] [MIC 0] [Tag 0] [CPU-&amp;gt;MIC Data]   0 (bytes)&lt;br /&gt;
[Offload] [MIC 0] [Tag 0] [MIC Time]        0.000140(seconds)&lt;br /&gt;
[Offload] [MIC 0] [Tag 0] [MIC-&amp;gt;CPU Data]   0 (bytes)&lt;br /&gt;
Bye&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Offload + OpenMP====&lt;br /&gt;
Amennyiben nem csak 1 processzort szeretnénk használni a Phi kártyánkon és az alkalmazásunkban használt számítás megengedi a párhuzamosítást, akkor az OpenMP segítségével párhuzamosíthatjuk a megfelelő számítást. &amp;lt;br /&amp;gt;&lt;br /&gt;
[[Fájl:Offload+openmp.png|Offload modell és OpenMP]] &amp;lt;br /&amp;gt;&lt;br /&gt;
Példánk maradjon ugyanúgy a mátrix-vektor szorzás. Amennyiben a teljes kártya adta kapacitást ki szeretnénk használni, akkor az OMP threadek számát 240-re kell beállítani. Azért nem 244-re, mivel 1 CPU-t dedikáltan csak a kommunikációra használ a rendszer és 1 CPU 4 hardver threaddel rendelkezik.&lt;br /&gt;
Ekkor a példánk így változik:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;omp.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#pragma offload_attribute(push, target(mic))&lt;br /&gt;
void multiply(int n, int m, double* A, double* b, double* c){&lt;br /&gt;
  const int nCPUs = omp_get_max_threads();   //240&lt;br /&gt;
  omp_set_num_threads(nCPUs);&lt;br /&gt;
#pragma omp parallel for schedule(dynamic, 1)&lt;br /&gt;
  for ( int i = 0 ; i &amp;lt; m ; i++)&lt;br /&gt;
     for ( int j = 0 ; j &amp;lt; n ; j++)&lt;br /&gt;
       c[i] += A[i*n+j] * b[j];&lt;br /&gt;
}&lt;br /&gt;
#pragma offload_attribute(pop)&lt;br /&gt;
&lt;br /&gt;
int main(){&lt;br /&gt;
  double * A = (double*) malloc(sizeof(double)*n*m);&lt;br /&gt;
  ...&lt;br /&gt;
#pragma offload target(mic) in(A:length(n*m))&lt;br /&gt;
     multiply(n, m, A, b, c);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Az alkalmazás fordításánál a fordítónak meg kell adni, hogy használhja az openmp library-ket is. Ezt a '''-qopenmp''' kapcsolóval tudjuk elérni.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
icpc -o matrix-CPU -qopenmp matrix.cc&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====MPI + Offload + OpenMP====&lt;br /&gt;
Abban az esetben, ha 1 számítási node nem elég, MPI segítségével össze tudunk kötni több nodet, heterogén rendszert alkotva, és azokon alkalmazható a fenti Offload + OpenMP modell. &amp;lt;br /&amp;gt;&lt;br /&gt;
[[Fájl:Mpi+offload+openmp.png|MPI elosztás valamint Offload modell és OpenMP]] &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Alkalmazások optimalizálása a kártyára==&lt;br /&gt;
Ebben a fejezetben optimalizálási lehetőségeket mutatunk be, melyek segítségül szolgálhatnak az egyes alkalmazások hatékonyabb futása érdekében ezzel jobban kihasználni a rendelkezésre álló erőforrások adta számítási kapacitás.&lt;br /&gt;
&lt;br /&gt;
===Környezeti változók===&lt;br /&gt;
Lehetséges csak a Phi kártyákon használt környezeti változók beállítása, amit az alkalmazásunk használni is tud. &amp;lt;br /&amp;gt;&lt;br /&gt;
Példánkban az OpenMP esetén használható szálak számát állíthatjuk be:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export MIC_ENV_PREFIX=XEONPHI&lt;br /&gt;
export OMP_NUM_THREADS=12&lt;br /&gt;
export XEONPHI_OMP_NUM_THREADS=240&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
A beállított prefix tetszőleges karaktersorozat lehet, a lényeg, hogy ezt a beállított prefixet használjuk a környezeti változók esetén is!&lt;br /&gt;
 &lt;br /&gt;
===Hosszredukció===&lt;br /&gt;
Többnyire a fordítók, ha nem mondjuk meg explicite, a dupla pontosságú változatot választják a lehetséges változó- és függvény típusok közül. Amennyiben a számításunk nem igényli a dupla pontosságot, akkor használjuk a forrásban mindenhol az adott típus vagy függvény egyszeres pontosságú változatát, mellyel jelentős teljesítmény növekedést tudunk elérni. Továbbá az egyes műveletek futási ideje nem egyforma, így a műveletet helyettesíthetjük más művelettel, ami gyorsabb, akkor ismét javíthatunk a hatékonyságon. &amp;lt;br /&amp;gt;&lt;br /&gt;
Íme pár példa, amit alkalmazni lehet:&lt;br /&gt;
* &amp;quot;''/''&amp;quot; művelet helyett &amp;quot;''*''&amp;quot; -t használjunk, mert azt gyorsabban végre tudja hajtani a processzor&lt;br /&gt;
* ''pow'' =&amp;gt; ''1.0 / sqrt()''    , tetszőleges hatványozás, pl.: 3/2-ik hatvány&lt;br /&gt;
* hardver áltatl támogatott műveletek:&lt;br /&gt;
** ''exp'' =&amp;gt; ''exp2''&lt;br /&gt;
** ''log'' =&amp;gt; ''log2''&lt;br /&gt;
* dupla pontosság -&amp;gt; egyszeres pontosság lebegőpontos számok és műveletek esetén &lt;br /&gt;
** ''1.0'' =&amp;gt; ''1.0f''&lt;br /&gt;
** ''pow'' =&amp;gt; ''powf''&lt;br /&gt;
&lt;br /&gt;
===Vektorizáció===&lt;br /&gt;
A vektorizáció azon fordítási időben eldönthető optimalizációs faktor, amely nagy mértékben függ attól a hardver felépítésétől, amire éppen a fordítás történni fog. Legfontosabb befolyásoló tényezők a processzor cache méretei illetve a vektor műveletekért felelős aritmetikai egységének jellemzői mivel ezeket használja fel a fordító az optimalizálási folyamata során. &amp;lt;br /&amp;gt;&lt;br /&gt;
Az automatikus vektorizáció minimum -O2 szinttől érvényes, de mivel általában ez a szint az alapértelmezett, ezért minden fordításnál alkalmazásra kerül. &amp;lt;br /&amp;gt;&lt;br /&gt;
A fordító programnak a következő kapcsolókat lehet megadni, melyek segítenek a minél jobb vektorizációban:&lt;br /&gt;
* ''-qopt-report=&amp;lt;szám&amp;gt;'' ; az optimalizációs jelentés részletességét lehet megadni 0-tól 5-ig, ahol az 5-s szint a legrészletesebbet jelenti&lt;br /&gt;
* ''-qopt-report-phase:vec'' ; a vektorizációról szeretnék jelentést kapni&lt;br /&gt;
* ''-opt-report-file=&amp;lt;fájlnév&amp;gt;'' ; megadható a riportfájl neve&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
icpc autovec.cc -mmic -qopt-report=5 -opt-report-file=vectorization.oMIC.optrpt -qopt-report-phase:vec&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====SIMD====&lt;br /&gt;
Az adatok hatékonyabb feldolgozására az egyik megvalósítás a SIMD (Single Instruction Multiple Data), amikor is a különálló adatokat úgy kezeli, mintha az vektorok volnának és azon végzi el a vektor-műveletet. Ezzel egy a hardver által támogatott műveletet végzünk egy lépésben egy nagyobb adatmennyiségen, ezzel párhuzamosítva a feldolgozási folyamatot. Ezzel a módszerrel nagyon jó adat szintű párhuzamosítást lehet elérni.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Fájl:Simd.png|SIMD]]&lt;br /&gt;
&lt;br /&gt;
Főbb támogatott műveletek:&lt;br /&gt;
* aritmetikai műveletek, bináris operátorok&lt;br /&gt;
* összehasonlítások&lt;br /&gt;
* konverziók és típus kasztolások&lt;br /&gt;
* bit-műveletek: NOT, AND, OR, XOR, XAND&lt;br /&gt;
&lt;br /&gt;
Használata: '''simd''' fordítási direktívával lehetséges &amp;lt;br /&amp;gt;&lt;br /&gt;
Például:&lt;br /&gt;
* egyszerű vektorizáció&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma simd&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
  A[i] += B[i];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* adat párhuzamosítás OpenMP segítségével&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for simd&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
  DoSomeWork(A[i]);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====AoS -&amp;gt; SoA====&lt;br /&gt;
A kiindulás probléma az, hogy a struktúrákat tartalmazó vektort nem lehet jól vektorizálni, mivel a cache-ben tárolt adatok nagy többsége feleslegesen van bent, mivel azon adatokat csak egy későbbi ciklus futás során fogja az alkalmazás feldolgozni. Tehát ezen adatok jelenleg feleslegesen foglalják a cache területet, ahelyett, hogy hasznos adatot tárolnánk ott.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
struct MyStructure {&lt;br /&gt;
  float x, y, z;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void My_Function(cons int nItem, MyStructure* const Items, const float delta) {&lt;br /&gt;
  for (int i = 0; i &amp;lt; nItem; i++) {&lt;br /&gt;
    ...&lt;br /&gt;
    for (int j = 0; j &amp;lt; nItem; j++) {&lt;br /&gt;
      const float dx = Items[j].x - Items[i].x;&lt;br /&gt;
      const float dy = Items[j].y - Items[i].y;&lt;br /&gt;
      const float dz = Items[j].z - Items[i].z;&lt;br /&gt;
      ...  &lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  ... &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A vektorban a struktúrák tárolása helyett minden elemnek egy dedikált vektort allokálunk és így könnyen tudja a fordító vektorizálni azt. Természetesen segítségként a vektor feldolgozását &amp;quot;tömbösítettük&amp;quot; a tileSize méret megadásával illetve a ''unroll'' fordítási direktíva segítségével.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
struct MyStructureSet {&lt;br /&gt;
  float *x, *y, *z;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void My_Function(cons int nItem, MyStructure* const Items, const float delta) {&lt;br /&gt;
  const int tileSize = 16;&lt;br /&gt;
#pragma omp parallel for schedule(guided)&lt;br /&gt;
  for (int ii = 0; ii &amp;lt; nItem; ii+= tileSize) {&lt;br /&gt;
    ...&lt;br /&gt;
#pragma unroll(tileSize)&lt;br /&gt;
    for (int j = 0; j &amp;lt; nItem; j++) {&lt;br /&gt;
#pragma unroll(tileSize)&lt;br /&gt;
      for (int i = ii; i &amp;lt; ii + tileSize; i++) {&lt;br /&gt;
        const float dx = Items.x[j] - Items.x[i];&lt;br /&gt;
        const float dy = Items.y[j] - Items.y[i];&lt;br /&gt;
        const float dz = Items.z[j] - Items.z[i];&lt;br /&gt;
        ...&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  ... &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Hatékony hossz választás====&lt;br /&gt;
Olyan esetben jelent előnyt, ha például az n*n-es mátrix esetén n nem osztható 16-val, mert akkor a vektorizáció nem teljesen hajtható végre. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// n%16 != 0&lt;br /&gt;
float* A = _mm_malloc(sizeof(float)*n*n, 64);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Ilyen esetben mindig marad egy kis vektor részlet, amire elemenként kell elvégezni a műveletet.&amp;lt;br /&amp;gt;&lt;br /&gt;
A megoldást az jelenti, hogy a memória allokálást kiterjesztjük és nagyobb memória területet foglalunk a változónak, így már alkalmazható lesz a vektorizáció, mivel a maradvány területen is el tudjuk végezni a műveletet egy lépésben.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int lda=n;&lt;br /&gt;
if (n % 16 != 0) lda += (16 - n%16); // most lda%16==0&lt;br /&gt;
float* A = _mm_malloc(sizeof(float)*n*lda, 64);&lt;br /&gt;
&lt;br /&gt;
// használat&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
  for (int j = 0; j &amp;lt; n; j++)&lt;br /&gt;
    A[i*lda + j] = ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====&amp;quot;Héj ciklusok&amp;quot; kiküszöbölése====&lt;br /&gt;
Ciklusok esetén lehetséges, hogy a vektorizációt nem teljes mértékben ismeri fel a fordító program. &lt;br /&gt;
Ekkor segíthetünk a fordítónak egy fordítási direktívával ('''#pragma vector aligned'''), ha teljes mértékben biztosak vagyunk, hogy a ciklusunk vektorizálható!&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma vector aligned&lt;br /&gt;
  for (int j = 0; j &amp;lt; n; j++)&lt;br /&gt;
    A[i*lda + j] -= ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Ettől jelentős futás idejű javulást érhetünk el, mivel ezzel a #pragma-val kikapcsoltuk a futás idejű ellenőrzést, de előfordulhatnak olyan esetek, főleg amikor nem jól mértük fel a ciklusunkat és nem sikerült a vektorizálás, hogy ''Segmentation fault'' hibára fogunk futni.&lt;br /&gt;
&lt;br /&gt;
====Regularizáció====&lt;br /&gt;
Van egy ciklusunk, ami egy vektor elemeit módosítja. A processzor az adatokat &amp;quot;csokorba szedve&amp;quot; emeli be a belső kis méretű cache-be. Egyszerre természetesen több vektor elem is bekerül a a cache-be ezzel gyorsítva a feldolgozást. Ezen vektor iterációk valahogyan ráilleszkedik a vektorra.&lt;br /&gt;
&lt;br /&gt;
[[Fájl:regularization.png|Vektor regularizáció]]&lt;br /&gt;
&lt;br /&gt;
Abban az esetben, amikor ez az illesztés nem teljesen tökéletes (lsd. piros elemek), keletkeznem héj ciklusok, melyeket a regularizációval is eliminálni lehet. &amp;lt;br /&amp;gt;&lt;br /&gt;
A feladat eredetileg valahogyan így nézhet ki:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
for (int i = 0; i&amp;lt;n; i++)&lt;br /&gt;
  A[i]=...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ez a következő átalakítással már alkalmas vektorizációra és nem lesz &amp;quot;peel loop&amp;quot; probléma.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
for (int b=0; b&amp;lt;n; b++) {&lt;br /&gt;
  const int jStart = b- b%16; // mindig osztható 16-val&lt;br /&gt;
  ...&lt;br /&gt;
#pragma simd&lt;br /&gt;
  for (int j = jStart; j&amp;lt;n; j++)&lt;br /&gt;
    A[j]=...&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Párhuzamosítás===&lt;br /&gt;
Több féle mód létezik a párhuzamosításra. Korábban említettük a SIMD technikát, ami adatpárhuzamosításra való 1 processzoron belül, de létezik még processzorok közti párhuzamosítás, amit az OpenMP segítségével tudunk hatékonyan megvalósítani. Amennyiben gépek közti párhuzamosításra, üzenek küldésre van szükség, akkor az MPI-t kell segítségül hívni. Általános megoldás nem létezik minden feladatra, mert MPI is alkalmas lehet gépen belüli párhuzamosításra, nem csak az OpenMP. Amit mérlegelni kell, hogy mi az amire az alkalmazásnak szükséges van illetve mekkora kommunikációs overhead keletkezik, ha lecseréljük az OpenMP-t MPI-ra. Általában az egymástól független számításokat OpenMP-vel, míg a kommunikációt igénylő feladatokra MPI-t szoktak használni.&lt;br /&gt;
&lt;br /&gt;
====Szálak párhuzamosítása===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export OMP_NUM_THREADS=5		#pragma omp parallel for              	//for thread paralelizm		for (int i = 0; i &amp;lt; n; i++) 		#pragma simd				// Vectorization in inner loop		  for (int j = 0; j &amp;lt; m; j++) 		    DoSomeWork(A[i][j]);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
=====Változók láthatósága, megosztása=====&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int A, B; // Variables declared at the beginning of a function		#pragma omp parallel private(A) shared(B)		{...}		or		int B; // Variable declared outside of parallel scope - shared by default		#pragma omp parallel		{...}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Párhuzamos szálak ütemezése====&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Szálak szinkronizációja====&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
	   * with mutexes		(it is very slow, waiting always)		#pragma omp parallel for		  for (int i = 0; i &amp;lt; n; i++) {		#pragma omp critical		    { // Only one thread at a time can execute this section		      total = total + i;		    }		  }	   * or with atomic operation		(it also [very] slow, lot of limitations)		#pragma omp parallel for		for (int i = 0; i &amp;lt; n; i++)		  { // Lightweight synchronization		#pragma omp atomic		    total += i;		  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=====Párhuzamos redukció=====&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
	     (it is fast)  BUT not for vector		int total = 0;		#pragma omp parallel for reduction(+: total)               //usage: reduction(operation: scalar)		for (int i = 0; i &amp;lt; n; i++) {		  total = total + i;		}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Ciklusok párhuzamosítása====&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
		#pragma omp parallel		{		  // Code placed here will be executed by all threads.		  ...		#pragma omp for [schedule (&amp;lt;mode&amp;gt;,&amp;lt;chunk&amp;gt;)		  for (int i = 0; i &amp;lt; n; i++) {		    // ... iterations will be distributed across available threads...		  }		  // ... code placed here will be executed by all threads		}	    modes: static, dynamic, guided	    chunk: integer value&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Ciklus kifejtés====&lt;br /&gt;
A technika már egy példa erejéig a vektorizációnál is látható, de ott nem került részletezésre. A megoldás lényege, hogy a kellően nagy ciklust szétdaraboljuk kisebb darabokra, amit már külön feldolgozhatóak, ezzel párhuzamosítva a folyamatokat. Az egy darab kiindulási ciklusból két darab egymásba ágyazott ciklust hozunk létre, ami ugyanazon a feladattéren dolgozik. &lt;br /&gt;
&lt;br /&gt;
Eredeti állapot:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++) {...}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Átírt változat:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int STRIP=1024;&lt;br /&gt;
for (int ii = 0; ii &amp;lt; n; ii += STRIP)&lt;br /&gt;
 for (int i = ii; i &amp;lt; ii+STRIP; i++) {...}&lt;br /&gt;
&lt;br /&gt;
// ha még van maradék elem, akkor azokon is végrehajtjuk a számításokat&lt;br /&gt;
if (n%STRIP != 0) {&lt;br /&gt;
  for (i=n-(n%STRIP)+1; i&amp;lt;n; i++) {...}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A darabok mérete (STRIP értéke) az adott rendszer és feladat függvényében változhat, tehát ez egyfajta &amp;quot;tuning&amp;quot; paraméter.&lt;br /&gt;
&lt;br /&gt;
====Automatikus ciklusösszevonás====&lt;br /&gt;
Az OpenMP támogatást nyújt több egymásba ágyazott ciklus &amp;quot;összevonására&amp;quot;. Ehhez csak a '''collaps''' fordítási direktívát és az összevonandó ciklusok számát kell megadni. &amp;lt;br /&amp;gt;&lt;br /&gt;
FONTOS: ekkor az automatikus vektorizáció kikapcsolásra kerül!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for collaps(2)&lt;br /&gt;
for (int i=0; i&amp;lt;n; i++)&lt;br /&gt;
  for( int j=0; j&amp;lt;m; j++){...}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A fneti példában a 2db for ciklis kerül párhuzamosításra úgy, hogy a fordítás során 1db ciklus generálódik belőle, ami a következő paraméterezéssel ekvivalens:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for&lt;br /&gt;
for (int c=0; c &amp;lt; n*m; c++) {&lt;br /&gt;
  int i = c/n;&lt;br /&gt;
  int j = c%n;&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Szigeti(AT)niif.hu</name></author>	</entry>

	<entry>
		<id>https://wiki.niif.hu/index.php?title=Intel_Xeon_Phi&amp;diff=3750</id>
		<title>Intel Xeon Phi</title>
		<link rel="alternate" type="text/html" href="https://wiki.niif.hu/index.php?title=Intel_Xeon_Phi&amp;diff=3750"/>
				<updated>2016-05-05T09:24:07Z</updated>
		
		<summary type="html">&lt;p&gt;Szigeti(AT)niif.hu: /* Ciklus kifejtés */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Az oldalon található információk segítséget nyújtanak a HPC felhasználóinknak, hogy alkalmazásaikat miként portolhatják valamint optimalizálhatják a hatékonyabb futást szem előtt tartva Intel Xeon Phi koprocesszor kártyákon.&lt;br /&gt;
&lt;br /&gt;
==Hardver bemutatása==&lt;br /&gt;
Intel® Xeon Phi™ Coprocessor 7120P&lt;br /&gt;
&amp;lt;gallery&amp;gt;&lt;br /&gt;
Fájl:Xeon-phi-family-rwd.png.rendition.intel.web.720.405.png|&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-  align=&amp;quot;center&amp;quot;&lt;br /&gt;
! CPU&lt;br /&gt;
! threads/Core&lt;br /&gt;
!max threads&lt;br /&gt;
! CPU Freq&lt;br /&gt;
! Memória&lt;br /&gt;
! L1 cache&lt;br /&gt;
! L2 cache&lt;br /&gt;
! Interfész&lt;br /&gt;
! Ptot&lt;br /&gt;
! Számítási kapacitás&lt;br /&gt;
|-  align=&amp;quot;center&amp;quot;&lt;br /&gt;
| 61&lt;br /&gt;
| 4 Hardver&lt;br /&gt;
| 244&lt;br /&gt;
| 1238 GHz&lt;br /&gt;
| 16GB GDDR5&lt;br /&gt;
| 32KB/core&lt;br /&gt;
| 512KB/core&lt;br /&gt;
| PCIe&lt;br /&gt;
| 300W&lt;br /&gt;
| 1,2 TFlop/s dupla pontosság&lt;br /&gt;
|-  align=&amp;quot;center&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
| OpenMP-nél csak '''240'''&lt;br /&gt;
|&lt;br /&gt;
| ~100 cycles&lt;br /&gt;
~174 GB/s&lt;br /&gt;
| ~3 cycles&lt;br /&gt;
| ~10 cycles&lt;br /&gt;
| ~7 GB/s&lt;br /&gt;
~1 microsecond&lt;br /&gt;
|&lt;br /&gt;
| 2,4 TFlop/s egyszeres pontosság&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Az Intel® 1. generációs Xeon Phi™ koprocesszorában található 61 processzor ugyanazt a 16GB memóriát éri el, de semmilyen módon se fér hozzá közvetlenül a hoszt gép fizikai memóriájához.&amp;lt;br /&amp;gt;&lt;br /&gt;
[[Budapest2_klaszter|Budapest2]] és [[HPC#Szupersz.C3.A1m.C3.ADt.C3.B3g.C3.A9peink_.C3.B6sszehasonl.C3.ADt.C3.A1sa|Debrecen3-Phi]] gépeinkben számítási nodonként 2-2 Phi™ koprocesszor található. &amp;lt;br/&amp;gt;&lt;br /&gt;
A kártyákon speciális célú Linux kernel fut melyre a belépés limitáltan csak a rendszergazdák számára lehetséges SSH-n keresztül a hoszt gépről. Természetesen a kártyára belépve olyan érzésünk lehet, mintha egy sok processzoros gépen dolgoznánk, ami rendelkezik rengeteg memóriával. Ez így is van, de a hatékonyabb kihasználás érdekében az SSH-n keresztüli elérés nem lehetséges, csak a Slurm segítségével valamint a programozási nyelvek által támogatott csatornákon keresztül. Ennek részletezése lentebb megtalálható.&lt;br /&gt;
&lt;br /&gt;
==Használati módok==&lt;br /&gt;
A koprocesszor használatai módjait két csoportba lehet sorolni. A '''natív''' és az '''offload''' használatot.  &amp;lt;br/&amp;gt;&lt;br /&gt;
Ezen használati módokról a lenti fejezetekben több részletet is megtudhat. Az itt megfogalmazott &amp;quot;támogatás&amp;quot; csak a mi infrastruktúránkra érvényes! &amp;lt;br /&amp;gt;&lt;br /&gt;
A bemutatott példák ''c++'' nyelven vannak, de természetesen ''c'' és ''fortran'' esetén is megtalálható az ekvivalens módszer.&lt;br /&gt;
&lt;br /&gt;
===a) nem támogatott mód===&lt;br /&gt;
====közvetlen natív használat====&lt;br /&gt;
A natív mód arra szolgál, hogy a MIC kártyára  lefordított alkalmazásunkat felmásoljuk a kártyára, majd belépünk SSH-n a kártyára és azon futtatjuk a bináris kódunkat. &amp;lt;br /&amp;gt;&lt;br /&gt;
Ez a legegyszerűbb használati mód, ugyanis nincs más dolgunk, csak a '''-mmic''' kapcsolót használni a fordításnál és a fordító egy máris a MIC architektúrára kész bináris készít nekünk. &amp;lt;br/&amp;gt;&lt;br /&gt;
Workflow a következő lenne:&lt;br /&gt;
# Alkalmazás megírása. például [[Média:hello.cc|''hello.cc'']]&lt;br /&gt;
# Alkalmazás fordítása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;icpc -o hello-MIC -mmic hello.cc&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Másolás a MIC kártyára &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;scp hello-MIC mic0:&amp;lt;/pre&amp;gt;&lt;br /&gt;
# belépés a kártyára &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;ssh mic0&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Alkalmazás futtatása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;./hello-MIC&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===b) támogatott, de nem preferált használat===&lt;br /&gt;
====automatikus kódátvitel====&lt;br /&gt;
Ebben az esetben nem kell átmásolnunk a bináris alkalmazásunkat a kártyára sőt még csak be se kell lépnünk, mert ezt a két lépést egy beépített tool végzi el. &amp;lt;br/&amp;gt;&lt;br /&gt;
A tool eléréséhez szükséges parancs neve: '''micnativeloadex'''&lt;br /&gt;
&lt;br /&gt;
Ebben az esetben a workflow a következőképpen módosul:&lt;br /&gt;
# Alkalmazás megírása. például [[Média:hello.cc|''hello.cc'']]&lt;br /&gt;
# Alkalmazás fordítása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;icpc -o hello-MIC -mmic hello.cc&amp;lt;/pre&amp;gt;&lt;br /&gt;
# A tool futásához szükséges library-k megadása &amp;lt;br&amp;gt; &amp;lt;pre&amp;gt;export SINK_LD_LIBRARY_PATH=/opt/intel/composerxe/compiler/lib/mic&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Alkalmazás futtatása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;micnativeloadex hello-MIC&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| BUDAPEST2[cn10] phi (0)$ export SINK_LD_LIBRARY_PATH=/opt/intel/composerxe/compiler/lib/mic &amp;lt;br/&amp;gt;BUDAPEST2[cn10] phi (0)$ micnativeloadex hello-MIC &amp;lt;br/&amp;gt;Hello world! I have 244 logical cores.&amp;lt;br/&amp;gt;BUDAPEST2[cn10] phi (0)$&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===c) támogatott módok===&lt;br /&gt;
====Offload====&lt;br /&gt;
Az Offload modell esetén szükséges a forráskód módosítása, mégpedig úgy, hogy azon kódrészletet, amit a koprocesszoron szeretnénk futtatni, ki kell egészíteni egy fordítási direktívával. Az alkalmazás fordításakor elegendő csak a CPU-ra való binárist készíteni mivel a modell végzi a megfelelő forrás részlet mozgatását és futtatását.&amp;lt;br /&amp;gt;&lt;br /&gt;
[[Fájl:offload.png|Offload modell]] &amp;lt;br /&amp;gt;&lt;br /&gt;
Példánkban jól látható, hogy a második kiíratás már a koprocesszoron fut.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
int main(int argc, char * argv[]) {&lt;br /&gt;
  printf(&amp;quot;Hello world from host!\n&amp;quot;);&lt;br /&gt;
#pragma offload target(mic)&lt;br /&gt;
  {&lt;br /&gt;
    printf(&amp;quot;Hello world from coprocessor!\n&amp;quot;); fflush(stdout);&lt;br /&gt;
  }&lt;br /&gt;
  printf(&amp;quot;Bye\n&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ebben a példában az operációs rendszer választja ki, hogy melyik Phi kártyán fusson le a kódrészlet. Amennyiben mi szeretnénk explicite megmondani, hogy melyik kártyán fusson, akkor a fordítási direktívában a mic kulcsszó után kell tenni egy &amp;quot;:&amp;quot;-t és a kártya sorszámát [mi esetünkben 0 vagy 1]. Ekkor így nézne ki a direktíva: ''#pragma offload target(mic:1)''&lt;br /&gt;
&lt;br /&gt;
Amennyiben az offload szekcióban függvény hívás is szerepel, akkor szükségeltetik az függvény futásához szükséges változók adatterületének átmásolása a Phi kártyára, majd a lefutás után visszamásolni a hoszt gépre. Ezen másolást az ''offload_attribute'' ''push'' és ''pop'' paraméterei segítségével határozhatjuk meg. &amp;lt;br /&amp;gt;&lt;br /&gt;
A következő mátrix-vektor szorzás példánál az A mátrix globális változó, míg a b és c vektorok a függvény paraméterei.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//A változók felmásolásra kerülnek a Xeon Phi-re&lt;br /&gt;
#pragma offload_attribute(push, target(mic))&lt;br /&gt;
  double* A;&lt;br /&gt;
  void MatrixVector(double *b, double *c, double m, double n) {&lt;br /&gt;
    ...&lt;br /&gt;
    c[] = A[..]*b[]&lt;br /&gt;
  }&lt;br /&gt;
// felmásolt objektumok visszamásolása&lt;br /&gt;
#pragma offload_attribute(pop)&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
  ...&lt;br /&gt;
#pragma offload target(mic) in(A[0:n*m])&lt;br /&gt;
// így is meg lehetne adni: #pragma offload target(mic:0) in(A : length(n*m))	        &lt;br /&gt;
  {&lt;br /&gt;
    MatrixVector(b, c, m, n);&lt;br /&gt;
  }&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ekkor minden memória területet 2x kell másolni, egyszer fel a kártyára, majd másodszor vissza a hoszt gépre, pedig elegendő lenne csak az eredmény vektort visszamásolni. Ezen optimalizálással egy későbbi fejezetben foglalkozunk. &amp;lt;p /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Futás során kaphatunk részletes riportot is, hogy pontosan milyen adatmozgatások történnek az alkalmazás futása során. Ehhez nincs szükség másra csak az '''OFFLOAD_REPORT''' környezeti változó megfelelő beállítása. A beállított értékek 1, 2 vagy 3 lehetnek, attól függően mennyire részletes riportot szeretnénk kapni. &amp;lt;br /&amp;gt;&lt;br /&gt;
beállítás:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export OFFLOAD_REPORT=3&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
példa kimenet a hello alkalmazásra:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Hello world from host!&lt;br /&gt;
[Offload] [MIC 0] [File]                    hello_offload.cc&lt;br /&gt;
[Offload] [MIC 0] [Line]                    5&lt;br /&gt;
[Offload] [MIC 0] [Tag]                     Tag 0&lt;br /&gt;
Hello world from coprocessor!&lt;br /&gt;
[Offload] [HOST]  [Tag 0] [CPU Time]        0.554653(seconds)&lt;br /&gt;
[Offload] [MIC 0] [Tag 0] [CPU-&amp;gt;MIC Data]   0 (bytes)&lt;br /&gt;
[Offload] [MIC 0] [Tag 0] [MIC Time]        0.000140(seconds)&lt;br /&gt;
[Offload] [MIC 0] [Tag 0] [MIC-&amp;gt;CPU Data]   0 (bytes)&lt;br /&gt;
Bye&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Offload + OpenMP====&lt;br /&gt;
Amennyiben nem csak 1 processzort szeretnénk használni a Phi kártyánkon és az alkalmazásunkban használt számítás megengedi a párhuzamosítást, akkor az OpenMP segítségével párhuzamosíthatjuk a megfelelő számítást. &amp;lt;br /&amp;gt;&lt;br /&gt;
[[Fájl:Offload+openmp.png|Offload modell és OpenMP]] &amp;lt;br /&amp;gt;&lt;br /&gt;
Példánk maradjon ugyanúgy a mátrix-vektor szorzás. Amennyiben a teljes kártya adta kapacitást ki szeretnénk használni, akkor az OMP threadek számát 240-re kell beállítani. Azért nem 244-re, mivel 1 CPU-t dedikáltan csak a kommunikációra használ a rendszer és 1 CPU 4 hardver threaddel rendelkezik.&lt;br /&gt;
Ekkor a példánk így változik:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;omp.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#pragma offload_attribute(push, target(mic))&lt;br /&gt;
void multiply(int n, int m, double* A, double* b, double* c){&lt;br /&gt;
  const int nCPUs = omp_get_max_threads();   //240&lt;br /&gt;
  omp_set_num_threads(nCPUs);&lt;br /&gt;
#pragma omp parallel for schedule(dynamic, 1)&lt;br /&gt;
  for ( int i = 0 ; i &amp;lt; m ; i++)&lt;br /&gt;
     for ( int j = 0 ; j &amp;lt; n ; j++)&lt;br /&gt;
       c[i] += A[i*n+j] * b[j];&lt;br /&gt;
}&lt;br /&gt;
#pragma offload_attribute(pop)&lt;br /&gt;
&lt;br /&gt;
int main(){&lt;br /&gt;
  double * A = (double*) malloc(sizeof(double)*n*m);&lt;br /&gt;
  ...&lt;br /&gt;
#pragma offload target(mic) in(A:length(n*m))&lt;br /&gt;
     multiply(n, m, A, b, c);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Az alkalmazás fordításánál a fordítónak meg kell adni, hogy használhja az openmp library-ket is. Ezt a '''-qopenmp''' kapcsolóval tudjuk elérni.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
icpc -o matrix-CPU -qopenmp matrix.cc&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====MPI + Offload + OpenMP====&lt;br /&gt;
Abban az esetben, ha 1 számítási node nem elég, MPI segítségével össze tudunk kötni több nodet, heterogén rendszert alkotva, és azokon alkalmazható a fenti Offload + OpenMP modell. &amp;lt;br /&amp;gt;&lt;br /&gt;
[[Fájl:Mpi+offload+openmp.png|MPI elosztás valamint Offload modell és OpenMP]] &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Alkalmazások optimalizálása a kártyára==&lt;br /&gt;
Ebben a fejezetben optimalizálási lehetőségeket mutatunk be, melyek segítségül szolgálhatnak az egyes alkalmazások hatékonyabb futása érdekében ezzel jobban kihasználni a rendelkezésre álló erőforrások adta számítási kapacitás.&lt;br /&gt;
&lt;br /&gt;
===Környezeti változók===&lt;br /&gt;
Lehetséges csak a Phi kártyákon használt környezeti változók beállítása, amit az alkalmazásunk használni is tud. &amp;lt;br /&amp;gt;&lt;br /&gt;
Példánkban az OpenMP esetén használható szálak számát állíthatjuk be:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export MIC_ENV_PREFIX=XEONPHI&lt;br /&gt;
export OMP_NUM_THREADS=12&lt;br /&gt;
export XEONPHI_OMP_NUM_THREADS=240&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
A beállított prefix tetszőleges karaktersorozat lehet, a lényeg, hogy ezt a beállított prefixet használjuk a környezeti változók esetén is!&lt;br /&gt;
 &lt;br /&gt;
===Hosszredukció===&lt;br /&gt;
Többnyire a fordítók, ha nem mondjuk meg explicite, a dupla pontosságú változatot választják a lehetséges változó- és függvény típusok közül. Amennyiben a számításunk nem igényli a dupla pontosságot, akkor használjuk a forrásban mindenhol az adott típus vagy függvény egyszeres pontosságú változatát, mellyel jelentős teljesítmény növekedést tudunk elérni. Továbbá az egyes műveletek futási ideje nem egyforma, így a műveletet helyettesíthetjük más művelettel, ami gyorsabb, akkor ismét javíthatunk a hatékonyságon. &amp;lt;br /&amp;gt;&lt;br /&gt;
Íme pár példa, amit alkalmazni lehet:&lt;br /&gt;
* &amp;quot;''/''&amp;quot; művelet helyett &amp;quot;''*''&amp;quot; -t használjunk, mert azt gyorsabban végre tudja hajtani a processzor&lt;br /&gt;
* ''pow'' =&amp;gt; ''1.0 / sqrt()''    , tetszőleges hatványozás, pl.: 3/2-ik hatvány&lt;br /&gt;
* hardver áltatl támogatott műveletek:&lt;br /&gt;
** ''exp'' =&amp;gt; ''exp2''&lt;br /&gt;
** ''log'' =&amp;gt; ''log2''&lt;br /&gt;
* dupla pontosság -&amp;gt; egyszeres pontosság lebegőpontos számok és műveletek esetén &lt;br /&gt;
** ''1.0'' =&amp;gt; ''1.0f''&lt;br /&gt;
** ''pow'' =&amp;gt; ''powf''&lt;br /&gt;
&lt;br /&gt;
===Vektorizáció===&lt;br /&gt;
A vektorizáció azon fordítási időben eldönthető optimalizációs faktor, amely nagy mértékben függ attól a hardver felépítésétől, amire éppen a fordítás történni fog. Legfontosabb befolyásoló tényezők a processzor cache méretei illetve a vektor műveletekért felelős aritmetikai egységének jellemzői mivel ezeket használja fel a fordító az optimalizálási folyamata során. &amp;lt;br /&amp;gt;&lt;br /&gt;
Az automatikus vektorizáció minimum -O2 szinttől érvényes, de mivel általában ez a szint az alapértelmezett, ezért minden fordításnál alkalmazásra kerül. &amp;lt;br /&amp;gt;&lt;br /&gt;
A fordító programnak a következő kapcsolókat lehet megadni, melyek segítenek a minél jobb vektorizációban:&lt;br /&gt;
* ''-qopt-report=&amp;lt;szám&amp;gt;'' ; az optimalizációs jelentés részletességét lehet megadni 0-tól 5-ig, ahol az 5-s szint a legrészletesebbet jelenti&lt;br /&gt;
* ''-qopt-report-phase:vec'' ; a vektorizációról szeretnék jelentést kapni&lt;br /&gt;
* ''-opt-report-file=&amp;lt;fájlnév&amp;gt;'' ; megadható a riportfájl neve&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
icpc autovec.cc -mmic -qopt-report=5 -opt-report-file=vectorization.oMIC.optrpt -qopt-report-phase:vec&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====SIMD====&lt;br /&gt;
Az adatok hatékonyabb feldolgozására az egyik megvalósítás a SIMD (Single Instruction Multiple Data), amikor is a különálló adatokat úgy kezeli, mintha az vektorok volnának és azon végzi el a vektor-műveletet. Ezzel egy a hardver által támogatott műveletet végzünk egy lépésben egy nagyobb adatmennyiségen, ezzel párhuzamosítva a feldolgozási folyamatot. Ezzel a módszerrel nagyon jó adat szintű párhuzamosítást lehet elérni.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Fájl:Simd.png|SIMD]]&lt;br /&gt;
&lt;br /&gt;
Főbb támogatott műveletek:&lt;br /&gt;
* aritmetikai műveletek, bináris operátorok&lt;br /&gt;
* összehasonlítások&lt;br /&gt;
* konverziók és típus kasztolások&lt;br /&gt;
* bit-műveletek: NOT, AND, OR, XOR, XAND&lt;br /&gt;
&lt;br /&gt;
Használata: '''simd''' fordítási direktívával lehetséges &amp;lt;br /&amp;gt;&lt;br /&gt;
Például:&lt;br /&gt;
* egyszerű vektorizáció&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma simd&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
  A[i] += B[i];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* adat párhuzamosítás OpenMP segítségével&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for simd&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
  DoSomeWork(A[i]);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====AoS -&amp;gt; SoA====&lt;br /&gt;
A kiindulás probléma az, hogy a struktúrákat tartalmazó vektort nem lehet jól vektorizálni, mivel a cache-ben tárolt adatok nagy többsége feleslegesen van bent, mivel azon adatokat csak egy későbbi ciklus futás során fogja az alkalmazás feldolgozni. Tehát ezen adatok jelenleg feleslegesen foglalják a cache területet, ahelyett, hogy hasznos adatot tárolnánk ott.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
struct MyStructure {&lt;br /&gt;
  float x, y, z;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void My_Function(cons int nItem, MyStructure* const Items, const float delta) {&lt;br /&gt;
  for (int i = 0; i &amp;lt; nItem; i++) {&lt;br /&gt;
    ...&lt;br /&gt;
    for (int j = 0; j &amp;lt; nItem; j++) {&lt;br /&gt;
      const float dx = Items[j].x - Items[i].x;&lt;br /&gt;
      const float dy = Items[j].y - Items[i].y;&lt;br /&gt;
      const float dz = Items[j].z - Items[i].z;&lt;br /&gt;
      ...  &lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  ... &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A vektorban a struktúrák tárolása helyett minden elemnek egy dedikált vektort allokálunk és így könnyen tudja a fordító vektorizálni azt. Természetesen segítségként a vektor feldolgozását &amp;quot;tömbösítettük&amp;quot; a tileSize méret megadásával illetve a ''unroll'' fordítási direktíva segítségével.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
struct MyStructureSet {&lt;br /&gt;
  float *x, *y, *z;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void My_Function(cons int nItem, MyStructure* const Items, const float delta) {&lt;br /&gt;
  const int tileSize = 16;&lt;br /&gt;
#pragma omp parallel for schedule(guided)&lt;br /&gt;
  for (int ii = 0; ii &amp;lt; nItem; ii+= tileSize) {&lt;br /&gt;
    ...&lt;br /&gt;
#pragma unroll(tileSize)&lt;br /&gt;
    for (int j = 0; j &amp;lt; nItem; j++) {&lt;br /&gt;
#pragma unroll(tileSize)&lt;br /&gt;
      for (int i = ii; i &amp;lt; ii + tileSize; i++) {&lt;br /&gt;
        const float dx = Items.x[j] - Items.x[i];&lt;br /&gt;
        const float dy = Items.y[j] - Items.y[i];&lt;br /&gt;
        const float dz = Items.z[j] - Items.z[i];&lt;br /&gt;
        ...&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  ... &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Hatékony hossz választás====&lt;br /&gt;
Olyan esetben jelent előnyt, ha például az n*n-es mátrix esetén n nem osztható 16-val, mert akkor a vektorizáció nem teljesen hajtható végre. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// n%16 != 0&lt;br /&gt;
float* A = _mm_malloc(sizeof(float)*n*n, 64);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Ilyen esetben mindig marad egy kis vektor részlet, amire elemenként kell elvégezni a műveletet.&amp;lt;br /&amp;gt;&lt;br /&gt;
A megoldást az jelenti, hogy a memória allokálást kiterjesztjük és nagyobb memória területet foglalunk a változónak, így már alkalmazható lesz a vektorizáció, mivel a maradvány területen is el tudjuk végezni a műveletet egy lépésben.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int lda=n;&lt;br /&gt;
if (n % 16 != 0) lda += (16 - n%16); // most lda%16==0&lt;br /&gt;
float* A = _mm_malloc(sizeof(float)*n*lda, 64);&lt;br /&gt;
&lt;br /&gt;
// használat&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
  for (int j = 0; j &amp;lt; n; j++)&lt;br /&gt;
    A[i*lda + j] = ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====&amp;quot;Héj ciklusok&amp;quot; kiküszöbölése====&lt;br /&gt;
Ciklusok esetén lehetséges, hogy a vektorizációt nem teljes mértékben ismeri fel a fordító program. &lt;br /&gt;
Ekkor segíthetünk a fordítónak egy fordítási direktívával ('''#pragma vector aligned'''), ha teljes mértékben biztosak vagyunk, hogy a ciklusunk vektorizálható!&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma vector aligned&lt;br /&gt;
  for (int j = 0; j &amp;lt; n; j++)&lt;br /&gt;
    A[i*lda + j] -= ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Ettől jelentős futás idejű javulást érhetünk el, mivel ezzel a #pragma-val kikapcsoltuk a futás idejű ellenőrzést, de előfordulhatnak olyan esetek, főleg amikor nem jól mértük fel a ciklusunkat és nem sikerült a vektorizálás, hogy ''Segmentation fault'' hibára fogunk futni.&lt;br /&gt;
&lt;br /&gt;
====Regularizáció====&lt;br /&gt;
Van egy ciklusunk, ami egy vektor elemeit módosítja. A processzor az adatokat &amp;quot;csokorba szedve&amp;quot; emeli be a belső kis méretű cache-be. Egyszerre természetesen több vektor elem is bekerül a a cache-be ezzel gyorsítva a feldolgozást. Ezen vektor iterációk valahogyan ráilleszkedik a vektorra.&lt;br /&gt;
&lt;br /&gt;
[[Fájl:regularization.png|Vektor regularizáció]]&lt;br /&gt;
&lt;br /&gt;
Abban az esetben, amikor ez az illesztés nem teljesen tökéletes (lsd. piros elemek), keletkeznem héj ciklusok, melyeket a regularizációval is eliminálni lehet. &amp;lt;br /&amp;gt;&lt;br /&gt;
A feladat eredetileg valahogyan így nézhet ki:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
for (int i = 0; i&amp;lt;n; i++)&lt;br /&gt;
  A[i]=...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ez a következő átalakítással már alkalmas vektorizációra és nem lesz &amp;quot;peel loop&amp;quot; probléma.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
for (int b=0; b&amp;lt;n; b++) {&lt;br /&gt;
  const int jStart = b- b%16; // mindig osztható 16-val&lt;br /&gt;
  ...&lt;br /&gt;
#pragma simd&lt;br /&gt;
  for (int j = jStart; j&amp;lt;n; j++)&lt;br /&gt;
    A[j]=...&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Párhuzamosítás===&lt;br /&gt;
====Párhuzamos szálak ütemezése====&lt;br /&gt;
====Ciklus kifejtés====&lt;br /&gt;
A technika már egy példa erejéig a vektorizációnál is látható, de ott nem került részletezésre. A megoldás lényege, hogy a kellően nagy ciklust szétdaraboljuk kisebb darabokra, amit már külön feldolgozhatóak, ezzel párhuzamosítva a folyamatokat. Az egy darab kiindulási ciklusból két darab egymásba ágyazott ciklust hozunk létre, ami ugyanazon a feladattéren dolgozik. &lt;br /&gt;
&lt;br /&gt;
Eredeti állapot:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++) {...}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Átírt változat:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int STRIP=1024;&lt;br /&gt;
for (int ii = 0; ii &amp;lt; n; ii += STRIP)&lt;br /&gt;
 for (int i = ii; i &amp;lt; ii+STRIP; i++) {...}&lt;br /&gt;
&lt;br /&gt;
// ha még van maradék elem, akkor azokon is végrehajtjuk a számításokat&lt;br /&gt;
if (n%STRIP != 0) {&lt;br /&gt;
  for (i=n-(n%STRIP)+1; i&amp;lt;n; i++) {...}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A darabok mérete (STRIP értéke) az adott rendszer és feladat függvényében változhat, tehát ez egyfajta &amp;quot;tuning&amp;quot; paraméter.&lt;br /&gt;
&lt;br /&gt;
====Automatikus ciklusösszevonás====&lt;br /&gt;
Az OpenMP támogatást nyújt több egymásba ágyazott ciklus &amp;quot;összevonására&amp;quot;. Ehhez csak a '''collaps''' fordítási direktívát és az összevonandó ciklusok számát kell megadni. &amp;lt;br /&amp;gt;&lt;br /&gt;
FONTOS: ekkor az automatikus vektorizáció kikapcsolásra kerül!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for collaps(2)&lt;br /&gt;
for (int i=0; i&amp;lt;n; i++)&lt;br /&gt;
  for( int j=0; j&amp;lt;m; j++){...}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A fneti példában a 2db for ciklis kerül párhuzamosításra úgy, hogy a fordítás során 1db ciklus generálódik belőle, ami a következő paraméterezéssel ekvivalens:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for&lt;br /&gt;
for (int c=0; c &amp;lt; n*m; c++) {&lt;br /&gt;
  int i = c/n;&lt;br /&gt;
  int j = c%n;&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Szigeti(AT)niif.hu</name></author>	</entry>

	<entry>
		<id>https://wiki.niif.hu/index.php?title=Intel_Xeon_Phi&amp;diff=3749</id>
		<title>Intel Xeon Phi</title>
		<link rel="alternate" type="text/html" href="https://wiki.niif.hu/index.php?title=Intel_Xeon_Phi&amp;diff=3749"/>
				<updated>2016-05-05T08:58:44Z</updated>
		
		<summary type="html">&lt;p&gt;Szigeti(AT)niif.hu: /* Automatikus ciklusösszevonás */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Az oldalon található információk segítséget nyújtanak a HPC felhasználóinknak, hogy alkalmazásaikat miként portolhatják valamint optimalizálhatják a hatékonyabb futást szem előtt tartva Intel Xeon Phi koprocesszor kártyákon.&lt;br /&gt;
&lt;br /&gt;
==Hardver bemutatása==&lt;br /&gt;
Intel® Xeon Phi™ Coprocessor 7120P&lt;br /&gt;
&amp;lt;gallery&amp;gt;&lt;br /&gt;
Fájl:Xeon-phi-family-rwd.png.rendition.intel.web.720.405.png|&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-  align=&amp;quot;center&amp;quot;&lt;br /&gt;
! CPU&lt;br /&gt;
! threads/Core&lt;br /&gt;
!max threads&lt;br /&gt;
! CPU Freq&lt;br /&gt;
! Memória&lt;br /&gt;
! L1 cache&lt;br /&gt;
! L2 cache&lt;br /&gt;
! Interfész&lt;br /&gt;
! Ptot&lt;br /&gt;
! Számítási kapacitás&lt;br /&gt;
|-  align=&amp;quot;center&amp;quot;&lt;br /&gt;
| 61&lt;br /&gt;
| 4 Hardver&lt;br /&gt;
| 244&lt;br /&gt;
| 1238 GHz&lt;br /&gt;
| 16GB GDDR5&lt;br /&gt;
| 32KB/core&lt;br /&gt;
| 512KB/core&lt;br /&gt;
| PCIe&lt;br /&gt;
| 300W&lt;br /&gt;
| 1,2 TFlop/s dupla pontosság&lt;br /&gt;
|-  align=&amp;quot;center&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
| OpenMP-nél csak '''240'''&lt;br /&gt;
|&lt;br /&gt;
| ~100 cycles&lt;br /&gt;
~174 GB/s&lt;br /&gt;
| ~3 cycles&lt;br /&gt;
| ~10 cycles&lt;br /&gt;
| ~7 GB/s&lt;br /&gt;
~1 microsecond&lt;br /&gt;
|&lt;br /&gt;
| 2,4 TFlop/s egyszeres pontosság&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Az Intel® 1. generációs Xeon Phi™ koprocesszorában található 61 processzor ugyanazt a 16GB memóriát éri el, de semmilyen módon se fér hozzá közvetlenül a hoszt gép fizikai memóriájához.&amp;lt;br /&amp;gt;&lt;br /&gt;
[[Budapest2_klaszter|Budapest2]] és [[HPC#Szupersz.C3.A1m.C3.ADt.C3.B3g.C3.A9peink_.C3.B6sszehasonl.C3.ADt.C3.A1sa|Debrecen3-Phi]] gépeinkben számítási nodonként 2-2 Phi™ koprocesszor található. &amp;lt;br/&amp;gt;&lt;br /&gt;
A kártyákon speciális célú Linux kernel fut melyre a belépés limitáltan csak a rendszergazdák számára lehetséges SSH-n keresztül a hoszt gépről. Természetesen a kártyára belépve olyan érzésünk lehet, mintha egy sok processzoros gépen dolgoznánk, ami rendelkezik rengeteg memóriával. Ez így is van, de a hatékonyabb kihasználás érdekében az SSH-n keresztüli elérés nem lehetséges, csak a Slurm segítségével valamint a programozási nyelvek által támogatott csatornákon keresztül. Ennek részletezése lentebb megtalálható.&lt;br /&gt;
&lt;br /&gt;
==Használati módok==&lt;br /&gt;
A koprocesszor használatai módjait két csoportba lehet sorolni. A '''natív''' és az '''offload''' használatot.  &amp;lt;br/&amp;gt;&lt;br /&gt;
Ezen használati módokról a lenti fejezetekben több részletet is megtudhat. Az itt megfogalmazott &amp;quot;támogatás&amp;quot; csak a mi infrastruktúránkra érvényes! &amp;lt;br /&amp;gt;&lt;br /&gt;
A bemutatott példák ''c++'' nyelven vannak, de természetesen ''c'' és ''fortran'' esetén is megtalálható az ekvivalens módszer.&lt;br /&gt;
&lt;br /&gt;
===a) nem támogatott mód===&lt;br /&gt;
====közvetlen natív használat====&lt;br /&gt;
A natív mód arra szolgál, hogy a MIC kártyára  lefordított alkalmazásunkat felmásoljuk a kártyára, majd belépünk SSH-n a kártyára és azon futtatjuk a bináris kódunkat. &amp;lt;br /&amp;gt;&lt;br /&gt;
Ez a legegyszerűbb használati mód, ugyanis nincs más dolgunk, csak a '''-mmic''' kapcsolót használni a fordításnál és a fordító egy máris a MIC architektúrára kész bináris készít nekünk. &amp;lt;br/&amp;gt;&lt;br /&gt;
Workflow a következő lenne:&lt;br /&gt;
# Alkalmazás megírása. például [[Média:hello.cc|''hello.cc'']]&lt;br /&gt;
# Alkalmazás fordítása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;icpc -o hello-MIC -mmic hello.cc&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Másolás a MIC kártyára &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;scp hello-MIC mic0:&amp;lt;/pre&amp;gt;&lt;br /&gt;
# belépés a kártyára &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;ssh mic0&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Alkalmazás futtatása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;./hello-MIC&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===b) támogatott, de nem preferált használat===&lt;br /&gt;
====automatikus kódátvitel====&lt;br /&gt;
Ebben az esetben nem kell átmásolnunk a bináris alkalmazásunkat a kártyára sőt még csak be se kell lépnünk, mert ezt a két lépést egy beépített tool végzi el. &amp;lt;br/&amp;gt;&lt;br /&gt;
A tool eléréséhez szükséges parancs neve: '''micnativeloadex'''&lt;br /&gt;
&lt;br /&gt;
Ebben az esetben a workflow a következőképpen módosul:&lt;br /&gt;
# Alkalmazás megírása. például [[Média:hello.cc|''hello.cc'']]&lt;br /&gt;
# Alkalmazás fordítása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;icpc -o hello-MIC -mmic hello.cc&amp;lt;/pre&amp;gt;&lt;br /&gt;
# A tool futásához szükséges library-k megadása &amp;lt;br&amp;gt; &amp;lt;pre&amp;gt;export SINK_LD_LIBRARY_PATH=/opt/intel/composerxe/compiler/lib/mic&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Alkalmazás futtatása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;micnativeloadex hello-MIC&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| BUDAPEST2[cn10] phi (0)$ export SINK_LD_LIBRARY_PATH=/opt/intel/composerxe/compiler/lib/mic &amp;lt;br/&amp;gt;BUDAPEST2[cn10] phi (0)$ micnativeloadex hello-MIC &amp;lt;br/&amp;gt;Hello world! I have 244 logical cores.&amp;lt;br/&amp;gt;BUDAPEST2[cn10] phi (0)$&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===c) támogatott módok===&lt;br /&gt;
====Offload====&lt;br /&gt;
Az Offload modell esetén szükséges a forráskód módosítása, mégpedig úgy, hogy azon kódrészletet, amit a koprocesszoron szeretnénk futtatni, ki kell egészíteni egy fordítási direktívával. Az alkalmazás fordításakor elegendő csak a CPU-ra való binárist készíteni mivel a modell végzi a megfelelő forrás részlet mozgatását és futtatását.&amp;lt;br /&amp;gt;&lt;br /&gt;
[[Fájl:offload.png|Offload modell]] &amp;lt;br /&amp;gt;&lt;br /&gt;
Példánkban jól látható, hogy a második kiíratás már a koprocesszoron fut.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
int main(int argc, char * argv[]) {&lt;br /&gt;
  printf(&amp;quot;Hello world from host!\n&amp;quot;);&lt;br /&gt;
#pragma offload target(mic)&lt;br /&gt;
  {&lt;br /&gt;
    printf(&amp;quot;Hello world from coprocessor!\n&amp;quot;); fflush(stdout);&lt;br /&gt;
  }&lt;br /&gt;
  printf(&amp;quot;Bye\n&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ebben a példában az operációs rendszer választja ki, hogy melyik Phi kártyán fusson le a kódrészlet. Amennyiben mi szeretnénk explicite megmondani, hogy melyik kártyán fusson, akkor a fordítási direktívában a mic kulcsszó után kell tenni egy &amp;quot;:&amp;quot;-t és a kártya sorszámát [mi esetünkben 0 vagy 1]. Ekkor így nézne ki a direktíva: ''#pragma offload target(mic:1)''&lt;br /&gt;
&lt;br /&gt;
Amennyiben az offload szekcióban függvény hívás is szerepel, akkor szükségeltetik az függvény futásához szükséges változók adatterületének átmásolása a Phi kártyára, majd a lefutás után visszamásolni a hoszt gépre. Ezen másolást az ''offload_attribute'' ''push'' és ''pop'' paraméterei segítségével határozhatjuk meg. &amp;lt;br /&amp;gt;&lt;br /&gt;
A következő mátrix-vektor szorzás példánál az A mátrix globális változó, míg a b és c vektorok a függvény paraméterei.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//A változók felmásolásra kerülnek a Xeon Phi-re&lt;br /&gt;
#pragma offload_attribute(push, target(mic))&lt;br /&gt;
  double* A;&lt;br /&gt;
  void MatrixVector(double *b, double *c, double m, double n) {&lt;br /&gt;
    ...&lt;br /&gt;
    c[] = A[..]*b[]&lt;br /&gt;
  }&lt;br /&gt;
// felmásolt objektumok visszamásolása&lt;br /&gt;
#pragma offload_attribute(pop)&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
  ...&lt;br /&gt;
#pragma offload target(mic) in(A[0:n*m])&lt;br /&gt;
// így is meg lehetne adni: #pragma offload target(mic:0) in(A : length(n*m))	        &lt;br /&gt;
  {&lt;br /&gt;
    MatrixVector(b, c, m, n);&lt;br /&gt;
  }&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ekkor minden memória területet 2x kell másolni, egyszer fel a kártyára, majd másodszor vissza a hoszt gépre, pedig elegendő lenne csak az eredmény vektort visszamásolni. Ezen optimalizálással egy későbbi fejezetben foglalkozunk. &amp;lt;p /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Futás során kaphatunk részletes riportot is, hogy pontosan milyen adatmozgatások történnek az alkalmazás futása során. Ehhez nincs szükség másra csak az '''OFFLOAD_REPORT''' környezeti változó megfelelő beállítása. A beállított értékek 1, 2 vagy 3 lehetnek, attól függően mennyire részletes riportot szeretnénk kapni. &amp;lt;br /&amp;gt;&lt;br /&gt;
beállítás:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export OFFLOAD_REPORT=3&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
példa kimenet a hello alkalmazásra:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Hello world from host!&lt;br /&gt;
[Offload] [MIC 0] [File]                    hello_offload.cc&lt;br /&gt;
[Offload] [MIC 0] [Line]                    5&lt;br /&gt;
[Offload] [MIC 0] [Tag]                     Tag 0&lt;br /&gt;
Hello world from coprocessor!&lt;br /&gt;
[Offload] [HOST]  [Tag 0] [CPU Time]        0.554653(seconds)&lt;br /&gt;
[Offload] [MIC 0] [Tag 0] [CPU-&amp;gt;MIC Data]   0 (bytes)&lt;br /&gt;
[Offload] [MIC 0] [Tag 0] [MIC Time]        0.000140(seconds)&lt;br /&gt;
[Offload] [MIC 0] [Tag 0] [MIC-&amp;gt;CPU Data]   0 (bytes)&lt;br /&gt;
Bye&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Offload + OpenMP====&lt;br /&gt;
Amennyiben nem csak 1 processzort szeretnénk használni a Phi kártyánkon és az alkalmazásunkban használt számítás megengedi a párhuzamosítást, akkor az OpenMP segítségével párhuzamosíthatjuk a megfelelő számítást. &amp;lt;br /&amp;gt;&lt;br /&gt;
[[Fájl:Offload+openmp.png|Offload modell és OpenMP]] &amp;lt;br /&amp;gt;&lt;br /&gt;
Példánk maradjon ugyanúgy a mátrix-vektor szorzás. Amennyiben a teljes kártya adta kapacitást ki szeretnénk használni, akkor az OMP threadek számát 240-re kell beállítani. Azért nem 244-re, mivel 1 CPU-t dedikáltan csak a kommunikációra használ a rendszer és 1 CPU 4 hardver threaddel rendelkezik.&lt;br /&gt;
Ekkor a példánk így változik:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;omp.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#pragma offload_attribute(push, target(mic))&lt;br /&gt;
void multiply(int n, int m, double* A, double* b, double* c){&lt;br /&gt;
  const int nCPUs = omp_get_max_threads();   //240&lt;br /&gt;
  omp_set_num_threads(nCPUs);&lt;br /&gt;
#pragma omp parallel for schedule(dynamic, 1)&lt;br /&gt;
  for ( int i = 0 ; i &amp;lt; m ; i++)&lt;br /&gt;
     for ( int j = 0 ; j &amp;lt; n ; j++)&lt;br /&gt;
       c[i] += A[i*n+j] * b[j];&lt;br /&gt;
}&lt;br /&gt;
#pragma offload_attribute(pop)&lt;br /&gt;
&lt;br /&gt;
int main(){&lt;br /&gt;
  double * A = (double*) malloc(sizeof(double)*n*m);&lt;br /&gt;
  ...&lt;br /&gt;
#pragma offload target(mic) in(A:length(n*m))&lt;br /&gt;
     multiply(n, m, A, b, c);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Az alkalmazás fordításánál a fordítónak meg kell adni, hogy használhja az openmp library-ket is. Ezt a '''-qopenmp''' kapcsolóval tudjuk elérni.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
icpc -o matrix-CPU -qopenmp matrix.cc&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====MPI + Offload + OpenMP====&lt;br /&gt;
Abban az esetben, ha 1 számítási node nem elég, MPI segítségével össze tudunk kötni több nodet, heterogén rendszert alkotva, és azokon alkalmazható a fenti Offload + OpenMP modell. &amp;lt;br /&amp;gt;&lt;br /&gt;
[[Fájl:Mpi+offload+openmp.png|MPI elosztás valamint Offload modell és OpenMP]] &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Alkalmazások optimalizálása a kártyára==&lt;br /&gt;
Ebben a fejezetben optimalizálási lehetőségeket mutatunk be, melyek segítségül szolgálhatnak az egyes alkalmazások hatékonyabb futása érdekében ezzel jobban kihasználni a rendelkezésre álló erőforrások adta számítási kapacitás.&lt;br /&gt;
&lt;br /&gt;
===Környezeti változók===&lt;br /&gt;
Lehetséges csak a Phi kártyákon használt környezeti változók beállítása, amit az alkalmazásunk használni is tud. &amp;lt;br /&amp;gt;&lt;br /&gt;
Példánkban az OpenMP esetén használható szálak számát állíthatjuk be:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export MIC_ENV_PREFIX=XEONPHI&lt;br /&gt;
export OMP_NUM_THREADS=12&lt;br /&gt;
export XEONPHI_OMP_NUM_THREADS=240&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
A beállított prefix tetszőleges karaktersorozat lehet, a lényeg, hogy ezt a beállított prefixet használjuk a környezeti változók esetén is!&lt;br /&gt;
 &lt;br /&gt;
===Hosszredukció===&lt;br /&gt;
Többnyire a fordítók, ha nem mondjuk meg explicite, a dupla pontosságú változatot választják a lehetséges változó- és függvény típusok közül. Amennyiben a számításunk nem igényli a dupla pontosságot, akkor használjuk a forrásban mindenhol az adott típus vagy függvény egyszeres pontosságú változatát, mellyel jelentős teljesítmény növekedést tudunk elérni. Továbbá az egyes műveletek futási ideje nem egyforma, így a műveletet helyettesíthetjük más művelettel, ami gyorsabb, akkor ismét javíthatunk a hatékonyságon. &amp;lt;br /&amp;gt;&lt;br /&gt;
Íme pár példa, amit alkalmazni lehet:&lt;br /&gt;
* &amp;quot;''/''&amp;quot; művelet helyett &amp;quot;''*''&amp;quot; -t használjunk, mert azt gyorsabban végre tudja hajtani a processzor&lt;br /&gt;
* ''pow'' =&amp;gt; ''1.0 / sqrt()''    , tetszőleges hatványozás, pl.: 3/2-ik hatvány&lt;br /&gt;
* hardver áltatl támogatott műveletek:&lt;br /&gt;
** ''exp'' =&amp;gt; ''exp2''&lt;br /&gt;
** ''log'' =&amp;gt; ''log2''&lt;br /&gt;
* dupla pontosság -&amp;gt; egyszeres pontosság lebegőpontos számok és műveletek esetén &lt;br /&gt;
** ''1.0'' =&amp;gt; ''1.0f''&lt;br /&gt;
** ''pow'' =&amp;gt; ''powf''&lt;br /&gt;
&lt;br /&gt;
===Vektorizáció===&lt;br /&gt;
A vektorizáció azon fordítási időben eldönthető optimalizációs faktor, amely nagy mértékben függ attól a hardver felépítésétől, amire éppen a fordítás történni fog. Legfontosabb befolyásoló tényezők a processzor cache méretei illetve a vektor műveletekért felelős aritmetikai egységének jellemzői mivel ezeket használja fel a fordító az optimalizálási folyamata során. &amp;lt;br /&amp;gt;&lt;br /&gt;
Az automatikus vektorizáció minimum -O2 szinttől érvényes, de mivel általában ez a szint az alapértelmezett, ezért minden fordításnál alkalmazásra kerül. &amp;lt;br /&amp;gt;&lt;br /&gt;
A fordító programnak a következő kapcsolókat lehet megadni, melyek segítenek a minél jobb vektorizációban:&lt;br /&gt;
* ''-qopt-report=&amp;lt;szám&amp;gt;'' ; az optimalizációs jelentés részletességét lehet megadni 0-tól 5-ig, ahol az 5-s szint a legrészletesebbet jelenti&lt;br /&gt;
* ''-qopt-report-phase:vec'' ; a vektorizációról szeretnék jelentést kapni&lt;br /&gt;
* ''-opt-report-file=&amp;lt;fájlnév&amp;gt;'' ; megadható a riportfájl neve&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
icpc autovec.cc -mmic -qopt-report=5 -opt-report-file=vectorization.oMIC.optrpt -qopt-report-phase:vec&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====SIMD====&lt;br /&gt;
Az adatok hatékonyabb feldolgozására az egyik megvalósítás a SIMD (Single Instruction Multiple Data), amikor is a különálló adatokat úgy kezeli, mintha az vektorok volnának és azon végzi el a vektor-műveletet. Ezzel egy a hardver által támogatott műveletet végzünk egy lépésben egy nagyobb adatmennyiségen, ezzel párhuzamosítva a feldolgozási folyamatot. Ezzel a módszerrel nagyon jó adat szintű párhuzamosítást lehet elérni.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Fájl:Simd.png|SIMD]]&lt;br /&gt;
&lt;br /&gt;
Főbb támogatott műveletek:&lt;br /&gt;
* aritmetikai műveletek, bináris operátorok&lt;br /&gt;
* összehasonlítások&lt;br /&gt;
* konverziók és típus kasztolások&lt;br /&gt;
* bit-műveletek: NOT, AND, OR, XOR, XAND&lt;br /&gt;
&lt;br /&gt;
Használata: '''simd''' fordítási direktívával lehetséges &amp;lt;br /&amp;gt;&lt;br /&gt;
Például:&lt;br /&gt;
* egyszerű vektorizáció&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma simd&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
  A[i] += B[i];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* adat párhuzamosítás OpenMP segítségével&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for simd&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
  DoSomeWork(A[i]);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====AoS -&amp;gt; SoA====&lt;br /&gt;
A kiindulás probléma az, hogy a struktúrákat tartalmazó vektort nem lehet jól vektorizálni, mivel a cache-ben tárolt adatok nagy többsége feleslegesen van bent, mivel azon adatokat csak egy későbbi ciklus futás során fogja az alkalmazás feldolgozni. Tehát ezen adatok jelenleg feleslegesen foglalják a cache területet, ahelyett, hogy hasznos adatot tárolnánk ott.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
struct MyStructure {&lt;br /&gt;
  float x, y, z;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void My_Function(cons int nItem, MyStructure* const Items, const float delta) {&lt;br /&gt;
  for (int i = 0; i &amp;lt; nItem; i++) {&lt;br /&gt;
    ...&lt;br /&gt;
    for (int j = 0; j &amp;lt; nItem; j++) {&lt;br /&gt;
      const float dx = Items[j].x - Items[i].x;&lt;br /&gt;
      const float dy = Items[j].y - Items[i].y;&lt;br /&gt;
      const float dz = Items[j].z - Items[i].z;&lt;br /&gt;
      ...  &lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  ... &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A vektorban a struktúrák tárolása helyett minden elemnek egy dedikált vektort allokálunk és így könnyen tudja a fordító vektorizálni azt. Természetesen segítségként a vektor feldolgozását &amp;quot;tömbösítettük&amp;quot; a tileSize méret megadásával illetve a ''unroll'' fordítási direktíva segítségével.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
struct MyStructureSet {&lt;br /&gt;
  float *x, *y, *z;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void My_Function(cons int nItem, MyStructure* const Items, const float delta) {&lt;br /&gt;
  const int tileSize = 16;&lt;br /&gt;
#pragma omp parallel for schedule(guided)&lt;br /&gt;
  for (int ii = 0; ii &amp;lt; nItem; ii+= tileSize) {&lt;br /&gt;
    ...&lt;br /&gt;
#pragma unroll(tileSize)&lt;br /&gt;
    for (int j = 0; j &amp;lt; nItem; j++) {&lt;br /&gt;
#pragma unroll(tileSize)&lt;br /&gt;
      for (int i = ii; i &amp;lt; ii + tileSize; i++) {&lt;br /&gt;
        const float dx = Items.x[j] - Items.x[i];&lt;br /&gt;
        const float dy = Items.y[j] - Items.y[i];&lt;br /&gt;
        const float dz = Items.z[j] - Items.z[i];&lt;br /&gt;
        ...&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  ... &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Hatékony hossz választás====&lt;br /&gt;
Olyan esetben jelent előnyt, ha például az n*n-es mátrix esetén n nem osztható 16-val, mert akkor a vektorizáció nem teljesen hajtható végre. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// n%16 != 0&lt;br /&gt;
float* A = _mm_malloc(sizeof(float)*n*n, 64);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Ilyen esetben mindig marad egy kis vektor részlet, amire elemenként kell elvégezni a műveletet.&amp;lt;br /&amp;gt;&lt;br /&gt;
A megoldást az jelenti, hogy a memória allokálást kiterjesztjük és nagyobb memória területet foglalunk a változónak, így már alkalmazható lesz a vektorizáció, mivel a maradvány területen is el tudjuk végezni a műveletet egy lépésben.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int lda=n;&lt;br /&gt;
if (n % 16 != 0) lda += (16 - n%16); // most lda%16==0&lt;br /&gt;
float* A = _mm_malloc(sizeof(float)*n*lda, 64);&lt;br /&gt;
&lt;br /&gt;
// használat&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
  for (int j = 0; j &amp;lt; n; j++)&lt;br /&gt;
    A[i*lda + j] = ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====&amp;quot;Héj ciklusok&amp;quot; kiküszöbölése====&lt;br /&gt;
Ciklusok esetén lehetséges, hogy a vektorizációt nem teljes mértékben ismeri fel a fordító program. &lt;br /&gt;
Ekkor segíthetünk a fordítónak egy fordítási direktívával ('''#pragma vector aligned'''), ha teljes mértékben biztosak vagyunk, hogy a ciklusunk vektorizálható!&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma vector aligned&lt;br /&gt;
  for (int j = 0; j &amp;lt; n; j++)&lt;br /&gt;
    A[i*lda + j] -= ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Ettől jelentős futás idejű javulást érhetünk el, mivel ezzel a #pragma-val kikapcsoltuk a futás idejű ellenőrzést, de előfordulhatnak olyan esetek, főleg amikor nem jól mértük fel a ciklusunkat és nem sikerült a vektorizálás, hogy ''Segmentation fault'' hibára fogunk futni.&lt;br /&gt;
&lt;br /&gt;
====Regularizáció====&lt;br /&gt;
Van egy ciklusunk, ami egy vektor elemeit módosítja. A processzor az adatokat &amp;quot;csokorba szedve&amp;quot; emeli be a belső kis méretű cache-be. Egyszerre természetesen több vektor elem is bekerül a a cache-be ezzel gyorsítva a feldolgozást. Ezen vektor iterációk valahogyan ráilleszkedik a vektorra.&lt;br /&gt;
&lt;br /&gt;
[[Fájl:regularization.png|Vektor regularizáció]]&lt;br /&gt;
&lt;br /&gt;
Abban az esetben, amikor ez az illesztés nem teljesen tökéletes (lsd. piros elemek), keletkeznem héj ciklusok, melyeket a regularizációval is eliminálni lehet. &amp;lt;br /&amp;gt;&lt;br /&gt;
A feladat eredetileg valahogyan így nézhet ki:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
for (int i = 0; i&amp;lt;n; i++)&lt;br /&gt;
  A[i]=...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ez a következő átalakítással már alkalmas vektorizációra és nem lesz &amp;quot;peel loop&amp;quot; probléma.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
for (int b=0; b&amp;lt;n; b++) {&lt;br /&gt;
  const int jStart = b- b%16; // mindig osztható 16-val&lt;br /&gt;
  ...&lt;br /&gt;
#pragma simd&lt;br /&gt;
  for (int j = jStart; j&amp;lt;n; j++)&lt;br /&gt;
    A[j]=...&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Párhuzamosítás===&lt;br /&gt;
====Párhuzamos szálak ütemezése====&lt;br /&gt;
====Ciklus kifejtés====&lt;br /&gt;
====Automatikus ciklusösszevonás====&lt;br /&gt;
Az OpenMP támogatást nyújt több egymásba ágyazott ciklus &amp;quot;összevonására&amp;quot;. Ehhez csak a '''collaps''' fordítási direktívát és az összevonandó ciklusok számát kell megadni. &amp;lt;br /&amp;gt;&lt;br /&gt;
FONTOS: ekkor az automatikus vektorizáció kikapcsolásra kerül!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for collaps(2)&lt;br /&gt;
for (int i=0; i&amp;lt;n; i++)&lt;br /&gt;
  for( int j=0; j&amp;lt;m; j++){...}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A fneti példában a 2db for ciklis kerül párhuzamosításra úgy, hogy a fordítás során 1db ciklus generálódik belőle, ami a következő paraméterezéssel ekvivalens:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for&lt;br /&gt;
for (int c=0; c &amp;lt; n*m; c++) {&lt;br /&gt;
  int i = c/n;&lt;br /&gt;
  int j = c%n;&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Szigeti(AT)niif.hu</name></author>	</entry>

	<entry>
		<id>https://wiki.niif.hu/index.php?title=Intel_Xeon_Phi&amp;diff=3748</id>
		<title>Intel Xeon Phi</title>
		<link rel="alternate" type="text/html" href="https://wiki.niif.hu/index.php?title=Intel_Xeon_Phi&amp;diff=3748"/>
				<updated>2016-05-05T07:23:57Z</updated>
		
		<summary type="html">&lt;p&gt;Szigeti(AT)niif.hu: /* SIMD */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Az oldalon található információk segítséget nyújtanak a HPC felhasználóinknak, hogy alkalmazásaikat miként portolhatják valamint optimalizálhatják a hatékonyabb futást szem előtt tartva Intel Xeon Phi koprocesszor kártyákon.&lt;br /&gt;
&lt;br /&gt;
==Hardver bemutatása==&lt;br /&gt;
Intel® Xeon Phi™ Coprocessor 7120P&lt;br /&gt;
&amp;lt;gallery&amp;gt;&lt;br /&gt;
Fájl:Xeon-phi-family-rwd.png.rendition.intel.web.720.405.png|&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-  align=&amp;quot;center&amp;quot;&lt;br /&gt;
! CPU&lt;br /&gt;
! threads/Core&lt;br /&gt;
!max threads&lt;br /&gt;
! CPU Freq&lt;br /&gt;
! Memória&lt;br /&gt;
! L1 cache&lt;br /&gt;
! L2 cache&lt;br /&gt;
! Interfész&lt;br /&gt;
! Ptot&lt;br /&gt;
! Számítási kapacitás&lt;br /&gt;
|-  align=&amp;quot;center&amp;quot;&lt;br /&gt;
| 61&lt;br /&gt;
| 4 Hardver&lt;br /&gt;
| 244&lt;br /&gt;
| 1238 GHz&lt;br /&gt;
| 16GB GDDR5&lt;br /&gt;
| 32KB/core&lt;br /&gt;
| 512KB/core&lt;br /&gt;
| PCIe&lt;br /&gt;
| 300W&lt;br /&gt;
| 1,2 TFlop/s dupla pontosság&lt;br /&gt;
|-  align=&amp;quot;center&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
| OpenMP-nél csak '''240'''&lt;br /&gt;
|&lt;br /&gt;
| ~100 cycles&lt;br /&gt;
~174 GB/s&lt;br /&gt;
| ~3 cycles&lt;br /&gt;
| ~10 cycles&lt;br /&gt;
| ~7 GB/s&lt;br /&gt;
~1 microsecond&lt;br /&gt;
|&lt;br /&gt;
| 2,4 TFlop/s egyszeres pontosság&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Az Intel® 1. generációs Xeon Phi™ koprocesszorában található 61 processzor ugyanazt a 16GB memóriát éri el, de semmilyen módon se fér hozzá közvetlenül a hoszt gép fizikai memóriájához.&amp;lt;br /&amp;gt;&lt;br /&gt;
[[Budapest2_klaszter|Budapest2]] és [[HPC#Szupersz.C3.A1m.C3.ADt.C3.B3g.C3.A9peink_.C3.B6sszehasonl.C3.ADt.C3.A1sa|Debrecen3-Phi]] gépeinkben számítási nodonként 2-2 Phi™ koprocesszor található. &amp;lt;br/&amp;gt;&lt;br /&gt;
A kártyákon speciális célú Linux kernel fut melyre a belépés limitáltan csak a rendszergazdák számára lehetséges SSH-n keresztül a hoszt gépről. Természetesen a kártyára belépve olyan érzésünk lehet, mintha egy sok processzoros gépen dolgoznánk, ami rendelkezik rengeteg memóriával. Ez így is van, de a hatékonyabb kihasználás érdekében az SSH-n keresztüli elérés nem lehetséges, csak a Slurm segítségével valamint a programozási nyelvek által támogatott csatornákon keresztül. Ennek részletezése lentebb megtalálható.&lt;br /&gt;
&lt;br /&gt;
==Használati módok==&lt;br /&gt;
A koprocesszor használatai módjait két csoportba lehet sorolni. A '''natív''' és az '''offload''' használatot.  &amp;lt;br/&amp;gt;&lt;br /&gt;
Ezen használati módokról a lenti fejezetekben több részletet is megtudhat. Az itt megfogalmazott &amp;quot;támogatás&amp;quot; csak a mi infrastruktúránkra érvényes! &amp;lt;br /&amp;gt;&lt;br /&gt;
A bemutatott példák ''c++'' nyelven vannak, de természetesen ''c'' és ''fortran'' esetén is megtalálható az ekvivalens módszer.&lt;br /&gt;
&lt;br /&gt;
===a) nem támogatott mód===&lt;br /&gt;
====közvetlen natív használat====&lt;br /&gt;
A natív mód arra szolgál, hogy a MIC kártyára  lefordított alkalmazásunkat felmásoljuk a kártyára, majd belépünk SSH-n a kártyára és azon futtatjuk a bináris kódunkat. &amp;lt;br /&amp;gt;&lt;br /&gt;
Ez a legegyszerűbb használati mód, ugyanis nincs más dolgunk, csak a '''-mmic''' kapcsolót használni a fordításnál és a fordító egy máris a MIC architektúrára kész bináris készít nekünk. &amp;lt;br/&amp;gt;&lt;br /&gt;
Workflow a következő lenne:&lt;br /&gt;
# Alkalmazás megírása. például [[Média:hello.cc|''hello.cc'']]&lt;br /&gt;
# Alkalmazás fordítása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;icpc -o hello-MIC -mmic hello.cc&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Másolás a MIC kártyára &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;scp hello-MIC mic0:&amp;lt;/pre&amp;gt;&lt;br /&gt;
# belépés a kártyára &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;ssh mic0&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Alkalmazás futtatása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;./hello-MIC&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===b) támogatott, de nem preferált használat===&lt;br /&gt;
====automatikus kódátvitel====&lt;br /&gt;
Ebben az esetben nem kell átmásolnunk a bináris alkalmazásunkat a kártyára sőt még csak be se kell lépnünk, mert ezt a két lépést egy beépített tool végzi el. &amp;lt;br/&amp;gt;&lt;br /&gt;
A tool eléréséhez szükséges parancs neve: '''micnativeloadex'''&lt;br /&gt;
&lt;br /&gt;
Ebben az esetben a workflow a következőképpen módosul:&lt;br /&gt;
# Alkalmazás megírása. például [[Média:hello.cc|''hello.cc'']]&lt;br /&gt;
# Alkalmazás fordítása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;icpc -o hello-MIC -mmic hello.cc&amp;lt;/pre&amp;gt;&lt;br /&gt;
# A tool futásához szükséges library-k megadása &amp;lt;br&amp;gt; &amp;lt;pre&amp;gt;export SINK_LD_LIBRARY_PATH=/opt/intel/composerxe/compiler/lib/mic&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Alkalmazás futtatása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;micnativeloadex hello-MIC&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| BUDAPEST2[cn10] phi (0)$ export SINK_LD_LIBRARY_PATH=/opt/intel/composerxe/compiler/lib/mic &amp;lt;br/&amp;gt;BUDAPEST2[cn10] phi (0)$ micnativeloadex hello-MIC &amp;lt;br/&amp;gt;Hello world! I have 244 logical cores.&amp;lt;br/&amp;gt;BUDAPEST2[cn10] phi (0)$&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===c) támogatott módok===&lt;br /&gt;
====Offload====&lt;br /&gt;
Az Offload modell esetén szükséges a forráskód módosítása, mégpedig úgy, hogy azon kódrészletet, amit a koprocesszoron szeretnénk futtatni, ki kell egészíteni egy fordítási direktívával. Az alkalmazás fordításakor elegendő csak a CPU-ra való binárist készíteni mivel a modell végzi a megfelelő forrás részlet mozgatását és futtatását.&amp;lt;br /&amp;gt;&lt;br /&gt;
[[Fájl:offload.png|Offload modell]] &amp;lt;br /&amp;gt;&lt;br /&gt;
Példánkban jól látható, hogy a második kiíratás már a koprocesszoron fut.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
int main(int argc, char * argv[]) {&lt;br /&gt;
  printf(&amp;quot;Hello world from host!\n&amp;quot;);&lt;br /&gt;
#pragma offload target(mic)&lt;br /&gt;
  {&lt;br /&gt;
    printf(&amp;quot;Hello world from coprocessor!\n&amp;quot;); fflush(stdout);&lt;br /&gt;
  }&lt;br /&gt;
  printf(&amp;quot;Bye\n&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ebben a példában az operációs rendszer választja ki, hogy melyik Phi kártyán fusson le a kódrészlet. Amennyiben mi szeretnénk explicite megmondani, hogy melyik kártyán fusson, akkor a fordítási direktívában a mic kulcsszó után kell tenni egy &amp;quot;:&amp;quot;-t és a kártya sorszámát [mi esetünkben 0 vagy 1]. Ekkor így nézne ki a direktíva: ''#pragma offload target(mic:1)''&lt;br /&gt;
&lt;br /&gt;
Amennyiben az offload szekcióban függvény hívás is szerepel, akkor szükségeltetik az függvény futásához szükséges változók adatterületének átmásolása a Phi kártyára, majd a lefutás után visszamásolni a hoszt gépre. Ezen másolást az ''offload_attribute'' ''push'' és ''pop'' paraméterei segítségével határozhatjuk meg. &amp;lt;br /&amp;gt;&lt;br /&gt;
A következő mátrix-vektor szorzás példánál az A mátrix globális változó, míg a b és c vektorok a függvény paraméterei.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//A változók felmásolásra kerülnek a Xeon Phi-re&lt;br /&gt;
#pragma offload_attribute(push, target(mic))&lt;br /&gt;
  double* A;&lt;br /&gt;
  void MatrixVector(double *b, double *c, double m, double n) {&lt;br /&gt;
    ...&lt;br /&gt;
    c[] = A[..]*b[]&lt;br /&gt;
  }&lt;br /&gt;
// felmásolt objektumok visszamásolása&lt;br /&gt;
#pragma offload_attribute(pop)&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
  ...&lt;br /&gt;
#pragma offload target(mic) in(A[0:n*m])&lt;br /&gt;
// így is meg lehetne adni: #pragma offload target(mic:0) in(A : length(n*m))	        &lt;br /&gt;
  {&lt;br /&gt;
    MatrixVector(b, c, m, n);&lt;br /&gt;
  }&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ekkor minden memória területet 2x kell másolni, egyszer fel a kártyára, majd másodszor vissza a hoszt gépre, pedig elegendő lenne csak az eredmény vektort visszamásolni. Ezen optimalizálással egy későbbi fejezetben foglalkozunk. &amp;lt;p /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Futás során kaphatunk részletes riportot is, hogy pontosan milyen adatmozgatások történnek az alkalmazás futása során. Ehhez nincs szükség másra csak az '''OFFLOAD_REPORT''' környezeti változó megfelelő beállítása. A beállított értékek 1, 2 vagy 3 lehetnek, attól függően mennyire részletes riportot szeretnénk kapni. &amp;lt;br /&amp;gt;&lt;br /&gt;
beállítás:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export OFFLOAD_REPORT=3&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
példa kimenet a hello alkalmazásra:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Hello world from host!&lt;br /&gt;
[Offload] [MIC 0] [File]                    hello_offload.cc&lt;br /&gt;
[Offload] [MIC 0] [Line]                    5&lt;br /&gt;
[Offload] [MIC 0] [Tag]                     Tag 0&lt;br /&gt;
Hello world from coprocessor!&lt;br /&gt;
[Offload] [HOST]  [Tag 0] [CPU Time]        0.554653(seconds)&lt;br /&gt;
[Offload] [MIC 0] [Tag 0] [CPU-&amp;gt;MIC Data]   0 (bytes)&lt;br /&gt;
[Offload] [MIC 0] [Tag 0] [MIC Time]        0.000140(seconds)&lt;br /&gt;
[Offload] [MIC 0] [Tag 0] [MIC-&amp;gt;CPU Data]   0 (bytes)&lt;br /&gt;
Bye&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Offload + OpenMP====&lt;br /&gt;
Amennyiben nem csak 1 processzort szeretnénk használni a Phi kártyánkon és az alkalmazásunkban használt számítás megengedi a párhuzamosítást, akkor az OpenMP segítségével párhuzamosíthatjuk a megfelelő számítást. &amp;lt;br /&amp;gt;&lt;br /&gt;
[[Fájl:Offload+openmp.png|Offload modell és OpenMP]] &amp;lt;br /&amp;gt;&lt;br /&gt;
Példánk maradjon ugyanúgy a mátrix-vektor szorzás. Amennyiben a teljes kártya adta kapacitást ki szeretnénk használni, akkor az OMP threadek számát 240-re kell beállítani. Azért nem 244-re, mivel 1 CPU-t dedikáltan csak a kommunikációra használ a rendszer és 1 CPU 4 hardver threaddel rendelkezik.&lt;br /&gt;
Ekkor a példánk így változik:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;omp.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#pragma offload_attribute(push, target(mic))&lt;br /&gt;
void multiply(int n, int m, double* A, double* b, double* c){&lt;br /&gt;
  const int nCPUs = omp_get_max_threads();   //240&lt;br /&gt;
  omp_set_num_threads(nCPUs);&lt;br /&gt;
#pragma omp parallel for schedule(dynamic, 1)&lt;br /&gt;
  for ( int i = 0 ; i &amp;lt; m ; i++)&lt;br /&gt;
     for ( int j = 0 ; j &amp;lt; n ; j++)&lt;br /&gt;
       c[i] += A[i*n+j] * b[j];&lt;br /&gt;
}&lt;br /&gt;
#pragma offload_attribute(pop)&lt;br /&gt;
&lt;br /&gt;
int main(){&lt;br /&gt;
  double * A = (double*) malloc(sizeof(double)*n*m);&lt;br /&gt;
  ...&lt;br /&gt;
#pragma offload target(mic) in(A:length(n*m))&lt;br /&gt;
     multiply(n, m, A, b, c);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Az alkalmazás fordításánál a fordítónak meg kell adni, hogy használhja az openmp library-ket is. Ezt a '''-qopenmp''' kapcsolóval tudjuk elérni.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
icpc -o matrix-CPU -qopenmp matrix.cc&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====MPI + Offload + OpenMP====&lt;br /&gt;
Abban az esetben, ha 1 számítási node nem elég, MPI segítségével össze tudunk kötni több nodet, heterogén rendszert alkotva, és azokon alkalmazható a fenti Offload + OpenMP modell. &amp;lt;br /&amp;gt;&lt;br /&gt;
[[Fájl:Mpi+offload+openmp.png|MPI elosztás valamint Offload modell és OpenMP]] &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Alkalmazások optimalizálása a kártyára==&lt;br /&gt;
Ebben a fejezetben optimalizálási lehetőségeket mutatunk be, melyek segítségül szolgálhatnak az egyes alkalmazások hatékonyabb futása érdekében ezzel jobban kihasználni a rendelkezésre álló erőforrások adta számítási kapacitás.&lt;br /&gt;
&lt;br /&gt;
===Környezeti változók===&lt;br /&gt;
Lehetséges csak a Phi kártyákon használt környezeti változók beállítása, amit az alkalmazásunk használni is tud. &amp;lt;br /&amp;gt;&lt;br /&gt;
Példánkban az OpenMP esetén használható szálak számát állíthatjuk be:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export MIC_ENV_PREFIX=XEONPHI&lt;br /&gt;
export OMP_NUM_THREADS=12&lt;br /&gt;
export XEONPHI_OMP_NUM_THREADS=240&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
A beállított prefix tetszőleges karaktersorozat lehet, a lényeg, hogy ezt a beállított prefixet használjuk a környezeti változók esetén is!&lt;br /&gt;
 &lt;br /&gt;
===Hosszredukció===&lt;br /&gt;
Többnyire a fordítók, ha nem mondjuk meg explicite, a dupla pontosságú változatot választják a lehetséges változó- és függvény típusok közül. Amennyiben a számításunk nem igényli a dupla pontosságot, akkor használjuk a forrásban mindenhol az adott típus vagy függvény egyszeres pontosságú változatát, mellyel jelentős teljesítmény növekedést tudunk elérni. Továbbá az egyes műveletek futási ideje nem egyforma, így a műveletet helyettesíthetjük más művelettel, ami gyorsabb, akkor ismét javíthatunk a hatékonyságon. &amp;lt;br /&amp;gt;&lt;br /&gt;
Íme pár példa, amit alkalmazni lehet:&lt;br /&gt;
* &amp;quot;''/''&amp;quot; művelet helyett &amp;quot;''*''&amp;quot; -t használjunk, mert azt gyorsabban végre tudja hajtani a processzor&lt;br /&gt;
* ''pow'' =&amp;gt; ''1.0 / sqrt()''    , tetszőleges hatványozás, pl.: 3/2-ik hatvány&lt;br /&gt;
* hardver áltatl támogatott műveletek:&lt;br /&gt;
** ''exp'' =&amp;gt; ''exp2''&lt;br /&gt;
** ''log'' =&amp;gt; ''log2''&lt;br /&gt;
* dupla pontosság -&amp;gt; egyszeres pontosság lebegőpontos számok és műveletek esetén &lt;br /&gt;
** ''1.0'' =&amp;gt; ''1.0f''&lt;br /&gt;
** ''pow'' =&amp;gt; ''powf''&lt;br /&gt;
&lt;br /&gt;
===Vektorizáció===&lt;br /&gt;
A vektorizáció azon fordítási időben eldönthető optimalizációs faktor, amely nagy mértékben függ attól a hardver felépítésétől, amire éppen a fordítás történni fog. Legfontosabb befolyásoló tényezők a processzor cache méretei illetve a vektor műveletekért felelős aritmetikai egységének jellemzői mivel ezeket használja fel a fordító az optimalizálási folyamata során. &amp;lt;br /&amp;gt;&lt;br /&gt;
Az automatikus vektorizáció minimum -O2 szinttől érvényes, de mivel általában ez a szint az alapértelmezett, ezért minden fordításnál alkalmazásra kerül. &amp;lt;br /&amp;gt;&lt;br /&gt;
A fordító programnak a következő kapcsolókat lehet megadni, melyek segítenek a minél jobb vektorizációban:&lt;br /&gt;
* ''-qopt-report=&amp;lt;szám&amp;gt;'' ; az optimalizációs jelentés részletességét lehet megadni 0-tól 5-ig, ahol az 5-s szint a legrészletesebbet jelenti&lt;br /&gt;
* ''-qopt-report-phase:vec'' ; a vektorizációról szeretnék jelentést kapni&lt;br /&gt;
* ''-opt-report-file=&amp;lt;fájlnév&amp;gt;'' ; megadható a riportfájl neve&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
icpc autovec.cc -mmic -qopt-report=5 -opt-report-file=vectorization.oMIC.optrpt -qopt-report-phase:vec&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====SIMD====&lt;br /&gt;
Az adatok hatékonyabb feldolgozására az egyik megvalósítás a SIMD (Single Instruction Multiple Data), amikor is a különálló adatokat úgy kezeli, mintha az vektorok volnának és azon végzi el a vektor-műveletet. Ezzel egy a hardver által támogatott műveletet végzünk egy lépésben egy nagyobb adatmennyiségen, ezzel párhuzamosítva a feldolgozási folyamatot. Ezzel a módszerrel nagyon jó adat szintű párhuzamosítást lehet elérni.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Fájl:Simd.png|SIMD]]&lt;br /&gt;
&lt;br /&gt;
Főbb támogatott műveletek:&lt;br /&gt;
* aritmetikai műveletek, bináris operátorok&lt;br /&gt;
* összehasonlítások&lt;br /&gt;
* konverziók és típus kasztolások&lt;br /&gt;
* bit-műveletek: NOT, AND, OR, XOR, XAND&lt;br /&gt;
&lt;br /&gt;
Használata: '''simd''' fordítási direktívával lehetséges &amp;lt;br /&amp;gt;&lt;br /&gt;
Például:&lt;br /&gt;
* egyszerű vektorizáció&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma simd&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
  A[i] += B[i];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* adat párhuzamosítás OpenMP segítségével&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for simd&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
  DoSomeWork(A[i]);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====AoS -&amp;gt; SoA====&lt;br /&gt;
A kiindulás probléma az, hogy a struktúrákat tartalmazó vektort nem lehet jól vektorizálni, mivel a cache-ben tárolt adatok nagy többsége feleslegesen van bent, mivel azon adatokat csak egy későbbi ciklus futás során fogja az alkalmazás feldolgozni. Tehát ezen adatok jelenleg feleslegesen foglalják a cache területet, ahelyett, hogy hasznos adatot tárolnánk ott.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
struct MyStructure {&lt;br /&gt;
  float x, y, z;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void My_Function(cons int nItem, MyStructure* const Items, const float delta) {&lt;br /&gt;
  for (int i = 0; i &amp;lt; nItem; i++) {&lt;br /&gt;
    ...&lt;br /&gt;
    for (int j = 0; j &amp;lt; nItem; j++) {&lt;br /&gt;
      const float dx = Items[j].x - Items[i].x;&lt;br /&gt;
      const float dy = Items[j].y - Items[i].y;&lt;br /&gt;
      const float dz = Items[j].z - Items[i].z;&lt;br /&gt;
      ...  &lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  ... &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A vektorban a struktúrák tárolása helyett minden elemnek egy dedikált vektort allokálunk és így könnyen tudja a fordító vektorizálni azt. Természetesen segítségként a vektor feldolgozását &amp;quot;tömbösítettük&amp;quot; a tileSize méret megadásával illetve a ''unroll'' fordítási direktíva segítségével.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
struct MyStructureSet {&lt;br /&gt;
  float *x, *y, *z;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void My_Function(cons int nItem, MyStructure* const Items, const float delta) {&lt;br /&gt;
  const int tileSize = 16;&lt;br /&gt;
#pragma omp parallel for schedule(guided)&lt;br /&gt;
  for (int ii = 0; ii &amp;lt; nItem; ii+= tileSize) {&lt;br /&gt;
    ...&lt;br /&gt;
#pragma unroll(tileSize)&lt;br /&gt;
    for (int j = 0; j &amp;lt; nItem; j++) {&lt;br /&gt;
#pragma unroll(tileSize)&lt;br /&gt;
      for (int i = ii; i &amp;lt; ii + tileSize; i++) {&lt;br /&gt;
        const float dx = Items.x[j] - Items.x[i];&lt;br /&gt;
        const float dy = Items.y[j] - Items.y[i];&lt;br /&gt;
        const float dz = Items.z[j] - Items.z[i];&lt;br /&gt;
        ...&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  ... &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Hatékony hossz választás====&lt;br /&gt;
Olyan esetben jelent előnyt, ha például az n*n-es mátrix esetén n nem osztható 16-val, mert akkor a vektorizáció nem teljesen hajtható végre. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// n%16 != 0&lt;br /&gt;
float* A = _mm_malloc(sizeof(float)*n*n, 64);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Ilyen esetben mindig marad egy kis vektor részlet, amire elemenként kell elvégezni a műveletet.&amp;lt;br /&amp;gt;&lt;br /&gt;
A megoldást az jelenti, hogy a memória allokálást kiterjesztjük és nagyobb memória területet foglalunk a változónak, így már alkalmazható lesz a vektorizáció, mivel a maradvány területen is el tudjuk végezni a műveletet egy lépésben.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int lda=n;&lt;br /&gt;
if (n % 16 != 0) lda += (16 - n%16); // most lda%16==0&lt;br /&gt;
float* A = _mm_malloc(sizeof(float)*n*lda, 64);&lt;br /&gt;
&lt;br /&gt;
// használat&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
  for (int j = 0; j &amp;lt; n; j++)&lt;br /&gt;
    A[i*lda + j] = ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====&amp;quot;Héj ciklusok&amp;quot; kiküszöbölése====&lt;br /&gt;
Ciklusok esetén lehetséges, hogy a vektorizációt nem teljes mértékben ismeri fel a fordító program. &lt;br /&gt;
Ekkor segíthetünk a fordítónak egy fordítási direktívával ('''#pragma vector aligned'''), ha teljes mértékben biztosak vagyunk, hogy a ciklusunk vektorizálható!&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma vector aligned&lt;br /&gt;
  for (int j = 0; j &amp;lt; n; j++)&lt;br /&gt;
    A[i*lda + j] -= ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Ettől jelentős futás idejű javulást érhetünk el, mivel ezzel a #pragma-val kikapcsoltuk a futás idejű ellenőrzést, de előfordulhatnak olyan esetek, főleg amikor nem jól mértük fel a ciklusunkat és nem sikerült a vektorizálás, hogy ''Segmentation fault'' hibára fogunk futni.&lt;br /&gt;
&lt;br /&gt;
====Regularizáció====&lt;br /&gt;
Van egy ciklusunk, ami egy vektor elemeit módosítja. A processzor az adatokat &amp;quot;csokorba szedve&amp;quot; emeli be a belső kis méretű cache-be. Egyszerre természetesen több vektor elem is bekerül a a cache-be ezzel gyorsítva a feldolgozást. Ezen vektor iterációk valahogyan ráilleszkedik a vektorra.&lt;br /&gt;
&lt;br /&gt;
[[Fájl:regularization.png|Vektor regularizáció]]&lt;br /&gt;
&lt;br /&gt;
Abban az esetben, amikor ez az illesztés nem teljesen tökéletes (lsd. piros elemek), keletkeznem héj ciklusok, melyeket a regularizációval is eliminálni lehet. &amp;lt;br /&amp;gt;&lt;br /&gt;
A feladat eredetileg valahogyan így nézhet ki:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
for (int i = 0; i&amp;lt;n; i++)&lt;br /&gt;
  A[i]=...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ez a következő átalakítással már alkalmas vektorizációra és nem lesz &amp;quot;peel loop&amp;quot; probléma.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
for (int b=0; b&amp;lt;n; b++) {&lt;br /&gt;
  const int jStart = b- b%16; // mindig osztható 16-val&lt;br /&gt;
  ...&lt;br /&gt;
#pragma simd&lt;br /&gt;
  for (int j = jStart; j&amp;lt;n; j++)&lt;br /&gt;
    A[j]=...&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Párhuzamosítás===&lt;br /&gt;
====Párhuzamos szálak ütemezése====&lt;br /&gt;
====Ciklus kifejtés====&lt;br /&gt;
====Automatikus ciklusösszevonás====&lt;/div&gt;</summary>
		<author><name>Szigeti(AT)niif.hu</name></author>	</entry>

	<entry>
		<id>https://wiki.niif.hu/index.php?title=Intel_Xeon_Phi&amp;diff=3747</id>
		<title>Intel Xeon Phi</title>
		<link rel="alternate" type="text/html" href="https://wiki.niif.hu/index.php?title=Intel_Xeon_Phi&amp;diff=3747"/>
				<updated>2016-05-05T07:23:43Z</updated>
		
		<summary type="html">&lt;p&gt;Szigeti(AT)niif.hu: /* SIMD */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Az oldalon található információk segítséget nyújtanak a HPC felhasználóinknak, hogy alkalmazásaikat miként portolhatják valamint optimalizálhatják a hatékonyabb futást szem előtt tartva Intel Xeon Phi koprocesszor kártyákon.&lt;br /&gt;
&lt;br /&gt;
==Hardver bemutatása==&lt;br /&gt;
Intel® Xeon Phi™ Coprocessor 7120P&lt;br /&gt;
&amp;lt;gallery&amp;gt;&lt;br /&gt;
Fájl:Xeon-phi-family-rwd.png.rendition.intel.web.720.405.png|&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-  align=&amp;quot;center&amp;quot;&lt;br /&gt;
! CPU&lt;br /&gt;
! threads/Core&lt;br /&gt;
!max threads&lt;br /&gt;
! CPU Freq&lt;br /&gt;
! Memória&lt;br /&gt;
! L1 cache&lt;br /&gt;
! L2 cache&lt;br /&gt;
! Interfész&lt;br /&gt;
! Ptot&lt;br /&gt;
! Számítási kapacitás&lt;br /&gt;
|-  align=&amp;quot;center&amp;quot;&lt;br /&gt;
| 61&lt;br /&gt;
| 4 Hardver&lt;br /&gt;
| 244&lt;br /&gt;
| 1238 GHz&lt;br /&gt;
| 16GB GDDR5&lt;br /&gt;
| 32KB/core&lt;br /&gt;
| 512KB/core&lt;br /&gt;
| PCIe&lt;br /&gt;
| 300W&lt;br /&gt;
| 1,2 TFlop/s dupla pontosság&lt;br /&gt;
|-  align=&amp;quot;center&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
| OpenMP-nél csak '''240'''&lt;br /&gt;
|&lt;br /&gt;
| ~100 cycles&lt;br /&gt;
~174 GB/s&lt;br /&gt;
| ~3 cycles&lt;br /&gt;
| ~10 cycles&lt;br /&gt;
| ~7 GB/s&lt;br /&gt;
~1 microsecond&lt;br /&gt;
|&lt;br /&gt;
| 2,4 TFlop/s egyszeres pontosság&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Az Intel® 1. generációs Xeon Phi™ koprocesszorában található 61 processzor ugyanazt a 16GB memóriát éri el, de semmilyen módon se fér hozzá közvetlenül a hoszt gép fizikai memóriájához.&amp;lt;br /&amp;gt;&lt;br /&gt;
[[Budapest2_klaszter|Budapest2]] és [[HPC#Szupersz.C3.A1m.C3.ADt.C3.B3g.C3.A9peink_.C3.B6sszehasonl.C3.ADt.C3.A1sa|Debrecen3-Phi]] gépeinkben számítási nodonként 2-2 Phi™ koprocesszor található. &amp;lt;br/&amp;gt;&lt;br /&gt;
A kártyákon speciális célú Linux kernel fut melyre a belépés limitáltan csak a rendszergazdák számára lehetséges SSH-n keresztül a hoszt gépről. Természetesen a kártyára belépve olyan érzésünk lehet, mintha egy sok processzoros gépen dolgoznánk, ami rendelkezik rengeteg memóriával. Ez így is van, de a hatékonyabb kihasználás érdekében az SSH-n keresztüli elérés nem lehetséges, csak a Slurm segítségével valamint a programozási nyelvek által támogatott csatornákon keresztül. Ennek részletezése lentebb megtalálható.&lt;br /&gt;
&lt;br /&gt;
==Használati módok==&lt;br /&gt;
A koprocesszor használatai módjait két csoportba lehet sorolni. A '''natív''' és az '''offload''' használatot.  &amp;lt;br/&amp;gt;&lt;br /&gt;
Ezen használati módokról a lenti fejezetekben több részletet is megtudhat. Az itt megfogalmazott &amp;quot;támogatás&amp;quot; csak a mi infrastruktúránkra érvényes! &amp;lt;br /&amp;gt;&lt;br /&gt;
A bemutatott példák ''c++'' nyelven vannak, de természetesen ''c'' és ''fortran'' esetén is megtalálható az ekvivalens módszer.&lt;br /&gt;
&lt;br /&gt;
===a) nem támogatott mód===&lt;br /&gt;
====közvetlen natív használat====&lt;br /&gt;
A natív mód arra szolgál, hogy a MIC kártyára  lefordított alkalmazásunkat felmásoljuk a kártyára, majd belépünk SSH-n a kártyára és azon futtatjuk a bináris kódunkat. &amp;lt;br /&amp;gt;&lt;br /&gt;
Ez a legegyszerűbb használati mód, ugyanis nincs más dolgunk, csak a '''-mmic''' kapcsolót használni a fordításnál és a fordító egy máris a MIC architektúrára kész bináris készít nekünk. &amp;lt;br/&amp;gt;&lt;br /&gt;
Workflow a következő lenne:&lt;br /&gt;
# Alkalmazás megírása. például [[Média:hello.cc|''hello.cc'']]&lt;br /&gt;
# Alkalmazás fordítása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;icpc -o hello-MIC -mmic hello.cc&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Másolás a MIC kártyára &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;scp hello-MIC mic0:&amp;lt;/pre&amp;gt;&lt;br /&gt;
# belépés a kártyára &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;ssh mic0&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Alkalmazás futtatása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;./hello-MIC&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===b) támogatott, de nem preferált használat===&lt;br /&gt;
====automatikus kódátvitel====&lt;br /&gt;
Ebben az esetben nem kell átmásolnunk a bináris alkalmazásunkat a kártyára sőt még csak be se kell lépnünk, mert ezt a két lépést egy beépített tool végzi el. &amp;lt;br/&amp;gt;&lt;br /&gt;
A tool eléréséhez szükséges parancs neve: '''micnativeloadex'''&lt;br /&gt;
&lt;br /&gt;
Ebben az esetben a workflow a következőképpen módosul:&lt;br /&gt;
# Alkalmazás megírása. például [[Média:hello.cc|''hello.cc'']]&lt;br /&gt;
# Alkalmazás fordítása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;icpc -o hello-MIC -mmic hello.cc&amp;lt;/pre&amp;gt;&lt;br /&gt;
# A tool futásához szükséges library-k megadása &amp;lt;br&amp;gt; &amp;lt;pre&amp;gt;export SINK_LD_LIBRARY_PATH=/opt/intel/composerxe/compiler/lib/mic&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Alkalmazás futtatása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;micnativeloadex hello-MIC&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| BUDAPEST2[cn10] phi (0)$ export SINK_LD_LIBRARY_PATH=/opt/intel/composerxe/compiler/lib/mic &amp;lt;br/&amp;gt;BUDAPEST2[cn10] phi (0)$ micnativeloadex hello-MIC &amp;lt;br/&amp;gt;Hello world! I have 244 logical cores.&amp;lt;br/&amp;gt;BUDAPEST2[cn10] phi (0)$&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===c) támogatott módok===&lt;br /&gt;
====Offload====&lt;br /&gt;
Az Offload modell esetén szükséges a forráskód módosítása, mégpedig úgy, hogy azon kódrészletet, amit a koprocesszoron szeretnénk futtatni, ki kell egészíteni egy fordítási direktívával. Az alkalmazás fordításakor elegendő csak a CPU-ra való binárist készíteni mivel a modell végzi a megfelelő forrás részlet mozgatását és futtatását.&amp;lt;br /&amp;gt;&lt;br /&gt;
[[Fájl:offload.png|Offload modell]] &amp;lt;br /&amp;gt;&lt;br /&gt;
Példánkban jól látható, hogy a második kiíratás már a koprocesszoron fut.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
int main(int argc, char * argv[]) {&lt;br /&gt;
  printf(&amp;quot;Hello world from host!\n&amp;quot;);&lt;br /&gt;
#pragma offload target(mic)&lt;br /&gt;
  {&lt;br /&gt;
    printf(&amp;quot;Hello world from coprocessor!\n&amp;quot;); fflush(stdout);&lt;br /&gt;
  }&lt;br /&gt;
  printf(&amp;quot;Bye\n&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ebben a példában az operációs rendszer választja ki, hogy melyik Phi kártyán fusson le a kódrészlet. Amennyiben mi szeretnénk explicite megmondani, hogy melyik kártyán fusson, akkor a fordítási direktívában a mic kulcsszó után kell tenni egy &amp;quot;:&amp;quot;-t és a kártya sorszámát [mi esetünkben 0 vagy 1]. Ekkor így nézne ki a direktíva: ''#pragma offload target(mic:1)''&lt;br /&gt;
&lt;br /&gt;
Amennyiben az offload szekcióban függvény hívás is szerepel, akkor szükségeltetik az függvény futásához szükséges változók adatterületének átmásolása a Phi kártyára, majd a lefutás után visszamásolni a hoszt gépre. Ezen másolást az ''offload_attribute'' ''push'' és ''pop'' paraméterei segítségével határozhatjuk meg. &amp;lt;br /&amp;gt;&lt;br /&gt;
A következő mátrix-vektor szorzás példánál az A mátrix globális változó, míg a b és c vektorok a függvény paraméterei.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//A változók felmásolásra kerülnek a Xeon Phi-re&lt;br /&gt;
#pragma offload_attribute(push, target(mic))&lt;br /&gt;
  double* A;&lt;br /&gt;
  void MatrixVector(double *b, double *c, double m, double n) {&lt;br /&gt;
    ...&lt;br /&gt;
    c[] = A[..]*b[]&lt;br /&gt;
  }&lt;br /&gt;
// felmásolt objektumok visszamásolása&lt;br /&gt;
#pragma offload_attribute(pop)&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
  ...&lt;br /&gt;
#pragma offload target(mic) in(A[0:n*m])&lt;br /&gt;
// így is meg lehetne adni: #pragma offload target(mic:0) in(A : length(n*m))	        &lt;br /&gt;
  {&lt;br /&gt;
    MatrixVector(b, c, m, n);&lt;br /&gt;
  }&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ekkor minden memória területet 2x kell másolni, egyszer fel a kártyára, majd másodszor vissza a hoszt gépre, pedig elegendő lenne csak az eredmény vektort visszamásolni. Ezen optimalizálással egy későbbi fejezetben foglalkozunk. &amp;lt;p /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Futás során kaphatunk részletes riportot is, hogy pontosan milyen adatmozgatások történnek az alkalmazás futása során. Ehhez nincs szükség másra csak az '''OFFLOAD_REPORT''' környezeti változó megfelelő beállítása. A beállított értékek 1, 2 vagy 3 lehetnek, attól függően mennyire részletes riportot szeretnénk kapni. &amp;lt;br /&amp;gt;&lt;br /&gt;
beállítás:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export OFFLOAD_REPORT=3&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
példa kimenet a hello alkalmazásra:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Hello world from host!&lt;br /&gt;
[Offload] [MIC 0] [File]                    hello_offload.cc&lt;br /&gt;
[Offload] [MIC 0] [Line]                    5&lt;br /&gt;
[Offload] [MIC 0] [Tag]                     Tag 0&lt;br /&gt;
Hello world from coprocessor!&lt;br /&gt;
[Offload] [HOST]  [Tag 0] [CPU Time]        0.554653(seconds)&lt;br /&gt;
[Offload] [MIC 0] [Tag 0] [CPU-&amp;gt;MIC Data]   0 (bytes)&lt;br /&gt;
[Offload] [MIC 0] [Tag 0] [MIC Time]        0.000140(seconds)&lt;br /&gt;
[Offload] [MIC 0] [Tag 0] [MIC-&amp;gt;CPU Data]   0 (bytes)&lt;br /&gt;
Bye&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Offload + OpenMP====&lt;br /&gt;
Amennyiben nem csak 1 processzort szeretnénk használni a Phi kártyánkon és az alkalmazásunkban használt számítás megengedi a párhuzamosítást, akkor az OpenMP segítségével párhuzamosíthatjuk a megfelelő számítást. &amp;lt;br /&amp;gt;&lt;br /&gt;
[[Fájl:Offload+openmp.png|Offload modell és OpenMP]] &amp;lt;br /&amp;gt;&lt;br /&gt;
Példánk maradjon ugyanúgy a mátrix-vektor szorzás. Amennyiben a teljes kártya adta kapacitást ki szeretnénk használni, akkor az OMP threadek számát 240-re kell beállítani. Azért nem 244-re, mivel 1 CPU-t dedikáltan csak a kommunikációra használ a rendszer és 1 CPU 4 hardver threaddel rendelkezik.&lt;br /&gt;
Ekkor a példánk így változik:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;omp.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#pragma offload_attribute(push, target(mic))&lt;br /&gt;
void multiply(int n, int m, double* A, double* b, double* c){&lt;br /&gt;
  const int nCPUs = omp_get_max_threads();   //240&lt;br /&gt;
  omp_set_num_threads(nCPUs);&lt;br /&gt;
#pragma omp parallel for schedule(dynamic, 1)&lt;br /&gt;
  for ( int i = 0 ; i &amp;lt; m ; i++)&lt;br /&gt;
     for ( int j = 0 ; j &amp;lt; n ; j++)&lt;br /&gt;
       c[i] += A[i*n+j] * b[j];&lt;br /&gt;
}&lt;br /&gt;
#pragma offload_attribute(pop)&lt;br /&gt;
&lt;br /&gt;
int main(){&lt;br /&gt;
  double * A = (double*) malloc(sizeof(double)*n*m);&lt;br /&gt;
  ...&lt;br /&gt;
#pragma offload target(mic) in(A:length(n*m))&lt;br /&gt;
     multiply(n, m, A, b, c);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Az alkalmazás fordításánál a fordítónak meg kell adni, hogy használhja az openmp library-ket is. Ezt a '''-qopenmp''' kapcsolóval tudjuk elérni.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
icpc -o matrix-CPU -qopenmp matrix.cc&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====MPI + Offload + OpenMP====&lt;br /&gt;
Abban az esetben, ha 1 számítási node nem elég, MPI segítségével össze tudunk kötni több nodet, heterogén rendszert alkotva, és azokon alkalmazható a fenti Offload + OpenMP modell. &amp;lt;br /&amp;gt;&lt;br /&gt;
[[Fájl:Mpi+offload+openmp.png|MPI elosztás valamint Offload modell és OpenMP]] &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Alkalmazások optimalizálása a kártyára==&lt;br /&gt;
Ebben a fejezetben optimalizálási lehetőségeket mutatunk be, melyek segítségül szolgálhatnak az egyes alkalmazások hatékonyabb futása érdekében ezzel jobban kihasználni a rendelkezésre álló erőforrások adta számítási kapacitás.&lt;br /&gt;
&lt;br /&gt;
===Környezeti változók===&lt;br /&gt;
Lehetséges csak a Phi kártyákon használt környezeti változók beállítása, amit az alkalmazásunk használni is tud. &amp;lt;br /&amp;gt;&lt;br /&gt;
Példánkban az OpenMP esetén használható szálak számát állíthatjuk be:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export MIC_ENV_PREFIX=XEONPHI&lt;br /&gt;
export OMP_NUM_THREADS=12&lt;br /&gt;
export XEONPHI_OMP_NUM_THREADS=240&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
A beállított prefix tetszőleges karaktersorozat lehet, a lényeg, hogy ezt a beállított prefixet használjuk a környezeti változók esetén is!&lt;br /&gt;
 &lt;br /&gt;
===Hosszredukció===&lt;br /&gt;
Többnyire a fordítók, ha nem mondjuk meg explicite, a dupla pontosságú változatot választják a lehetséges változó- és függvény típusok közül. Amennyiben a számításunk nem igényli a dupla pontosságot, akkor használjuk a forrásban mindenhol az adott típus vagy függvény egyszeres pontosságú változatát, mellyel jelentős teljesítmény növekedést tudunk elérni. Továbbá az egyes műveletek futási ideje nem egyforma, így a műveletet helyettesíthetjük más művelettel, ami gyorsabb, akkor ismét javíthatunk a hatékonyságon. &amp;lt;br /&amp;gt;&lt;br /&gt;
Íme pár példa, amit alkalmazni lehet:&lt;br /&gt;
* &amp;quot;''/''&amp;quot; művelet helyett &amp;quot;''*''&amp;quot; -t használjunk, mert azt gyorsabban végre tudja hajtani a processzor&lt;br /&gt;
* ''pow'' =&amp;gt; ''1.0 / sqrt()''    , tetszőleges hatványozás, pl.: 3/2-ik hatvány&lt;br /&gt;
* hardver áltatl támogatott műveletek:&lt;br /&gt;
** ''exp'' =&amp;gt; ''exp2''&lt;br /&gt;
** ''log'' =&amp;gt; ''log2''&lt;br /&gt;
* dupla pontosság -&amp;gt; egyszeres pontosság lebegőpontos számok és műveletek esetén &lt;br /&gt;
** ''1.0'' =&amp;gt; ''1.0f''&lt;br /&gt;
** ''pow'' =&amp;gt; ''powf''&lt;br /&gt;
&lt;br /&gt;
===Vektorizáció===&lt;br /&gt;
A vektorizáció azon fordítási időben eldönthető optimalizációs faktor, amely nagy mértékben függ attól a hardver felépítésétől, amire éppen a fordítás történni fog. Legfontosabb befolyásoló tényezők a processzor cache méretei illetve a vektor műveletekért felelős aritmetikai egységének jellemzői mivel ezeket használja fel a fordító az optimalizálási folyamata során. &amp;lt;br /&amp;gt;&lt;br /&gt;
Az automatikus vektorizáció minimum -O2 szinttől érvényes, de mivel általában ez a szint az alapértelmezett, ezért minden fordításnál alkalmazásra kerül. &amp;lt;br /&amp;gt;&lt;br /&gt;
A fordító programnak a következő kapcsolókat lehet megadni, melyek segítenek a minél jobb vektorizációban:&lt;br /&gt;
* ''-qopt-report=&amp;lt;szám&amp;gt;'' ; az optimalizációs jelentés részletességét lehet megadni 0-tól 5-ig, ahol az 5-s szint a legrészletesebbet jelenti&lt;br /&gt;
* ''-qopt-report-phase:vec'' ; a vektorizációról szeretnék jelentést kapni&lt;br /&gt;
* ''-opt-report-file=&amp;lt;fájlnév&amp;gt;'' ; megadható a riportfájl neve&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
icpc autovec.cc -mmic -qopt-report=5 -opt-report-file=vectorization.oMIC.optrpt -qopt-report-phase:vec&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====SIMD====&lt;br /&gt;
Az adatok hatékonyabb feldolgozására az egyik megvalósítás a SIMD (Single Instruction Multiple Data), amikor is a különálló adatokat úgy kezeli, mintha az vektorok volnának és azon végzi el a vektor-műveletet. Ezzel egy a hardver által támogatott műveletet végzünk egy lépésben egy nagyobb adatmennyiségen, ezzel párhuzamosítva a feldolgozási folyamatot. Ezzel a módszerrel nagyon jó adat szintű párhuzamosítást lehet elérni.&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Fájl:Simd.png|SIMD]]&lt;br /&gt;
&lt;br /&gt;
Főbb támogatott műveletek:&lt;br /&gt;
* aritmetikai műveletek, bináris operátorok&lt;br /&gt;
* összehasonlítások&lt;br /&gt;
* konverziók és típus kasztolások&lt;br /&gt;
* bit-műveletek: NOT, AND, OR, XOR, XAND&lt;br /&gt;
&lt;br /&gt;
Használata: '''simd''' fordítási direktívával lehetséges &amp;lt;br /&amp;gt;&lt;br /&gt;
Például:&lt;br /&gt;
* egyszerű vektorizáció&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma simd&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
  A[i] += B[i];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* adat párhuzamosítás OpenMP segítségével&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for simd&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
  DoSomWork(A[i]);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====AoS -&amp;gt; SoA====&lt;br /&gt;
A kiindulás probléma az, hogy a struktúrákat tartalmazó vektort nem lehet jól vektorizálni, mivel a cache-ben tárolt adatok nagy többsége feleslegesen van bent, mivel azon adatokat csak egy későbbi ciklus futás során fogja az alkalmazás feldolgozni. Tehát ezen adatok jelenleg feleslegesen foglalják a cache területet, ahelyett, hogy hasznos adatot tárolnánk ott.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
struct MyStructure {&lt;br /&gt;
  float x, y, z;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void My_Function(cons int nItem, MyStructure* const Items, const float delta) {&lt;br /&gt;
  for (int i = 0; i &amp;lt; nItem; i++) {&lt;br /&gt;
    ...&lt;br /&gt;
    for (int j = 0; j &amp;lt; nItem; j++) {&lt;br /&gt;
      const float dx = Items[j].x - Items[i].x;&lt;br /&gt;
      const float dy = Items[j].y - Items[i].y;&lt;br /&gt;
      const float dz = Items[j].z - Items[i].z;&lt;br /&gt;
      ...  &lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  ... &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A vektorban a struktúrák tárolása helyett minden elemnek egy dedikált vektort allokálunk és így könnyen tudja a fordító vektorizálni azt. Természetesen segítségként a vektor feldolgozását &amp;quot;tömbösítettük&amp;quot; a tileSize méret megadásával illetve a ''unroll'' fordítási direktíva segítségével.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
struct MyStructureSet {&lt;br /&gt;
  float *x, *y, *z;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void My_Function(cons int nItem, MyStructure* const Items, const float delta) {&lt;br /&gt;
  const int tileSize = 16;&lt;br /&gt;
#pragma omp parallel for schedule(guided)&lt;br /&gt;
  for (int ii = 0; ii &amp;lt; nItem; ii+= tileSize) {&lt;br /&gt;
    ...&lt;br /&gt;
#pragma unroll(tileSize)&lt;br /&gt;
    for (int j = 0; j &amp;lt; nItem; j++) {&lt;br /&gt;
#pragma unroll(tileSize)&lt;br /&gt;
      for (int i = ii; i &amp;lt; ii + tileSize; i++) {&lt;br /&gt;
        const float dx = Items.x[j] - Items.x[i];&lt;br /&gt;
        const float dy = Items.y[j] - Items.y[i];&lt;br /&gt;
        const float dz = Items.z[j] - Items.z[i];&lt;br /&gt;
        ...&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  ... &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Hatékony hossz választás====&lt;br /&gt;
Olyan esetben jelent előnyt, ha például az n*n-es mátrix esetén n nem osztható 16-val, mert akkor a vektorizáció nem teljesen hajtható végre. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// n%16 != 0&lt;br /&gt;
float* A = _mm_malloc(sizeof(float)*n*n, 64);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Ilyen esetben mindig marad egy kis vektor részlet, amire elemenként kell elvégezni a műveletet.&amp;lt;br /&amp;gt;&lt;br /&gt;
A megoldást az jelenti, hogy a memória allokálást kiterjesztjük és nagyobb memória területet foglalunk a változónak, így már alkalmazható lesz a vektorizáció, mivel a maradvány területen is el tudjuk végezni a műveletet egy lépésben.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int lda=n;&lt;br /&gt;
if (n % 16 != 0) lda += (16 - n%16); // most lda%16==0&lt;br /&gt;
float* A = _mm_malloc(sizeof(float)*n*lda, 64);&lt;br /&gt;
&lt;br /&gt;
// használat&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
  for (int j = 0; j &amp;lt; n; j++)&lt;br /&gt;
    A[i*lda + j] = ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====&amp;quot;Héj ciklusok&amp;quot; kiküszöbölése====&lt;br /&gt;
Ciklusok esetén lehetséges, hogy a vektorizációt nem teljes mértékben ismeri fel a fordító program. &lt;br /&gt;
Ekkor segíthetünk a fordítónak egy fordítási direktívával ('''#pragma vector aligned'''), ha teljes mértékben biztosak vagyunk, hogy a ciklusunk vektorizálható!&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma vector aligned&lt;br /&gt;
  for (int j = 0; j &amp;lt; n; j++)&lt;br /&gt;
    A[i*lda + j] -= ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Ettől jelentős futás idejű javulást érhetünk el, mivel ezzel a #pragma-val kikapcsoltuk a futás idejű ellenőrzést, de előfordulhatnak olyan esetek, főleg amikor nem jól mértük fel a ciklusunkat és nem sikerült a vektorizálás, hogy ''Segmentation fault'' hibára fogunk futni.&lt;br /&gt;
&lt;br /&gt;
====Regularizáció====&lt;br /&gt;
Van egy ciklusunk, ami egy vektor elemeit módosítja. A processzor az adatokat &amp;quot;csokorba szedve&amp;quot; emeli be a belső kis méretű cache-be. Egyszerre természetesen több vektor elem is bekerül a a cache-be ezzel gyorsítva a feldolgozást. Ezen vektor iterációk valahogyan ráilleszkedik a vektorra.&lt;br /&gt;
&lt;br /&gt;
[[Fájl:regularization.png|Vektor regularizáció]]&lt;br /&gt;
&lt;br /&gt;
Abban az esetben, amikor ez az illesztés nem teljesen tökéletes (lsd. piros elemek), keletkeznem héj ciklusok, melyeket a regularizációval is eliminálni lehet. &amp;lt;br /&amp;gt;&lt;br /&gt;
A feladat eredetileg valahogyan így nézhet ki:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
for (int i = 0; i&amp;lt;n; i++)&lt;br /&gt;
  A[i]=...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ez a következő átalakítással már alkalmas vektorizációra és nem lesz &amp;quot;peel loop&amp;quot; probléma.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
for (int b=0; b&amp;lt;n; b++) {&lt;br /&gt;
  const int jStart = b- b%16; // mindig osztható 16-val&lt;br /&gt;
  ...&lt;br /&gt;
#pragma simd&lt;br /&gt;
  for (int j = jStart; j&amp;lt;n; j++)&lt;br /&gt;
    A[j]=...&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Párhuzamosítás===&lt;br /&gt;
====Párhuzamos szálak ütemezése====&lt;br /&gt;
====Ciklus kifejtés====&lt;br /&gt;
====Automatikus ciklusösszevonás====&lt;/div&gt;</summary>
		<author><name>Szigeti(AT)niif.hu</name></author>	</entry>

	<entry>
		<id>https://wiki.niif.hu/index.php?title=F%C3%A1jl:Simd.png&amp;diff=3746</id>
		<title>Fájl:Simd.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.niif.hu/index.php?title=F%C3%A1jl:Simd.png&amp;diff=3746"/>
				<updated>2016-05-05T07:22:36Z</updated>
		
		<summary type="html">&lt;p&gt;Szigeti(AT)niif.hu: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Szigeti(AT)niif.hu</name></author>	</entry>

	<entry>
		<id>https://wiki.niif.hu/index.php?title=Intel_Xeon_Phi&amp;diff=3745</id>
		<title>Intel Xeon Phi</title>
		<link rel="alternate" type="text/html" href="https://wiki.niif.hu/index.php?title=Intel_Xeon_Phi&amp;diff=3745"/>
				<updated>2016-05-04T11:17:55Z</updated>
		
		<summary type="html">&lt;p&gt;Szigeti(AT)niif.hu: /* Vektorizáció */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Az oldalon található információk segítséget nyújtanak a HPC felhasználóinknak, hogy alkalmazásaikat miként portolhatják valamint optimalizálhatják a hatékonyabb futást szem előtt tartva Intel Xeon Phi koprocesszor kártyákon.&lt;br /&gt;
&lt;br /&gt;
==Hardver bemutatása==&lt;br /&gt;
Intel® Xeon Phi™ Coprocessor 7120P&lt;br /&gt;
&amp;lt;gallery&amp;gt;&lt;br /&gt;
Fájl:Xeon-phi-family-rwd.png.rendition.intel.web.720.405.png|&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-  align=&amp;quot;center&amp;quot;&lt;br /&gt;
! CPU&lt;br /&gt;
! threads/Core&lt;br /&gt;
!max threads&lt;br /&gt;
! CPU Freq&lt;br /&gt;
! Memória&lt;br /&gt;
! L1 cache&lt;br /&gt;
! L2 cache&lt;br /&gt;
! Interfész&lt;br /&gt;
! Ptot&lt;br /&gt;
! Számítási kapacitás&lt;br /&gt;
|-  align=&amp;quot;center&amp;quot;&lt;br /&gt;
| 61&lt;br /&gt;
| 4 Hardver&lt;br /&gt;
| 244&lt;br /&gt;
| 1238 GHz&lt;br /&gt;
| 16GB GDDR5&lt;br /&gt;
| 32KB/core&lt;br /&gt;
| 512KB/core&lt;br /&gt;
| PCIe&lt;br /&gt;
| 300W&lt;br /&gt;
| 1,2 TFlop/s dupla pontosság&lt;br /&gt;
|-  align=&amp;quot;center&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
| OpenMP-nél csak '''240'''&lt;br /&gt;
|&lt;br /&gt;
| ~100 cycles&lt;br /&gt;
~174 GB/s&lt;br /&gt;
| ~3 cycles&lt;br /&gt;
| ~10 cycles&lt;br /&gt;
| ~7 GB/s&lt;br /&gt;
~1 microsecond&lt;br /&gt;
|&lt;br /&gt;
| 2,4 TFlop/s egyszeres pontosság&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Az Intel® 1. generációs Xeon Phi™ koprocesszorában található 61 processzor ugyanazt a 16GB memóriát éri el, de semmilyen módon se fér hozzá közvetlenül a hoszt gép fizikai memóriájához.&amp;lt;br /&amp;gt;&lt;br /&gt;
[[Budapest2_klaszter|Budapest2]] és [[HPC#Szupersz.C3.A1m.C3.ADt.C3.B3g.C3.A9peink_.C3.B6sszehasonl.C3.ADt.C3.A1sa|Debrecen3-Phi]] gépeinkben számítási nodonként 2-2 Phi™ koprocesszor található. &amp;lt;br/&amp;gt;&lt;br /&gt;
A kártyákon speciális célú Linux kernel fut melyre a belépés limitáltan csak a rendszergazdák számára lehetséges SSH-n keresztül a hoszt gépről. Természetesen a kártyára belépve olyan érzésünk lehet, mintha egy sok processzoros gépen dolgoznánk, ami rendelkezik rengeteg memóriával. Ez így is van, de a hatékonyabb kihasználás érdekében az SSH-n keresztüli elérés nem lehetséges, csak a Slurm segítségével valamint a programozási nyelvek által támogatott csatornákon keresztül. Ennek részletezése lentebb megtalálható.&lt;br /&gt;
&lt;br /&gt;
==Használati módok==&lt;br /&gt;
A koprocesszor használatai módjait két csoportba lehet sorolni. A '''natív''' és az '''offload''' használatot.  &amp;lt;br/&amp;gt;&lt;br /&gt;
Ezen használati módokról a lenti fejezetekben több részletet is megtudhat. Az itt megfogalmazott &amp;quot;támogatás&amp;quot; csak a mi infrastruktúránkra érvényes! &amp;lt;br /&amp;gt;&lt;br /&gt;
A bemutatott példák ''c++'' nyelven vannak, de természetesen ''c'' és ''fortran'' esetén is megtalálható az ekvivalens módszer.&lt;br /&gt;
&lt;br /&gt;
===a) nem támogatott mód===&lt;br /&gt;
====közvetlen natív használat====&lt;br /&gt;
A natív mód arra szolgál, hogy a MIC kártyára  lefordított alkalmazásunkat felmásoljuk a kártyára, majd belépünk SSH-n a kártyára és azon futtatjuk a bináris kódunkat. &amp;lt;br /&amp;gt;&lt;br /&gt;
Ez a legegyszerűbb használati mód, ugyanis nincs más dolgunk, csak a '''-mmic''' kapcsolót használni a fordításnál és a fordító egy máris a MIC architektúrára kész bináris készít nekünk. &amp;lt;br/&amp;gt;&lt;br /&gt;
Workflow a következő lenne:&lt;br /&gt;
# Alkalmazás megírása. például [[Média:hello.cc|''hello.cc'']]&lt;br /&gt;
# Alkalmazás fordítása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;icpc -o hello-MIC -mmic hello.cc&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Másolás a MIC kártyára &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;scp hello-MIC mic0:&amp;lt;/pre&amp;gt;&lt;br /&gt;
# belépés a kártyára &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;ssh mic0&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Alkalmazás futtatása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;./hello-MIC&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===b) támogatott, de nem preferált használat===&lt;br /&gt;
====automatikus kódátvitel====&lt;br /&gt;
Ebben az esetben nem kell átmásolnunk a bináris alkalmazásunkat a kártyára sőt még csak be se kell lépnünk, mert ezt a két lépést egy beépített tool végzi el. &amp;lt;br/&amp;gt;&lt;br /&gt;
A tool eléréséhez szükséges parancs neve: '''micnativeloadex'''&lt;br /&gt;
&lt;br /&gt;
Ebben az esetben a workflow a következőképpen módosul:&lt;br /&gt;
# Alkalmazás megírása. például [[Média:hello.cc|''hello.cc'']]&lt;br /&gt;
# Alkalmazás fordítása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;icpc -o hello-MIC -mmic hello.cc&amp;lt;/pre&amp;gt;&lt;br /&gt;
# A tool futásához szükséges library-k megadása &amp;lt;br&amp;gt; &amp;lt;pre&amp;gt;export SINK_LD_LIBRARY_PATH=/opt/intel/composerxe/compiler/lib/mic&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Alkalmazás futtatása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;micnativeloadex hello-MIC&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| BUDAPEST2[cn10] phi (0)$ export SINK_LD_LIBRARY_PATH=/opt/intel/composerxe/compiler/lib/mic &amp;lt;br/&amp;gt;BUDAPEST2[cn10] phi (0)$ micnativeloadex hello-MIC &amp;lt;br/&amp;gt;Hello world! I have 244 logical cores.&amp;lt;br/&amp;gt;BUDAPEST2[cn10] phi (0)$&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===c) támogatott módok===&lt;br /&gt;
====Offload====&lt;br /&gt;
Az Offload modell esetén szükséges a forráskód módosítása, mégpedig úgy, hogy azon kódrészletet, amit a koprocesszoron szeretnénk futtatni, ki kell egészíteni egy fordítási direktívával. Az alkalmazás fordításakor elegendő csak a CPU-ra való binárist készíteni mivel a modell végzi a megfelelő forrás részlet mozgatását és futtatását.&amp;lt;br /&amp;gt;&lt;br /&gt;
[[Fájl:offload.png|Offload modell]] &amp;lt;br /&amp;gt;&lt;br /&gt;
Példánkban jól látható, hogy a második kiíratás már a koprocesszoron fut.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
int main(int argc, char * argv[]) {&lt;br /&gt;
  printf(&amp;quot;Hello world from host!\n&amp;quot;);&lt;br /&gt;
#pragma offload target(mic)&lt;br /&gt;
  {&lt;br /&gt;
    printf(&amp;quot;Hello world from coprocessor!\n&amp;quot;); fflush(stdout);&lt;br /&gt;
  }&lt;br /&gt;
  printf(&amp;quot;Bye\n&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ebben a példában az operációs rendszer választja ki, hogy melyik Phi kártyán fusson le a kódrészlet. Amennyiben mi szeretnénk explicite megmondani, hogy melyik kártyán fusson, akkor a fordítási direktívában a mic kulcsszó után kell tenni egy &amp;quot;:&amp;quot;-t és a kártya sorszámát [mi esetünkben 0 vagy 1]. Ekkor így nézne ki a direktíva: ''#pragma offload target(mic:1)''&lt;br /&gt;
&lt;br /&gt;
Amennyiben az offload szekcióban függvény hívás is szerepel, akkor szükségeltetik az függvény futásához szükséges változók adatterületének átmásolása a Phi kártyára, majd a lefutás után visszamásolni a hoszt gépre. Ezen másolást az ''offload_attribute'' ''push'' és ''pop'' paraméterei segítségével határozhatjuk meg. &amp;lt;br /&amp;gt;&lt;br /&gt;
A következő mátrix-vektor szorzás példánál az A mátrix globális változó, míg a b és c vektorok a függvény paraméterei.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//A változók felmásolásra kerülnek a Xeon Phi-re&lt;br /&gt;
#pragma offload_attribute(push, target(mic))&lt;br /&gt;
  double* A;&lt;br /&gt;
  void MatrixVector(double *b, double *c, double m, double n) {&lt;br /&gt;
    ...&lt;br /&gt;
    c[] = A[..]*b[]&lt;br /&gt;
  }&lt;br /&gt;
// felmásolt objektumok visszamásolása&lt;br /&gt;
#pragma offload_attribute(pop)&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
  ...&lt;br /&gt;
#pragma offload target(mic) in(A[0:n*m])&lt;br /&gt;
// így is meg lehetne adni: #pragma offload target(mic:0) in(A : length(n*m))	        &lt;br /&gt;
  {&lt;br /&gt;
    MatrixVector(b, c, m, n);&lt;br /&gt;
  }&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ekkor minden memória területet 2x kell másolni, egyszer fel a kártyára, majd másodszor vissza a hoszt gépre, pedig elegendő lenne csak az eredmény vektort visszamásolni. Ezen optimalizálással egy későbbi fejezetben foglalkozunk. &amp;lt;p /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Futás során kaphatunk részletes riportot is, hogy pontosan milyen adatmozgatások történnek az alkalmazás futása során. Ehhez nincs szükség másra csak az '''OFFLOAD_REPORT''' környezeti változó megfelelő beállítása. A beállított értékek 1, 2 vagy 3 lehetnek, attól függően mennyire részletes riportot szeretnénk kapni. &amp;lt;br /&amp;gt;&lt;br /&gt;
beállítás:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export OFFLOAD_REPORT=3&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
példa kimenet a hello alkalmazásra:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Hello world from host!&lt;br /&gt;
[Offload] [MIC 0] [File]                    hello_offload.cc&lt;br /&gt;
[Offload] [MIC 0] [Line]                    5&lt;br /&gt;
[Offload] [MIC 0] [Tag]                     Tag 0&lt;br /&gt;
Hello world from coprocessor!&lt;br /&gt;
[Offload] [HOST]  [Tag 0] [CPU Time]        0.554653(seconds)&lt;br /&gt;
[Offload] [MIC 0] [Tag 0] [CPU-&amp;gt;MIC Data]   0 (bytes)&lt;br /&gt;
[Offload] [MIC 0] [Tag 0] [MIC Time]        0.000140(seconds)&lt;br /&gt;
[Offload] [MIC 0] [Tag 0] [MIC-&amp;gt;CPU Data]   0 (bytes)&lt;br /&gt;
Bye&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Offload + OpenMP====&lt;br /&gt;
Amennyiben nem csak 1 processzort szeretnénk használni a Phi kártyánkon és az alkalmazásunkban használt számítás megengedi a párhuzamosítást, akkor az OpenMP segítségével párhuzamosíthatjuk a megfelelő számítást. &amp;lt;br /&amp;gt;&lt;br /&gt;
[[Fájl:Offload+openmp.png|Offload modell és OpenMP]] &amp;lt;br /&amp;gt;&lt;br /&gt;
Példánk maradjon ugyanúgy a mátrix-vektor szorzás. Amennyiben a teljes kártya adta kapacitást ki szeretnénk használni, akkor az OMP threadek számát 240-re kell beállítani. Azért nem 244-re, mivel 1 CPU-t dedikáltan csak a kommunikációra használ a rendszer és 1 CPU 4 hardver threaddel rendelkezik.&lt;br /&gt;
Ekkor a példánk így változik:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;omp.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#pragma offload_attribute(push, target(mic))&lt;br /&gt;
void multiply(int n, int m, double* A, double* b, double* c){&lt;br /&gt;
  const int nCPUs = omp_get_max_threads();   //240&lt;br /&gt;
  omp_set_num_threads(nCPUs);&lt;br /&gt;
#pragma omp parallel for schedule(dynamic, 1)&lt;br /&gt;
  for ( int i = 0 ; i &amp;lt; m ; i++)&lt;br /&gt;
     for ( int j = 0 ; j &amp;lt; n ; j++)&lt;br /&gt;
       c[i] += A[i*n+j] * b[j];&lt;br /&gt;
}&lt;br /&gt;
#pragma offload_attribute(pop)&lt;br /&gt;
&lt;br /&gt;
int main(){&lt;br /&gt;
  double * A = (double*) malloc(sizeof(double)*n*m);&lt;br /&gt;
  ...&lt;br /&gt;
#pragma offload target(mic) in(A:length(n*m))&lt;br /&gt;
     multiply(n, m, A, b, c);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Az alkalmazás fordításánál a fordítónak meg kell adni, hogy használhja az openmp library-ket is. Ezt a '''-qopenmp''' kapcsolóval tudjuk elérni.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
icpc -o matrix-CPU -qopenmp matrix.cc&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====MPI + Offload + OpenMP====&lt;br /&gt;
Abban az esetben, ha 1 számítási node nem elég, MPI segítségével össze tudunk kötni több nodet, heterogén rendszert alkotva, és azokon alkalmazható a fenti Offload + OpenMP modell. &amp;lt;br /&amp;gt;&lt;br /&gt;
[[Fájl:Mpi+offload+openmp.png|MPI elosztás valamint Offload modell és OpenMP]] &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Alkalmazások optimalizálása a kártyára==&lt;br /&gt;
Ebben a fejezetben optimalizálási lehetőségeket mutatunk be, melyek segítségül szolgálhatnak az egyes alkalmazások hatékonyabb futása érdekében ezzel jobban kihasználni a rendelkezésre álló erőforrások adta számítási kapacitás.&lt;br /&gt;
&lt;br /&gt;
===Környezeti változók===&lt;br /&gt;
Lehetséges csak a Phi kártyákon használt környezeti változók beállítása, amit az alkalmazásunk használni is tud. &amp;lt;br /&amp;gt;&lt;br /&gt;
Példánkban az OpenMP esetén használható szálak számát állíthatjuk be:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export MIC_ENV_PREFIX=XEONPHI&lt;br /&gt;
export OMP_NUM_THREADS=12&lt;br /&gt;
export XEONPHI_OMP_NUM_THREADS=240&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
A beállított prefix tetszőleges karaktersorozat lehet, a lényeg, hogy ezt a beállított prefixet használjuk a környezeti változók esetén is!&lt;br /&gt;
 &lt;br /&gt;
===Hosszredukció===&lt;br /&gt;
Többnyire a fordítók, ha nem mondjuk meg explicite, a dupla pontosságú változatot választják a lehetséges változó- és függvény típusok közül. Amennyiben a számításunk nem igényli a dupla pontosságot, akkor használjuk a forrásban mindenhol az adott típus vagy függvény egyszeres pontosságú változatát, mellyel jelentős teljesítmény növekedést tudunk elérni. Továbbá az egyes műveletek futási ideje nem egyforma, így a műveletet helyettesíthetjük más művelettel, ami gyorsabb, akkor ismét javíthatunk a hatékonyságon. &amp;lt;br /&amp;gt;&lt;br /&gt;
Íme pár példa, amit alkalmazni lehet:&lt;br /&gt;
* &amp;quot;''/''&amp;quot; művelet helyett &amp;quot;''*''&amp;quot; -t használjunk, mert azt gyorsabban végre tudja hajtani a processzor&lt;br /&gt;
* ''pow'' =&amp;gt; ''1.0 / sqrt()''    , tetszőleges hatványozás, pl.: 3/2-ik hatvány&lt;br /&gt;
* hardver áltatl támogatott műveletek:&lt;br /&gt;
** ''exp'' =&amp;gt; ''exp2''&lt;br /&gt;
** ''log'' =&amp;gt; ''log2''&lt;br /&gt;
* dupla pontosság -&amp;gt; egyszeres pontosság lebegőpontos számok és műveletek esetén &lt;br /&gt;
** ''1.0'' =&amp;gt; ''1.0f''&lt;br /&gt;
** ''pow'' =&amp;gt; ''powf''&lt;br /&gt;
&lt;br /&gt;
===Vektorizáció===&lt;br /&gt;
A vektorizáció azon fordítási időben eldönthető optimalizációs faktor, amely nagy mértékben függ attól a hardver felépítésétől, amire éppen a fordítás történni fog. Legfontosabb befolyásoló tényezők a processzor cache méretei illetve a vektor műveletekért felelős aritmetikai egységének jellemzői mivel ezeket használja fel a fordító az optimalizálási folyamata során. &amp;lt;br /&amp;gt;&lt;br /&gt;
Az automatikus vektorizáció minimum -O2 szinttől érvényes, de mivel általában ez a szint az alapértelmezett, ezért minden fordításnál alkalmazásra kerül. &amp;lt;br /&amp;gt;&lt;br /&gt;
A fordító programnak a következő kapcsolókat lehet megadni, melyek segítenek a minél jobb vektorizációban:&lt;br /&gt;
* ''-qopt-report=&amp;lt;szám&amp;gt;'' ; az optimalizációs jelentés részletességét lehet megadni 0-tól 5-ig, ahol az 5-s szint a legrészletesebbet jelenti&lt;br /&gt;
* ''-qopt-report-phase:vec'' ; a vektorizációról szeretnék jelentést kapni&lt;br /&gt;
* ''-opt-report-file=&amp;lt;fájlnév&amp;gt;'' ; megadható a riportfájl neve&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
icpc autovec.cc -mmic -qopt-report=5 -opt-report-file=vectorization.oMIC.optrpt -qopt-report-phase:vec&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====SIMD====&lt;br /&gt;
Az adatok hatékonyabb feldolgozására az egyik megvalósítás a SIMD (Single Instruction Multiple Data), amikor is a különálló adatokat úgy kezeli, mintha az vektorok volnának és azon végzi el a vektor-műveletet. Ezzel egy a hardver által támogatott műveletet végzünk egy lépésben egy nagyobb adatmennyiségen, ezzel párhuzamosítva a feldolgozási folyamatot. Ezzel a módszerrel nagyon jó adat szintű párhuzamosítást lehet elérni.&amp;lt;br /&amp;gt;&lt;br /&gt;
Főbb támogatott műveletek:&lt;br /&gt;
* aritmetikai műveletek, bináris operátorok&lt;br /&gt;
* összehasonlítások&lt;br /&gt;
* konverziók és típus kasztolások&lt;br /&gt;
* bit-műveletek: NOT, AND, OR, XOR, XAND&lt;br /&gt;
&lt;br /&gt;
Használata: '''simd''' fordítási direktívával lehetséges &amp;lt;br /&amp;gt;&lt;br /&gt;
Például:&lt;br /&gt;
* egyszerű vektorizáció&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma simd&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
  A[i] += B[i];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* adat párhuzamosítás OpenMP segítségével&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for simd&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
  DoSomWork(A[i]);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====AoS -&amp;gt; SoA====&lt;br /&gt;
A kiindulás probléma az, hogy a struktúrákat tartalmazó vektort nem lehet jól vektorizálni, mivel a cache-ben tárolt adatok nagy többsége feleslegesen van bent, mivel azon adatokat csak egy későbbi ciklus futás során fogja az alkalmazás feldolgozni. Tehát ezen adatok jelenleg feleslegesen foglalják a cache területet, ahelyett, hogy hasznos adatot tárolnánk ott.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
struct MyStructure {&lt;br /&gt;
  float x, y, z;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void My_Function(cons int nItem, MyStructure* const Items, const float delta) {&lt;br /&gt;
  for (int i = 0; i &amp;lt; nItem; i++) {&lt;br /&gt;
    ...&lt;br /&gt;
    for (int j = 0; j &amp;lt; nItem; j++) {&lt;br /&gt;
      const float dx = Items[j].x - Items[i].x;&lt;br /&gt;
      const float dy = Items[j].y - Items[i].y;&lt;br /&gt;
      const float dz = Items[j].z - Items[i].z;&lt;br /&gt;
      ...  &lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  ... &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A vektorban a struktúrák tárolása helyett minden elemnek egy dedikált vektort allokálunk és így könnyen tudja a fordító vektorizálni azt. Természetesen segítségként a vektor feldolgozását &amp;quot;tömbösítettük&amp;quot; a tileSize méret megadásával illetve a ''unroll'' fordítási direktíva segítségével.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
struct MyStructureSet {&lt;br /&gt;
  float *x, *y, *z;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void My_Function(cons int nItem, MyStructure* const Items, const float delta) {&lt;br /&gt;
  const int tileSize = 16;&lt;br /&gt;
#pragma omp parallel for schedule(guided)&lt;br /&gt;
  for (int ii = 0; ii &amp;lt; nItem; ii+= tileSize) {&lt;br /&gt;
    ...&lt;br /&gt;
#pragma unroll(tileSize)&lt;br /&gt;
    for (int j = 0; j &amp;lt; nItem; j++) {&lt;br /&gt;
#pragma unroll(tileSize)&lt;br /&gt;
      for (int i = ii; i &amp;lt; ii + tileSize; i++) {&lt;br /&gt;
        const float dx = Items.x[j] - Items.x[i];&lt;br /&gt;
        const float dy = Items.y[j] - Items.y[i];&lt;br /&gt;
        const float dz = Items.z[j] - Items.z[i];&lt;br /&gt;
        ...&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  ... &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Hatékony hossz választás====&lt;br /&gt;
Olyan esetben jelent előnyt, ha például az n*n-es mátrix esetén n nem osztható 16-val, mert akkor a vektorizáció nem teljesen hajtható végre. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// n%16 != 0&lt;br /&gt;
float* A = _mm_malloc(sizeof(float)*n*n, 64);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Ilyen esetben mindig marad egy kis vektor részlet, amire elemenként kell elvégezni a műveletet.&amp;lt;br /&amp;gt;&lt;br /&gt;
A megoldást az jelenti, hogy a memória allokálást kiterjesztjük és nagyobb memória területet foglalunk a változónak, így már alkalmazható lesz a vektorizáció, mivel a maradvány területen is el tudjuk végezni a műveletet egy lépésben.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int lda=n;&lt;br /&gt;
if (n % 16 != 0) lda += (16 - n%16); // most lda%16==0&lt;br /&gt;
float* A = _mm_malloc(sizeof(float)*n*lda, 64);&lt;br /&gt;
&lt;br /&gt;
// használat&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
  for (int j = 0; j &amp;lt; n; j++)&lt;br /&gt;
    A[i*lda + j] = ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====&amp;quot;Héj ciklusok&amp;quot; kiküszöbölése====&lt;br /&gt;
Ciklusok esetén lehetséges, hogy a vektorizációt nem teljes mértékben ismeri fel a fordító program. &lt;br /&gt;
Ekkor segíthetünk a fordítónak egy fordítási direktívával ('''#pragma vector aligned'''), ha teljes mértékben biztosak vagyunk, hogy a ciklusunk vektorizálható!&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma vector aligned&lt;br /&gt;
  for (int j = 0; j &amp;lt; n; j++)&lt;br /&gt;
    A[i*lda + j] -= ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Ettől jelentős futás idejű javulást érhetünk el, mivel ezzel a #pragma-val kikapcsoltuk a futás idejű ellenőrzést, de előfordulhatnak olyan esetek, főleg amikor nem jól mértük fel a ciklusunkat és nem sikerült a vektorizálás, hogy ''Segmentation fault'' hibára fogunk futni.&lt;br /&gt;
&lt;br /&gt;
====Regularizáció====&lt;br /&gt;
Van egy ciklusunk, ami egy vektor elemeit módosítja. A processzor az adatokat &amp;quot;csokorba szedve&amp;quot; emeli be a belső kis méretű cache-be. Egyszerre természetesen több vektor elem is bekerül a a cache-be ezzel gyorsítva a feldolgozást. Ezen vektor iterációk valahogyan ráilleszkedik a vektorra.&lt;br /&gt;
&lt;br /&gt;
[[Fájl:regularization.png|Vektor regularizáció]]&lt;br /&gt;
&lt;br /&gt;
Abban az esetben, amikor ez az illesztés nem teljesen tökéletes (lsd. piros elemek), keletkeznem héj ciklusok, melyeket a regularizációval is eliminálni lehet. &amp;lt;br /&amp;gt;&lt;br /&gt;
A feladat eredetileg valahogyan így nézhet ki:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
for (int i = 0; i&amp;lt;n; i++)&lt;br /&gt;
  A[i]=...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ez a következő átalakítással már alkalmas vektorizációra és nem lesz &amp;quot;peel loop&amp;quot; probléma.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
for (int b=0; b&amp;lt;n; b++) {&lt;br /&gt;
  const int jStart = b- b%16; // mindig osztható 16-val&lt;br /&gt;
  ...&lt;br /&gt;
#pragma simd&lt;br /&gt;
  for (int j = jStart; j&amp;lt;n; j++)&lt;br /&gt;
    A[j]=...&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Párhuzamosítás===&lt;br /&gt;
====Párhuzamos szálak ütemezése====&lt;br /&gt;
====Ciklus kifejtés====&lt;br /&gt;
====Automatikus ciklusösszevonás====&lt;/div&gt;</summary>
		<author><name>Szigeti(AT)niif.hu</name></author>	</entry>

	<entry>
		<id>https://wiki.niif.hu/index.php?title=Intel_Xeon_Phi&amp;diff=3744</id>
		<title>Intel Xeon Phi</title>
		<link rel="alternate" type="text/html" href="https://wiki.niif.hu/index.php?title=Intel_Xeon_Phi&amp;diff=3744"/>
				<updated>2016-05-04T09:05:22Z</updated>
		
		<summary type="html">&lt;p&gt;Szigeti(AT)niif.hu: /* SIMD */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Az oldalon található információk segítséget nyújtanak a HPC felhasználóinknak, hogy alkalmazásaikat miként portolhatják valamint optimalizálhatják a hatékonyabb futást szem előtt tartva Intel Xeon Phi koprocesszor kártyákon.&lt;br /&gt;
&lt;br /&gt;
==Hardver bemutatása==&lt;br /&gt;
Intel® Xeon Phi™ Coprocessor 7120P&lt;br /&gt;
&amp;lt;gallery&amp;gt;&lt;br /&gt;
Fájl:Xeon-phi-family-rwd.png.rendition.intel.web.720.405.png|&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-  align=&amp;quot;center&amp;quot;&lt;br /&gt;
! CPU&lt;br /&gt;
! threads/Core&lt;br /&gt;
!max threads&lt;br /&gt;
! CPU Freq&lt;br /&gt;
! Memória&lt;br /&gt;
! L1 cache&lt;br /&gt;
! L2 cache&lt;br /&gt;
! Interfész&lt;br /&gt;
! Ptot&lt;br /&gt;
! Számítási kapacitás&lt;br /&gt;
|-  align=&amp;quot;center&amp;quot;&lt;br /&gt;
| 61&lt;br /&gt;
| 4 Hardver&lt;br /&gt;
| 244&lt;br /&gt;
| 1238 GHz&lt;br /&gt;
| 16GB GDDR5&lt;br /&gt;
| 32KB/core&lt;br /&gt;
| 512KB/core&lt;br /&gt;
| PCIe&lt;br /&gt;
| 300W&lt;br /&gt;
| 1,2 TFlop/s dupla pontosság&lt;br /&gt;
|-  align=&amp;quot;center&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
| OpenMP-nél csak '''240'''&lt;br /&gt;
|&lt;br /&gt;
| ~100 cycles&lt;br /&gt;
~174 GB/s&lt;br /&gt;
| ~3 cycles&lt;br /&gt;
| ~10 cycles&lt;br /&gt;
| ~7 GB/s&lt;br /&gt;
~1 microsecond&lt;br /&gt;
|&lt;br /&gt;
| 2,4 TFlop/s egyszeres pontosság&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Az Intel® 1. generációs Xeon Phi™ koprocesszorában található 61 processzor ugyanazt a 16GB memóriát éri el, de semmilyen módon se fér hozzá közvetlenül a hoszt gép fizikai memóriájához.&amp;lt;br /&amp;gt;&lt;br /&gt;
[[Budapest2_klaszter|Budapest2]] és [[HPC#Szupersz.C3.A1m.C3.ADt.C3.B3g.C3.A9peink_.C3.B6sszehasonl.C3.ADt.C3.A1sa|Debrecen3-Phi]] gépeinkben számítási nodonként 2-2 Phi™ koprocesszor található. &amp;lt;br/&amp;gt;&lt;br /&gt;
A kártyákon speciális célú Linux kernel fut melyre a belépés limitáltan csak a rendszergazdák számára lehetséges SSH-n keresztül a hoszt gépről. Természetesen a kártyára belépve olyan érzésünk lehet, mintha egy sok processzoros gépen dolgoznánk, ami rendelkezik rengeteg memóriával. Ez így is van, de a hatékonyabb kihasználás érdekében az SSH-n keresztüli elérés nem lehetséges, csak a Slurm segítségével valamint a programozási nyelvek által támogatott csatornákon keresztül. Ennek részletezése lentebb megtalálható.&lt;br /&gt;
&lt;br /&gt;
==Használati módok==&lt;br /&gt;
A koprocesszor használatai módjait két csoportba lehet sorolni. A '''natív''' és az '''offload''' használatot.  &amp;lt;br/&amp;gt;&lt;br /&gt;
Ezen használati módokról a lenti fejezetekben több részletet is megtudhat. Az itt megfogalmazott &amp;quot;támogatás&amp;quot; csak a mi infrastruktúránkra érvényes! &amp;lt;br /&amp;gt;&lt;br /&gt;
A bemutatott példák ''c++'' nyelven vannak, de természetesen ''c'' és ''fortran'' esetén is megtalálható az ekvivalens módszer.&lt;br /&gt;
&lt;br /&gt;
===a) nem támogatott mód===&lt;br /&gt;
====közvetlen natív használat====&lt;br /&gt;
A natív mód arra szolgál, hogy a MIC kártyára  lefordított alkalmazásunkat felmásoljuk a kártyára, majd belépünk SSH-n a kártyára és azon futtatjuk a bináris kódunkat. &amp;lt;br /&amp;gt;&lt;br /&gt;
Ez a legegyszerűbb használati mód, ugyanis nincs más dolgunk, csak a '''-mmic''' kapcsolót használni a fordításnál és a fordító egy máris a MIC architektúrára kész bináris készít nekünk. &amp;lt;br/&amp;gt;&lt;br /&gt;
Workflow a következő lenne:&lt;br /&gt;
# Alkalmazás megírása. például [[Média:hello.cc|''hello.cc'']]&lt;br /&gt;
# Alkalmazás fordítása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;icpc -o hello-MIC -mmic hello.cc&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Másolás a MIC kártyára &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;scp hello-MIC mic0:&amp;lt;/pre&amp;gt;&lt;br /&gt;
# belépés a kártyára &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;ssh mic0&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Alkalmazás futtatása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;./hello-MIC&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===b) támogatott, de nem preferált használat===&lt;br /&gt;
====automatikus kódátvitel====&lt;br /&gt;
Ebben az esetben nem kell átmásolnunk a bináris alkalmazásunkat a kártyára sőt még csak be se kell lépnünk, mert ezt a két lépést egy beépített tool végzi el. &amp;lt;br/&amp;gt;&lt;br /&gt;
A tool eléréséhez szükséges parancs neve: '''micnativeloadex'''&lt;br /&gt;
&lt;br /&gt;
Ebben az esetben a workflow a következőképpen módosul:&lt;br /&gt;
# Alkalmazás megírása. például [[Média:hello.cc|''hello.cc'']]&lt;br /&gt;
# Alkalmazás fordítása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;icpc -o hello-MIC -mmic hello.cc&amp;lt;/pre&amp;gt;&lt;br /&gt;
# A tool futásához szükséges library-k megadása &amp;lt;br&amp;gt; &amp;lt;pre&amp;gt;export SINK_LD_LIBRARY_PATH=/opt/intel/composerxe/compiler/lib/mic&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Alkalmazás futtatása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;micnativeloadex hello-MIC&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| BUDAPEST2[cn10] phi (0)$ export SINK_LD_LIBRARY_PATH=/opt/intel/composerxe/compiler/lib/mic &amp;lt;br/&amp;gt;BUDAPEST2[cn10] phi (0)$ micnativeloadex hello-MIC &amp;lt;br/&amp;gt;Hello world! I have 244 logical cores.&amp;lt;br/&amp;gt;BUDAPEST2[cn10] phi (0)$&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===c) támogatott módok===&lt;br /&gt;
====Offload====&lt;br /&gt;
Az Offload modell esetén szükséges a forráskód módosítása, mégpedig úgy, hogy azon kódrészletet, amit a koprocesszoron szeretnénk futtatni, ki kell egészíteni egy fordítási direktívával. Az alkalmazás fordításakor elegendő csak a CPU-ra való binárist készíteni mivel a modell végzi a megfelelő forrás részlet mozgatását és futtatását.&amp;lt;br /&amp;gt;&lt;br /&gt;
[[Fájl:offload.png|Offload modell]] &amp;lt;br /&amp;gt;&lt;br /&gt;
Példánkban jól látható, hogy a második kiíratás már a koprocesszoron fut.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
int main(int argc, char * argv[]) {&lt;br /&gt;
  printf(&amp;quot;Hello world from host!\n&amp;quot;);&lt;br /&gt;
#pragma offload target(mic)&lt;br /&gt;
  {&lt;br /&gt;
    printf(&amp;quot;Hello world from coprocessor!\n&amp;quot;); fflush(stdout);&lt;br /&gt;
  }&lt;br /&gt;
  printf(&amp;quot;Bye\n&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ebben a példában az operációs rendszer választja ki, hogy melyik Phi kártyán fusson le a kódrészlet. Amennyiben mi szeretnénk explicite megmondani, hogy melyik kártyán fusson, akkor a fordítási direktívában a mic kulcsszó után kell tenni egy &amp;quot;:&amp;quot;-t és a kártya sorszámát [mi esetünkben 0 vagy 1]. Ekkor így nézne ki a direktíva: ''#pragma offload target(mic:1)''&lt;br /&gt;
&lt;br /&gt;
Amennyiben az offload szekcióban függvény hívás is szerepel, akkor szükségeltetik az függvény futásához szükséges változók adatterületének átmásolása a Phi kártyára, majd a lefutás után visszamásolni a hoszt gépre. Ezen másolást az ''offload_attribute'' ''push'' és ''pop'' paraméterei segítségével határozhatjuk meg. &amp;lt;br /&amp;gt;&lt;br /&gt;
A következő mátrix-vektor szorzás példánál az A mátrix globális változó, míg a b és c vektorok a függvény paraméterei.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//A változók felmásolásra kerülnek a Xeon Phi-re&lt;br /&gt;
#pragma offload_attribute(push, target(mic))&lt;br /&gt;
  double* A;&lt;br /&gt;
  void MatrixVector(double *b, double *c, double m, double n) {&lt;br /&gt;
    ...&lt;br /&gt;
    c[] = A[..]*b[]&lt;br /&gt;
  }&lt;br /&gt;
// felmásolt objektumok visszamásolása&lt;br /&gt;
#pragma offload_attribute(pop)&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
  ...&lt;br /&gt;
#pragma offload target(mic) in(A[0:n*m])&lt;br /&gt;
// így is meg lehetne adni: #pragma offload target(mic:0) in(A : length(n*m))	        &lt;br /&gt;
  {&lt;br /&gt;
    MatrixVector(b, c, m, n);&lt;br /&gt;
  }&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ekkor minden memória területet 2x kell másolni, egyszer fel a kártyára, majd másodszor vissza a hoszt gépre, pedig elegendő lenne csak az eredmény vektort visszamásolni. Ezen optimalizálással egy későbbi fejezetben foglalkozunk. &amp;lt;p /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Futás során kaphatunk részletes riportot is, hogy pontosan milyen adatmozgatások történnek az alkalmazás futása során. Ehhez nincs szükség másra csak az '''OFFLOAD_REPORT''' környezeti változó megfelelő beállítása. A beállított értékek 1, 2 vagy 3 lehetnek, attól függően mennyire részletes riportot szeretnénk kapni. &amp;lt;br /&amp;gt;&lt;br /&gt;
beállítás:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export OFFLOAD_REPORT=3&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
példa kimenet a hello alkalmazásra:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Hello world from host!&lt;br /&gt;
[Offload] [MIC 0] [File]                    hello_offload.cc&lt;br /&gt;
[Offload] [MIC 0] [Line]                    5&lt;br /&gt;
[Offload] [MIC 0] [Tag]                     Tag 0&lt;br /&gt;
Hello world from coprocessor!&lt;br /&gt;
[Offload] [HOST]  [Tag 0] [CPU Time]        0.554653(seconds)&lt;br /&gt;
[Offload] [MIC 0] [Tag 0] [CPU-&amp;gt;MIC Data]   0 (bytes)&lt;br /&gt;
[Offload] [MIC 0] [Tag 0] [MIC Time]        0.000140(seconds)&lt;br /&gt;
[Offload] [MIC 0] [Tag 0] [MIC-&amp;gt;CPU Data]   0 (bytes)&lt;br /&gt;
Bye&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Offload + OpenMP====&lt;br /&gt;
Amennyiben nem csak 1 processzort szeretnénk használni a Phi kártyánkon és az alkalmazásunkban használt számítás megengedi a párhuzamosítást, akkor az OpenMP segítségével párhuzamosíthatjuk a megfelelő számítást. &amp;lt;br /&amp;gt;&lt;br /&gt;
[[Fájl:Offload+openmp.png|Offload modell és OpenMP]] &amp;lt;br /&amp;gt;&lt;br /&gt;
Példánk maradjon ugyanúgy a mátrix-vektor szorzás. Amennyiben a teljes kártya adta kapacitást ki szeretnénk használni, akkor az OMP threadek számát 240-re kell beállítani. Azért nem 244-re, mivel 1 CPU-t dedikáltan csak a kommunikációra használ a rendszer és 1 CPU 4 hardver threaddel rendelkezik.&lt;br /&gt;
Ekkor a példánk így változik:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;omp.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#pragma offload_attribute(push, target(mic))&lt;br /&gt;
void multiply(int n, int m, double* A, double* b, double* c){&lt;br /&gt;
  const int nCPUs = omp_get_max_threads();   //240&lt;br /&gt;
  omp_set_num_threads(nCPUs);&lt;br /&gt;
#pragma omp parallel for schedule(dynamic, 1)&lt;br /&gt;
  for ( int i = 0 ; i &amp;lt; m ; i++)&lt;br /&gt;
     for ( int j = 0 ; j &amp;lt; n ; j++)&lt;br /&gt;
       c[i] += A[i*n+j] * b[j];&lt;br /&gt;
}&lt;br /&gt;
#pragma offload_attribute(pop)&lt;br /&gt;
&lt;br /&gt;
int main(){&lt;br /&gt;
  double * A = (double*) malloc(sizeof(double)*n*m);&lt;br /&gt;
  ...&lt;br /&gt;
#pragma offload target(mic) in(A:length(n*m))&lt;br /&gt;
     multiply(n, m, A, b, c);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Az alkalmazás fordításánál a fordítónak meg kell adni, hogy használhja az openmp library-ket is. Ezt a '''-qopenmp''' kapcsolóval tudjuk elérni.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
icpc -o matrix-CPU -qopenmp matrix.cc&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====MPI + Offload + OpenMP====&lt;br /&gt;
Abban az esetben, ha 1 számítási node nem elég, MPI segítségével össze tudunk kötni több nodet, heterogén rendszert alkotva, és azokon alkalmazható a fenti Offload + OpenMP modell. &amp;lt;br /&amp;gt;&lt;br /&gt;
[[Fájl:Mpi+offload+openmp.png|MPI elosztás valamint Offload modell és OpenMP]] &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Alkalmazások optimalizálása a kártyára==&lt;br /&gt;
Ebben a fejezetben optimalizálási lehetőségeket mutatunk be, melyek segítségül szolgálhatnak az egyes alkalmazások hatékonyabb futása érdekében ezzel jobban kihasználni a rendelkezésre álló erőforrások adta számítási kapacitás.&lt;br /&gt;
&lt;br /&gt;
===Környezeti változók===&lt;br /&gt;
Lehetséges csak a Phi kártyákon használt környezeti változók beállítása, amit az alkalmazásunk használni is tud. &amp;lt;br /&amp;gt;&lt;br /&gt;
Példánkban az OpenMP esetén használható szálak számát állíthatjuk be:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export MIC_ENV_PREFIX=XEONPHI&lt;br /&gt;
export OMP_NUM_THREADS=12&lt;br /&gt;
export XEONPHI_OMP_NUM_THREADS=240&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
A beállított prefix tetszőleges karaktersorozat lehet, a lényeg, hogy ezt a beállított prefixet használjuk a környezeti változók esetén is!&lt;br /&gt;
 &lt;br /&gt;
===Hosszredukció===&lt;br /&gt;
Többnyire a fordítók, ha nem mondjuk meg explicite, a dupla pontosságú változatot választják a lehetséges változó- és függvény típusok közül. Amennyiben a számításunk nem igényli a dupla pontosságot, akkor használjuk a forrásban mindenhol az adott típus vagy függvény egyszeres pontosságú változatát, mellyel jelentős teljesítmény növekedést tudunk elérni. Továbbá az egyes műveletek futási ideje nem egyforma, így a műveletet helyettesíthetjük más művelettel, ami gyorsabb, akkor ismét javíthatunk a hatékonyságon. &amp;lt;br /&amp;gt;&lt;br /&gt;
Íme pár példa, amit alkalmazni lehet:&lt;br /&gt;
* &amp;quot;''/''&amp;quot; művelet helyett &amp;quot;''*''&amp;quot; -t használjunk, mert azt gyorsabban végre tudja hajtani a processzor&lt;br /&gt;
* ''pow'' =&amp;gt; ''1.0 / sqrt()''    , tetszőleges hatványozás, pl.: 3/2-ik hatvány&lt;br /&gt;
* hardver áltatl támogatott műveletek:&lt;br /&gt;
** ''exp'' =&amp;gt; ''exp2''&lt;br /&gt;
** ''log'' =&amp;gt; ''log2''&lt;br /&gt;
* dupla pontosság -&amp;gt; egyszeres pontosság lebegőpontos számok és műveletek esetén &lt;br /&gt;
** ''1.0'' =&amp;gt; ''1.0f''&lt;br /&gt;
** ''pow'' =&amp;gt; ''powf''&lt;br /&gt;
&lt;br /&gt;
===Vektorizáció===&lt;br /&gt;
&lt;br /&gt;
====SIMD====&lt;br /&gt;
Az adatok hatékonyabb feldolgozására az egyik megvalósítás a SIMD (Single Instruction Multiple Data), amikor is a különálló adatokat úgy kezeli, mintha az vektorok volnának és azon végzi el a vektor-műveletet. Ezzel egy a hardver által támogatott műveletet végzünk egy lépésben egy nagyobb adatmennyiségen, ezzel párhuzamosítva a feldolgozási folyamatot. Ezzel a módszerrel nagyon jó adat szintű párhuzamosítást lehet elérni.&amp;lt;br /&amp;gt;&lt;br /&gt;
Főbb támogatott műveletek:&lt;br /&gt;
* aritmetikai műveletek, bináris operátorok&lt;br /&gt;
* összehasonlítások&lt;br /&gt;
* konverziók és típus kasztolások&lt;br /&gt;
* bit-műveletek: NOT, AND, OR, XOR, XAND&lt;br /&gt;
&lt;br /&gt;
Használata: '''simd''' fordítási direktívával lehetséges &amp;lt;br /&amp;gt;&lt;br /&gt;
Például:&lt;br /&gt;
* egyszerű vektorizáció&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma simd&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
  A[i] += B[i];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* adat párhuzamosítás OpenMP segítségével&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma omp parallel for simd&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
  DoSomWork(A[i]);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====AoS -&amp;gt; SoA====&lt;br /&gt;
A kiindulás probléma az, hogy a struktúrákat tartalmazó vektort nem lehet jól vektorizálni, mivel a cache-ben tárolt adatok nagy többsége feleslegesen van bent, mivel azon adatokat csak egy későbbi ciklus futás során fogja az alkalmazás feldolgozni. Tehát ezen adatok jelenleg feleslegesen foglalják a cache területet, ahelyett, hogy hasznos adatot tárolnánk ott.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
struct MyStructure {&lt;br /&gt;
  float x, y, z;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void My_Function(cons int nItem, MyStructure* const Items, const float delta) {&lt;br /&gt;
  for (int i = 0; i &amp;lt; nItem; i++) {&lt;br /&gt;
    ...&lt;br /&gt;
    for (int j = 0; j &amp;lt; nItem; j++) {&lt;br /&gt;
      const float dx = Items[j].x - Items[i].x;&lt;br /&gt;
      const float dy = Items[j].y - Items[i].y;&lt;br /&gt;
      const float dz = Items[j].z - Items[i].z;&lt;br /&gt;
      ...  &lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  ... &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A vektorban a struktúrák tárolása helyett minden elemnek egy dedikált vektort allokálunk és így könnyen tudja a fordító vektorizálni azt. Természetesen segítségként a vektor feldolgozását &amp;quot;tömbösítettük&amp;quot; a tileSize méret megadásával illetve a ''unroll'' fordítási direktíva segítségével.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
struct MyStructureSet {&lt;br /&gt;
  float *x, *y, *z;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void My_Function(cons int nItem, MyStructure* const Items, const float delta) {&lt;br /&gt;
  const int tileSize = 16;&lt;br /&gt;
#pragma omp parallel for schedule(guided)&lt;br /&gt;
  for (int ii = 0; ii &amp;lt; nItem; ii+= tileSize) {&lt;br /&gt;
    ...&lt;br /&gt;
#pragma unroll(tileSize)&lt;br /&gt;
    for (int j = 0; j &amp;lt; nItem; j++) {&lt;br /&gt;
#pragma unroll(tileSize)&lt;br /&gt;
      for (int i = ii; i &amp;lt; ii + tileSize; i++) {&lt;br /&gt;
        const float dx = Items.x[j] - Items.x[i];&lt;br /&gt;
        const float dy = Items.y[j] - Items.y[i];&lt;br /&gt;
        const float dz = Items.z[j] - Items.z[i];&lt;br /&gt;
        ...&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  ... &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Hatékony hossz választás====&lt;br /&gt;
Olyan esetben jelent előnyt, ha például az n*n-es mátrix esetén n nem osztható 16-val, mert akkor a vektorizáció nem teljesen hajtható végre. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// n%16 != 0&lt;br /&gt;
float* A = _mm_malloc(sizeof(float)*n*n, 64);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Ilyen esetben mindig marad egy kis vektor részlet, amire elemenként kell elvégezni a műveletet.&amp;lt;br /&amp;gt;&lt;br /&gt;
A megoldást az jelenti, hogy a memória allokálást kiterjesztjük és nagyobb memória területet foglalunk a változónak, így már alkalmazható lesz a vektorizáció, mivel a maradvány területen is el tudjuk végezni a műveletet egy lépésben.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int lda=n;&lt;br /&gt;
if (n % 16 != 0) lda += (16 - n%16); // most lda%16==0&lt;br /&gt;
float* A = _mm_malloc(sizeof(float)*n*lda, 64);&lt;br /&gt;
&lt;br /&gt;
// használat&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
  for (int j = 0; j &amp;lt; n; j++)&lt;br /&gt;
    A[i*lda + j] = ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====&amp;quot;Héj ciklusok&amp;quot; kiküszöbölése====&lt;br /&gt;
Ciklusok esetén lehetséges, hogy a vektorizációt nem teljes mértékben ismeri fel a fordító program. &lt;br /&gt;
Ekkor segíthetünk a fordítónak egy fordítási direktívával ('''#pragma vector aligned'''), ha teljes mértékben biztosak vagyunk, hogy a ciklusunk vektorizálható!&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma vector aligned&lt;br /&gt;
  for (int j = 0; j &amp;lt; n; j++)&lt;br /&gt;
    A[i*lda + j] -= ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Ettől jelentős futás idejű javulást érhetünk el, mivel ezzel a #pragma-val kikapcsoltuk a futás idejű ellenőrzést, de előfordulhatnak olyan esetek, főleg amikor nem jól mértük fel a ciklusunkat és nem sikerült a vektorizálás, hogy ''Segmentation fault'' hibára fogunk futni.&lt;br /&gt;
&lt;br /&gt;
====Regularizáció====&lt;br /&gt;
Van egy ciklusunk, ami egy vektor elemeit módosítja. A processzor az adatokat &amp;quot;csokorba szedve&amp;quot; emeli be a belső kis méretű cache-be. Egyszerre természetesen több vektor elem is bekerül a a cache-be ezzel gyorsítva a feldolgozást. Ezen vektor iterációk valahogyan ráilleszkedik a vektorra.&lt;br /&gt;
&lt;br /&gt;
[[Fájl:regularization.png|Vektor regularizáció]]&lt;br /&gt;
&lt;br /&gt;
Abban az esetben, amikor ez az illesztés nem teljesen tökéletes (lsd. piros elemek), keletkeznem héj ciklusok, melyeket a regularizációval is eliminálni lehet. &amp;lt;br /&amp;gt;&lt;br /&gt;
A feladat eredetileg valahogyan így nézhet ki:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
for (int i = 0; i&amp;lt;n; i++)&lt;br /&gt;
  A[i]=...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ez a következő átalakítással már alkalmas vektorizációra és nem lesz &amp;quot;peel loop&amp;quot; probléma.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
for (int b=0; b&amp;lt;n; b++) {&lt;br /&gt;
  const int jStart = b- b%16; // mindig osztható 16-val&lt;br /&gt;
  ...&lt;br /&gt;
#pragma simd&lt;br /&gt;
  for (int j = jStart; j&amp;lt;n; j++)&lt;br /&gt;
    A[j]=...&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Párhuzamosítás===&lt;br /&gt;
====Párhuzamos szálak ütemezése====&lt;br /&gt;
====Ciklus kifejtés====&lt;br /&gt;
====Automatikus ciklusösszevonás====&lt;/div&gt;</summary>
		<author><name>Szigeti(AT)niif.hu</name></author>	</entry>

	<entry>
		<id>https://wiki.niif.hu/index.php?title=Intel_Xeon_Phi&amp;diff=3743</id>
		<title>Intel Xeon Phi</title>
		<link rel="alternate" type="text/html" href="https://wiki.niif.hu/index.php?title=Intel_Xeon_Phi&amp;diff=3743"/>
				<updated>2016-05-04T07:58:54Z</updated>
		
		<summary type="html">&lt;p&gt;Szigeti(AT)niif.hu: /* AoS -&amp;gt; SoA */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Az oldalon található információk segítséget nyújtanak a HPC felhasználóinknak, hogy alkalmazásaikat miként portolhatják valamint optimalizálhatják a hatékonyabb futást szem előtt tartva Intel Xeon Phi koprocesszor kártyákon.&lt;br /&gt;
&lt;br /&gt;
==Hardver bemutatása==&lt;br /&gt;
Intel® Xeon Phi™ Coprocessor 7120P&lt;br /&gt;
&amp;lt;gallery&amp;gt;&lt;br /&gt;
Fájl:Xeon-phi-family-rwd.png.rendition.intel.web.720.405.png|&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-  align=&amp;quot;center&amp;quot;&lt;br /&gt;
! CPU&lt;br /&gt;
! threads/Core&lt;br /&gt;
!max threads&lt;br /&gt;
! CPU Freq&lt;br /&gt;
! Memória&lt;br /&gt;
! L1 cache&lt;br /&gt;
! L2 cache&lt;br /&gt;
! Interfész&lt;br /&gt;
! Ptot&lt;br /&gt;
! Számítási kapacitás&lt;br /&gt;
|-  align=&amp;quot;center&amp;quot;&lt;br /&gt;
| 61&lt;br /&gt;
| 4 Hardver&lt;br /&gt;
| 244&lt;br /&gt;
| 1238 GHz&lt;br /&gt;
| 16GB GDDR5&lt;br /&gt;
| 32KB/core&lt;br /&gt;
| 512KB/core&lt;br /&gt;
| PCIe&lt;br /&gt;
| 300W&lt;br /&gt;
| 1,2 TFlop/s dupla pontosság&lt;br /&gt;
|-  align=&amp;quot;center&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
| OpenMP-nél csak '''240'''&lt;br /&gt;
|&lt;br /&gt;
| ~100 cycles&lt;br /&gt;
~174 GB/s&lt;br /&gt;
| ~3 cycles&lt;br /&gt;
| ~10 cycles&lt;br /&gt;
| ~7 GB/s&lt;br /&gt;
~1 microsecond&lt;br /&gt;
|&lt;br /&gt;
| 2,4 TFlop/s egyszeres pontosság&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Az Intel® 1. generációs Xeon Phi™ koprocesszorában található 61 processzor ugyanazt a 16GB memóriát éri el, de semmilyen módon se fér hozzá közvetlenül a hoszt gép fizikai memóriájához.&amp;lt;br /&amp;gt;&lt;br /&gt;
[[Budapest2_klaszter|Budapest2]] és [[HPC#Szupersz.C3.A1m.C3.ADt.C3.B3g.C3.A9peink_.C3.B6sszehasonl.C3.ADt.C3.A1sa|Debrecen3-Phi]] gépeinkben számítási nodonként 2-2 Phi™ koprocesszor található. &amp;lt;br/&amp;gt;&lt;br /&gt;
A kártyákon speciális célú Linux kernel fut melyre a belépés limitáltan csak a rendszergazdák számára lehetséges SSH-n keresztül a hoszt gépről. Természetesen a kártyára belépve olyan érzésünk lehet, mintha egy sok processzoros gépen dolgoznánk, ami rendelkezik rengeteg memóriával. Ez így is van, de a hatékonyabb kihasználás érdekében az SSH-n keresztüli elérés nem lehetséges, csak a Slurm segítségével valamint a programozási nyelvek által támogatott csatornákon keresztül. Ennek részletezése lentebb megtalálható.&lt;br /&gt;
&lt;br /&gt;
==Használati módok==&lt;br /&gt;
A koprocesszor használatai módjait két csoportba lehet sorolni. A '''natív''' és az '''offload''' használatot.  &amp;lt;br/&amp;gt;&lt;br /&gt;
Ezen használati módokról a lenti fejezetekben több részletet is megtudhat. Az itt megfogalmazott &amp;quot;támogatás&amp;quot; csak a mi infrastruktúránkra érvényes! &amp;lt;br /&amp;gt;&lt;br /&gt;
A bemutatott példák ''c++'' nyelven vannak, de természetesen ''c'' és ''fortran'' esetén is megtalálható az ekvivalens módszer.&lt;br /&gt;
&lt;br /&gt;
===a) nem támogatott mód===&lt;br /&gt;
====közvetlen natív használat====&lt;br /&gt;
A natív mód arra szolgál, hogy a MIC kártyára  lefordított alkalmazásunkat felmásoljuk a kártyára, majd belépünk SSH-n a kártyára és azon futtatjuk a bináris kódunkat. &amp;lt;br /&amp;gt;&lt;br /&gt;
Ez a legegyszerűbb használati mód, ugyanis nincs más dolgunk, csak a '''-mmic''' kapcsolót használni a fordításnál és a fordító egy máris a MIC architektúrára kész bináris készít nekünk. &amp;lt;br/&amp;gt;&lt;br /&gt;
Workflow a következő lenne:&lt;br /&gt;
# Alkalmazás megírása. például [[Média:hello.cc|''hello.cc'']]&lt;br /&gt;
# Alkalmazás fordítása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;icpc -o hello-MIC -mmic hello.cc&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Másolás a MIC kártyára &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;scp hello-MIC mic0:&amp;lt;/pre&amp;gt;&lt;br /&gt;
# belépés a kártyára &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;ssh mic0&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Alkalmazás futtatása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;./hello-MIC&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===b) támogatott, de nem preferált használat===&lt;br /&gt;
====automatikus kódátvitel====&lt;br /&gt;
Ebben az esetben nem kell átmásolnunk a bináris alkalmazásunkat a kártyára sőt még csak be se kell lépnünk, mert ezt a két lépést egy beépített tool végzi el. &amp;lt;br/&amp;gt;&lt;br /&gt;
A tool eléréséhez szükséges parancs neve: '''micnativeloadex'''&lt;br /&gt;
&lt;br /&gt;
Ebben az esetben a workflow a következőképpen módosul:&lt;br /&gt;
# Alkalmazás megírása. például [[Média:hello.cc|''hello.cc'']]&lt;br /&gt;
# Alkalmazás fordítása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;icpc -o hello-MIC -mmic hello.cc&amp;lt;/pre&amp;gt;&lt;br /&gt;
# A tool futásához szükséges library-k megadása &amp;lt;br&amp;gt; &amp;lt;pre&amp;gt;export SINK_LD_LIBRARY_PATH=/opt/intel/composerxe/compiler/lib/mic&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Alkalmazás futtatása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;micnativeloadex hello-MIC&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| BUDAPEST2[cn10] phi (0)$ export SINK_LD_LIBRARY_PATH=/opt/intel/composerxe/compiler/lib/mic &amp;lt;br/&amp;gt;BUDAPEST2[cn10] phi (0)$ micnativeloadex hello-MIC &amp;lt;br/&amp;gt;Hello world! I have 244 logical cores.&amp;lt;br/&amp;gt;BUDAPEST2[cn10] phi (0)$&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===c) támogatott módok===&lt;br /&gt;
====Offload====&lt;br /&gt;
Az Offload modell esetén szükséges a forráskód módosítása, mégpedig úgy, hogy azon kódrészletet, amit a koprocesszoron szeretnénk futtatni, ki kell egészíteni egy fordítási direktívával. Az alkalmazás fordításakor elegendő csak a CPU-ra való binárist készíteni mivel a modell végzi a megfelelő forrás részlet mozgatását és futtatását.&amp;lt;br /&amp;gt;&lt;br /&gt;
[[Fájl:offload.png|Offload modell]] &amp;lt;br /&amp;gt;&lt;br /&gt;
Példánkban jól látható, hogy a második kiíratás már a koprocesszoron fut.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
int main(int argc, char * argv[]) {&lt;br /&gt;
  printf(&amp;quot;Hello world from host!\n&amp;quot;);&lt;br /&gt;
#pragma offload target(mic)&lt;br /&gt;
  {&lt;br /&gt;
    printf(&amp;quot;Hello world from coprocessor!\n&amp;quot;); fflush(stdout);&lt;br /&gt;
  }&lt;br /&gt;
  printf(&amp;quot;Bye\n&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ebben a példában az operációs rendszer választja ki, hogy melyik Phi kártyán fusson le a kódrészlet. Amennyiben mi szeretnénk explicite megmondani, hogy melyik kártyán fusson, akkor a fordítási direktívában a mic kulcsszó után kell tenni egy &amp;quot;:&amp;quot;-t és a kártya sorszámát [mi esetünkben 0 vagy 1]. Ekkor így nézne ki a direktíva: ''#pragma offload target(mic:1)''&lt;br /&gt;
&lt;br /&gt;
Amennyiben az offload szekcióban függvény hívás is szerepel, akkor szükségeltetik az függvény futásához szükséges változók adatterületének átmásolása a Phi kártyára, majd a lefutás után visszamásolni a hoszt gépre. Ezen másolást az ''offload_attribute'' ''push'' és ''pop'' paraméterei segítségével határozhatjuk meg. &amp;lt;br /&amp;gt;&lt;br /&gt;
A következő mátrix-vektor szorzás példánál az A mátrix globális változó, míg a b és c vektorok a függvény paraméterei.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//A változók felmásolásra kerülnek a Xeon Phi-re&lt;br /&gt;
#pragma offload_attribute(push, target(mic))&lt;br /&gt;
  double* A;&lt;br /&gt;
  void MatrixVector(double *b, double *c, double m, double n) {&lt;br /&gt;
    ...&lt;br /&gt;
    c[] = A[..]*b[]&lt;br /&gt;
  }&lt;br /&gt;
// felmásolt objektumok visszamásolása&lt;br /&gt;
#pragma offload_attribute(pop)&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
  ...&lt;br /&gt;
#pragma offload target(mic) in(A[0:n*m])&lt;br /&gt;
// így is meg lehetne adni: #pragma offload target(mic:0) in(A : length(n*m))	        &lt;br /&gt;
  {&lt;br /&gt;
    MatrixVector(b, c, m, n);&lt;br /&gt;
  }&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ekkor minden memória területet 2x kell másolni, egyszer fel a kártyára, majd másodszor vissza a hoszt gépre, pedig elegendő lenne csak az eredmény vektort visszamásolni. Ezen optimalizálással egy későbbi fejezetben foglalkozunk. &amp;lt;p /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Futás során kaphatunk részletes riportot is, hogy pontosan milyen adatmozgatások történnek az alkalmazás futása során. Ehhez nincs szükség másra csak az '''OFFLOAD_REPORT''' környezeti változó megfelelő beállítása. A beállított értékek 1, 2 vagy 3 lehetnek, attól függően mennyire részletes riportot szeretnénk kapni. &amp;lt;br /&amp;gt;&lt;br /&gt;
beállítás:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export OFFLOAD_REPORT=3&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
példa kimenet a hello alkalmazásra:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Hello world from host!&lt;br /&gt;
[Offload] [MIC 0] [File]                    hello_offload.cc&lt;br /&gt;
[Offload] [MIC 0] [Line]                    5&lt;br /&gt;
[Offload] [MIC 0] [Tag]                     Tag 0&lt;br /&gt;
Hello world from coprocessor!&lt;br /&gt;
[Offload] [HOST]  [Tag 0] [CPU Time]        0.554653(seconds)&lt;br /&gt;
[Offload] [MIC 0] [Tag 0] [CPU-&amp;gt;MIC Data]   0 (bytes)&lt;br /&gt;
[Offload] [MIC 0] [Tag 0] [MIC Time]        0.000140(seconds)&lt;br /&gt;
[Offload] [MIC 0] [Tag 0] [MIC-&amp;gt;CPU Data]   0 (bytes)&lt;br /&gt;
Bye&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Offload + OpenMP====&lt;br /&gt;
Amennyiben nem csak 1 processzort szeretnénk használni a Phi kártyánkon és az alkalmazásunkban használt számítás megengedi a párhuzamosítást, akkor az OpenMP segítségével párhuzamosíthatjuk a megfelelő számítást. &amp;lt;br /&amp;gt;&lt;br /&gt;
[[Fájl:Offload+openmp.png|Offload modell és OpenMP]] &amp;lt;br /&amp;gt;&lt;br /&gt;
Példánk maradjon ugyanúgy a mátrix-vektor szorzás. Amennyiben a teljes kártya adta kapacitást ki szeretnénk használni, akkor az OMP threadek számát 240-re kell beállítani. Azért nem 244-re, mivel 1 CPU-t dedikáltan csak a kommunikációra használ a rendszer és 1 CPU 4 hardver threaddel rendelkezik.&lt;br /&gt;
Ekkor a példánk így változik:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;omp.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#pragma offload_attribute(push, target(mic))&lt;br /&gt;
void multiply(int n, int m, double* A, double* b, double* c){&lt;br /&gt;
  const int nCPUs = omp_get_max_threads();   //240&lt;br /&gt;
  omp_set_num_threads(nCPUs);&lt;br /&gt;
#pragma omp parallel for schedule(dynamic, 1)&lt;br /&gt;
  for ( int i = 0 ; i &amp;lt; m ; i++)&lt;br /&gt;
     for ( int j = 0 ; j &amp;lt; n ; j++)&lt;br /&gt;
       c[i] += A[i*n+j] * b[j];&lt;br /&gt;
}&lt;br /&gt;
#pragma offload_attribute(pop)&lt;br /&gt;
&lt;br /&gt;
int main(){&lt;br /&gt;
  double * A = (double*) malloc(sizeof(double)*n*m);&lt;br /&gt;
  ...&lt;br /&gt;
#pragma offload target(mic) in(A:length(n*m))&lt;br /&gt;
     multiply(n, m, A, b, c);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Az alkalmazás fordításánál a fordítónak meg kell adni, hogy használhja az openmp library-ket is. Ezt a '''-qopenmp''' kapcsolóval tudjuk elérni.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
icpc -o matrix-CPU -qopenmp matrix.cc&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====MPI + Offload + OpenMP====&lt;br /&gt;
Abban az esetben, ha 1 számítási node nem elég, MPI segítségével össze tudunk kötni több nodet, heterogén rendszert alkotva, és azokon alkalmazható a fenti Offload + OpenMP modell. &amp;lt;br /&amp;gt;&lt;br /&gt;
[[Fájl:Mpi+offload+openmp.png|MPI elosztás valamint Offload modell és OpenMP]] &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Alkalmazások optimalizálása a kártyára==&lt;br /&gt;
Ebben a fejezetben optimalizálási lehetőségeket mutatunk be, melyek segítségül szolgálhatnak az egyes alkalmazások hatékonyabb futása érdekében ezzel jobban kihasználni a rendelkezésre álló erőforrások adta számítási kapacitás.&lt;br /&gt;
&lt;br /&gt;
===Környezeti változók===&lt;br /&gt;
Lehetséges csak a Phi kártyákon használt környezeti változók beállítása, amit az alkalmazásunk használni is tud. &amp;lt;br /&amp;gt;&lt;br /&gt;
Példánkban az OpenMP esetén használható szálak számát állíthatjuk be:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export MIC_ENV_PREFIX=XEONPHI&lt;br /&gt;
export OMP_NUM_THREADS=12&lt;br /&gt;
export XEONPHI_OMP_NUM_THREADS=240&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
A beállított prefix tetszőleges karaktersorozat lehet, a lényeg, hogy ezt a beállított prefixet használjuk a környezeti változók esetén is!&lt;br /&gt;
 &lt;br /&gt;
===Hosszredukció===&lt;br /&gt;
Többnyire a fordítók, ha nem mondjuk meg explicite, a dupla pontosságú változatot választják a lehetséges változó- és függvény típusok közül. Amennyiben a számításunk nem igényli a dupla pontosságot, akkor használjuk a forrásban mindenhol az adott típus vagy függvény egyszeres pontosságú változatát, mellyel jelentős teljesítmény növekedést tudunk elérni. Továbbá az egyes műveletek futási ideje nem egyforma, így a műveletet helyettesíthetjük más művelettel, ami gyorsabb, akkor ismét javíthatunk a hatékonyságon. &amp;lt;br /&amp;gt;&lt;br /&gt;
Íme pár példa, amit alkalmazni lehet:&lt;br /&gt;
* &amp;quot;''/''&amp;quot; művelet helyett &amp;quot;''*''&amp;quot; -t használjunk, mert azt gyorsabban végre tudja hajtani a processzor&lt;br /&gt;
* ''pow'' =&amp;gt; ''1.0 / sqrt()''    , tetszőleges hatványozás, pl.: 3/2-ik hatvány&lt;br /&gt;
* hardver áltatl támogatott műveletek:&lt;br /&gt;
** ''exp'' =&amp;gt; ''exp2''&lt;br /&gt;
** ''log'' =&amp;gt; ''log2''&lt;br /&gt;
* dupla pontosság -&amp;gt; egyszeres pontosság lebegőpontos számok és műveletek esetén &lt;br /&gt;
** ''1.0'' =&amp;gt; ''1.0f''&lt;br /&gt;
** ''pow'' =&amp;gt; ''powf''&lt;br /&gt;
&lt;br /&gt;
===Vektorizáció===&lt;br /&gt;
&lt;br /&gt;
====SIMD====&lt;br /&gt;
====AoS -&amp;gt; SoA====&lt;br /&gt;
A kiindulás probléma az, hogy a struktúrákat tartalmazó vektort nem lehet jól vektorizálni, mivel a cache-ben tárolt adatok nagy többsége feleslegesen van bent, mivel azon adatokat csak egy későbbi ciklus futás során fogja az alkalmazás feldolgozni. Tehát ezen adatok jelenleg feleslegesen foglalják a cache területet, ahelyett, hogy hasznos adatot tárolnánk ott.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
struct MyStructure {&lt;br /&gt;
  float x, y, z;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void My_Function(cons int nItem, MyStructure* const Items, const float delta) {&lt;br /&gt;
  for (int i = 0; i &amp;lt; nItem; i++) {&lt;br /&gt;
    ...&lt;br /&gt;
    for (int j = 0; j &amp;lt; nItem; j++) {&lt;br /&gt;
      const float dx = Items[j].x - Items[i].x;&lt;br /&gt;
      const float dy = Items[j].y - Items[i].y;&lt;br /&gt;
      const float dz = Items[j].z - Items[i].z;&lt;br /&gt;
      ...  &lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  ... &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A vektorban a struktúrák tárolása helyett minden elemnek egy dedikált vektort allokálunk és így könnyen tudja a fordító vektorizálni azt. Természetesen segítségként a vektor feldolgozását &amp;quot;tömbösítettük&amp;quot; a tileSize méret megadásával illetve a ''unroll'' fordítási direktíva segítségével.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
struct MyStructureSet {&lt;br /&gt;
  float *x, *y, *z;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void My_Function(cons int nItem, MyStructure* const Items, const float delta) {&lt;br /&gt;
  const int tileSize = 16;&lt;br /&gt;
#pragma omp parallel for schedule(guided)&lt;br /&gt;
  for (int ii = 0; ii &amp;lt; nItem; ii+= tileSize) {&lt;br /&gt;
    ...&lt;br /&gt;
#pragma unroll(tileSize)&lt;br /&gt;
    for (int j = 0; j &amp;lt; nItem; j++) {&lt;br /&gt;
#pragma unroll(tileSize)&lt;br /&gt;
      for (int i = ii; i &amp;lt; ii + tileSize; i++) {&lt;br /&gt;
        const float dx = Items.x[j] - Items.x[i];&lt;br /&gt;
        const float dy = Items.y[j] - Items.y[i];&lt;br /&gt;
        const float dz = Items.z[j] - Items.z[i];&lt;br /&gt;
        ...&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  ... &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Hatékony hossz választás====&lt;br /&gt;
Olyan esetben jelent előnyt, ha például az n*n-es mátrix esetén n nem osztható 16-val, mert akkor a vektorizáció nem teljesen hajtható végre. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// n%16 != 0&lt;br /&gt;
float* A = _mm_malloc(sizeof(float)*n*n, 64);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Ilyen esetben mindig marad egy kis vektor részlet, amire elemenként kell elvégezni a műveletet.&amp;lt;br /&amp;gt;&lt;br /&gt;
A megoldást az jelenti, hogy a memória allokálást kiterjesztjük és nagyobb memória területet foglalunk a változónak, így már alkalmazható lesz a vektorizáció, mivel a maradvány területen is el tudjuk végezni a műveletet egy lépésben.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int lda=n;&lt;br /&gt;
if (n % 16 != 0) lda += (16 - n%16); // most lda%16==0&lt;br /&gt;
float* A = _mm_malloc(sizeof(float)*n*lda, 64);&lt;br /&gt;
&lt;br /&gt;
// használat&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
  for (int j = 0; j &amp;lt; n; j++)&lt;br /&gt;
    A[i*lda + j] = ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====&amp;quot;Héj ciklusok&amp;quot; kiküszöbölése====&lt;br /&gt;
Ciklusok esetén lehetséges, hogy a vektorizációt nem teljes mértékben ismeri fel a fordító program. &lt;br /&gt;
Ekkor segíthetünk a fordítónak egy fordítási direktívával ('''#pragma vector aligned'''), ha teljes mértékben biztosak vagyunk, hogy a ciklusunk vektorizálható!&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma vector aligned&lt;br /&gt;
  for (int j = 0; j &amp;lt; n; j++)&lt;br /&gt;
    A[i*lda + j] -= ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Ettől jelentős futás idejű javulást érhetünk el, mivel ezzel a #pragma-val kikapcsoltuk a futás idejű ellenőrzést, de előfordulhatnak olyan esetek, főleg amikor nem jól mértük fel a ciklusunkat és nem sikerült a vektorizálás, hogy ''Segmentation fault'' hibára fogunk futni.&lt;br /&gt;
&lt;br /&gt;
====Regularizáció====&lt;br /&gt;
Van egy ciklusunk, ami egy vektor elemeit módosítja. A processzor az adatokat &amp;quot;csokorba szedve&amp;quot; emeli be a belső kis méretű cache-be. Egyszerre természetesen több vektor elem is bekerül a a cache-be ezzel gyorsítva a feldolgozást. Ezen vektor iterációk valahogyan ráilleszkedik a vektorra.&lt;br /&gt;
&lt;br /&gt;
[[Fájl:regularization.png|Vektor regularizáció]]&lt;br /&gt;
&lt;br /&gt;
Abban az esetben, amikor ez az illesztés nem teljesen tökéletes (lsd. piros elemek), keletkeznem héj ciklusok, melyeket a regularizációval is eliminálni lehet. &amp;lt;br /&amp;gt;&lt;br /&gt;
A feladat eredetileg valahogyan így nézhet ki:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
for (int i = 0; i&amp;lt;n; i++)&lt;br /&gt;
  A[i]=...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ez a következő átalakítással már alkalmas vektorizációra és nem lesz &amp;quot;peel loop&amp;quot; probléma.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
for (int b=0; b&amp;lt;n; b++) {&lt;br /&gt;
  const int jStart = b- b%16; // mindig osztható 16-val&lt;br /&gt;
  ...&lt;br /&gt;
#pragma simd&lt;br /&gt;
  for (int j = jStart; j&amp;lt;n; j++)&lt;br /&gt;
    A[j]=...&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Párhuzamosítás===&lt;br /&gt;
====Párhuzamos szálak ütemezése====&lt;br /&gt;
====Ciklus kifejtés====&lt;br /&gt;
====Automatikus ciklusösszevonás====&lt;/div&gt;</summary>
		<author><name>Szigeti(AT)niif.hu</name></author>	</entry>

	<entry>
		<id>https://wiki.niif.hu/index.php?title=Intel_Xeon_Phi&amp;diff=3742</id>
		<title>Intel Xeon Phi</title>
		<link rel="alternate" type="text/html" href="https://wiki.niif.hu/index.php?title=Intel_Xeon_Phi&amp;diff=3742"/>
				<updated>2016-05-03T12:42:27Z</updated>
		
		<summary type="html">&lt;p&gt;Szigeti(AT)niif.hu: /* Regularizáció */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Az oldalon található információk segítséget nyújtanak a HPC felhasználóinknak, hogy alkalmazásaikat miként portolhatják valamint optimalizálhatják a hatékonyabb futást szem előtt tartva Intel Xeon Phi koprocesszor kártyákon.&lt;br /&gt;
&lt;br /&gt;
==Hardver bemutatása==&lt;br /&gt;
Intel® Xeon Phi™ Coprocessor 7120P&lt;br /&gt;
&amp;lt;gallery&amp;gt;&lt;br /&gt;
Fájl:Xeon-phi-family-rwd.png.rendition.intel.web.720.405.png|&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-  align=&amp;quot;center&amp;quot;&lt;br /&gt;
! CPU&lt;br /&gt;
! threads/Core&lt;br /&gt;
!max threads&lt;br /&gt;
! CPU Freq&lt;br /&gt;
! Memória&lt;br /&gt;
! L1 cache&lt;br /&gt;
! L2 cache&lt;br /&gt;
! Interfész&lt;br /&gt;
! Ptot&lt;br /&gt;
! Számítási kapacitás&lt;br /&gt;
|-  align=&amp;quot;center&amp;quot;&lt;br /&gt;
| 61&lt;br /&gt;
| 4 Hardver&lt;br /&gt;
| 244&lt;br /&gt;
| 1238 GHz&lt;br /&gt;
| 16GB GDDR5&lt;br /&gt;
| 32KB/core&lt;br /&gt;
| 512KB/core&lt;br /&gt;
| PCIe&lt;br /&gt;
| 300W&lt;br /&gt;
| 1,2 TFlop/s dupla pontosság&lt;br /&gt;
|-  align=&amp;quot;center&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
| OpenMP-nél csak '''240'''&lt;br /&gt;
|&lt;br /&gt;
| ~100 cycles&lt;br /&gt;
~174 GB/s&lt;br /&gt;
| ~3 cycles&lt;br /&gt;
| ~10 cycles&lt;br /&gt;
| ~7 GB/s&lt;br /&gt;
~1 microsecond&lt;br /&gt;
|&lt;br /&gt;
| 2,4 TFlop/s egyszeres pontosság&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Az Intel® 1. generációs Xeon Phi™ koprocesszorában található 61 processzor ugyanazt a 16GB memóriát éri el, de semmilyen módon se fér hozzá közvetlenül a hoszt gép fizikai memóriájához.&amp;lt;br /&amp;gt;&lt;br /&gt;
[[Budapest2_klaszter|Budapest2]] és [[HPC#Szupersz.C3.A1m.C3.ADt.C3.B3g.C3.A9peink_.C3.B6sszehasonl.C3.ADt.C3.A1sa|Debrecen3-Phi]] gépeinkben számítási nodonként 2-2 Phi™ koprocesszor található. &amp;lt;br/&amp;gt;&lt;br /&gt;
A kártyákon speciális célú Linux kernel fut melyre a belépés limitáltan csak a rendszergazdák számára lehetséges SSH-n keresztül a hoszt gépről. Természetesen a kártyára belépve olyan érzésünk lehet, mintha egy sok processzoros gépen dolgoznánk, ami rendelkezik rengeteg memóriával. Ez így is van, de a hatékonyabb kihasználás érdekében az SSH-n keresztüli elérés nem lehetséges, csak a Slurm segítségével valamint a programozási nyelvek által támogatott csatornákon keresztül. Ennek részletezése lentebb megtalálható.&lt;br /&gt;
&lt;br /&gt;
==Használati módok==&lt;br /&gt;
A koprocesszor használatai módjait két csoportba lehet sorolni. A '''natív''' és az '''offload''' használatot.  &amp;lt;br/&amp;gt;&lt;br /&gt;
Ezen használati módokról a lenti fejezetekben több részletet is megtudhat. Az itt megfogalmazott &amp;quot;támogatás&amp;quot; csak a mi infrastruktúránkra érvényes! &amp;lt;br /&amp;gt;&lt;br /&gt;
A bemutatott példák ''c++'' nyelven vannak, de természetesen ''c'' és ''fortran'' esetén is megtalálható az ekvivalens módszer.&lt;br /&gt;
&lt;br /&gt;
===a) nem támogatott mód===&lt;br /&gt;
====közvetlen natív használat====&lt;br /&gt;
A natív mód arra szolgál, hogy a MIC kártyára  lefordított alkalmazásunkat felmásoljuk a kártyára, majd belépünk SSH-n a kártyára és azon futtatjuk a bináris kódunkat. &amp;lt;br /&amp;gt;&lt;br /&gt;
Ez a legegyszerűbb használati mód, ugyanis nincs más dolgunk, csak a '''-mmic''' kapcsolót használni a fordításnál és a fordító egy máris a MIC architektúrára kész bináris készít nekünk. &amp;lt;br/&amp;gt;&lt;br /&gt;
Workflow a következő lenne:&lt;br /&gt;
# Alkalmazás megírása. például [[Média:hello.cc|''hello.cc'']]&lt;br /&gt;
# Alkalmazás fordítása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;icpc -o hello-MIC -mmic hello.cc&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Másolás a MIC kártyára &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;scp hello-MIC mic0:&amp;lt;/pre&amp;gt;&lt;br /&gt;
# belépés a kártyára &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;ssh mic0&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Alkalmazás futtatása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;./hello-MIC&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===b) támogatott, de nem preferált használat===&lt;br /&gt;
====automatikus kódátvitel====&lt;br /&gt;
Ebben az esetben nem kell átmásolnunk a bináris alkalmazásunkat a kártyára sőt még csak be se kell lépnünk, mert ezt a két lépést egy beépített tool végzi el. &amp;lt;br/&amp;gt;&lt;br /&gt;
A tool eléréséhez szükséges parancs neve: '''micnativeloadex'''&lt;br /&gt;
&lt;br /&gt;
Ebben az esetben a workflow a következőképpen módosul:&lt;br /&gt;
# Alkalmazás megírása. például [[Média:hello.cc|''hello.cc'']]&lt;br /&gt;
# Alkalmazás fordítása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;icpc -o hello-MIC -mmic hello.cc&amp;lt;/pre&amp;gt;&lt;br /&gt;
# A tool futásához szükséges library-k megadása &amp;lt;br&amp;gt; &amp;lt;pre&amp;gt;export SINK_LD_LIBRARY_PATH=/opt/intel/composerxe/compiler/lib/mic&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Alkalmazás futtatása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;micnativeloadex hello-MIC&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| BUDAPEST2[cn10] phi (0)$ export SINK_LD_LIBRARY_PATH=/opt/intel/composerxe/compiler/lib/mic &amp;lt;br/&amp;gt;BUDAPEST2[cn10] phi (0)$ micnativeloadex hello-MIC &amp;lt;br/&amp;gt;Hello world! I have 244 logical cores.&amp;lt;br/&amp;gt;BUDAPEST2[cn10] phi (0)$&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===c) támogatott módok===&lt;br /&gt;
====Offload====&lt;br /&gt;
Az Offload modell esetén szükséges a forráskód módosítása, mégpedig úgy, hogy azon kódrészletet, amit a koprocesszoron szeretnénk futtatni, ki kell egészíteni egy fordítási direktívával. Az alkalmazás fordításakor elegendő csak a CPU-ra való binárist készíteni mivel a modell végzi a megfelelő forrás részlet mozgatását és futtatását.&amp;lt;br /&amp;gt;&lt;br /&gt;
[[Fájl:offload.png|Offload modell]] &amp;lt;br /&amp;gt;&lt;br /&gt;
Példánkban jól látható, hogy a második kiíratás már a koprocesszoron fut.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
int main(int argc, char * argv[]) {&lt;br /&gt;
  printf(&amp;quot;Hello world from host!\n&amp;quot;);&lt;br /&gt;
#pragma offload target(mic)&lt;br /&gt;
  {&lt;br /&gt;
    printf(&amp;quot;Hello world from coprocessor!\n&amp;quot;); fflush(stdout);&lt;br /&gt;
  }&lt;br /&gt;
  printf(&amp;quot;Bye\n&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ebben a példában az operációs rendszer választja ki, hogy melyik Phi kártyán fusson le a kódrészlet. Amennyiben mi szeretnénk explicite megmondani, hogy melyik kártyán fusson, akkor a fordítási direktívában a mic kulcsszó után kell tenni egy &amp;quot;:&amp;quot;-t és a kártya sorszámát [mi esetünkben 0 vagy 1]. Ekkor így nézne ki a direktíva: ''#pragma offload target(mic:1)''&lt;br /&gt;
&lt;br /&gt;
Amennyiben az offload szekcióban függvény hívás is szerepel, akkor szükségeltetik az függvény futásához szükséges változók adatterületének átmásolása a Phi kártyára, majd a lefutás után visszamásolni a hoszt gépre. Ezen másolást az ''offload_attribute'' ''push'' és ''pop'' paraméterei segítségével határozhatjuk meg. &amp;lt;br /&amp;gt;&lt;br /&gt;
A következő mátrix-vektor szorzás példánál az A mátrix globális változó, míg a b és c vektorok a függvény paraméterei.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//A változók felmásolásra kerülnek a Xeon Phi-re&lt;br /&gt;
#pragma offload_attribute(push, target(mic))&lt;br /&gt;
  double* A;&lt;br /&gt;
  void MatrixVector(double *b, double *c, double m, double n) {&lt;br /&gt;
    ...&lt;br /&gt;
    c[] = A[..]*b[]&lt;br /&gt;
  }&lt;br /&gt;
// felmásolt objektumok visszamásolása&lt;br /&gt;
#pragma offload_attribute(pop)&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
  ...&lt;br /&gt;
#pragma offload target(mic) in(A[0:n*m])&lt;br /&gt;
// így is meg lehetne adni: #pragma offload target(mic:0) in(A : length(n*m))	        &lt;br /&gt;
  {&lt;br /&gt;
    MatrixVector(b, c, m, n);&lt;br /&gt;
  }&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ekkor minden memória területet 2x kell másolni, egyszer fel a kártyára, majd másodszor vissza a hoszt gépre, pedig elegendő lenne csak az eredmény vektort visszamásolni. Ezen optimalizálással egy későbbi fejezetben foglalkozunk. &amp;lt;p /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Futás során kaphatunk részletes riportot is, hogy pontosan milyen adatmozgatások történnek az alkalmazás futása során. Ehhez nincs szükség másra csak az '''OFFLOAD_REPORT''' környezeti változó megfelelő beállítása. A beállított értékek 1, 2 vagy 3 lehetnek, attól függően mennyire részletes riportot szeretnénk kapni. &amp;lt;br /&amp;gt;&lt;br /&gt;
beállítás:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export OFFLOAD_REPORT=3&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
példa kimenet a hello alkalmazásra:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Hello world from host!&lt;br /&gt;
[Offload] [MIC 0] [File]                    hello_offload.cc&lt;br /&gt;
[Offload] [MIC 0] [Line]                    5&lt;br /&gt;
[Offload] [MIC 0] [Tag]                     Tag 0&lt;br /&gt;
Hello world from coprocessor!&lt;br /&gt;
[Offload] [HOST]  [Tag 0] [CPU Time]        0.554653(seconds)&lt;br /&gt;
[Offload] [MIC 0] [Tag 0] [CPU-&amp;gt;MIC Data]   0 (bytes)&lt;br /&gt;
[Offload] [MIC 0] [Tag 0] [MIC Time]        0.000140(seconds)&lt;br /&gt;
[Offload] [MIC 0] [Tag 0] [MIC-&amp;gt;CPU Data]   0 (bytes)&lt;br /&gt;
Bye&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Offload + OpenMP====&lt;br /&gt;
Amennyiben nem csak 1 processzort szeretnénk használni a Phi kártyánkon és az alkalmazásunkban használt számítás megengedi a párhuzamosítást, akkor az OpenMP segítségével párhuzamosíthatjuk a megfelelő számítást. &amp;lt;br /&amp;gt;&lt;br /&gt;
[[Fájl:Offload+openmp.png|Offload modell és OpenMP]] &amp;lt;br /&amp;gt;&lt;br /&gt;
Példánk maradjon ugyanúgy a mátrix-vektor szorzás. Amennyiben a teljes kártya adta kapacitást ki szeretnénk használni, akkor az OMP threadek számát 240-re kell beállítani. Azért nem 244-re, mivel 1 CPU-t dedikáltan csak a kommunikációra használ a rendszer és 1 CPU 4 hardver threaddel rendelkezik.&lt;br /&gt;
Ekkor a példánk így változik:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;omp.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#pragma offload_attribute(push, target(mic))&lt;br /&gt;
void multiply(int n, int m, double* A, double* b, double* c){&lt;br /&gt;
  const int nCPUs = omp_get_max_threads();   //240&lt;br /&gt;
  omp_set_num_threads(nCPUs);&lt;br /&gt;
#pragma omp parallel for schedule(dynamic, 1)&lt;br /&gt;
  for ( int i = 0 ; i &amp;lt; m ; i++)&lt;br /&gt;
     for ( int j = 0 ; j &amp;lt; n ; j++)&lt;br /&gt;
       c[i] += A[i*n+j] * b[j];&lt;br /&gt;
}&lt;br /&gt;
#pragma offload_attribute(pop)&lt;br /&gt;
&lt;br /&gt;
int main(){&lt;br /&gt;
  double * A = (double*) malloc(sizeof(double)*n*m);&lt;br /&gt;
  ...&lt;br /&gt;
#pragma offload target(mic) in(A:length(n*m))&lt;br /&gt;
     multiply(n, m, A, b, c);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Az alkalmazás fordításánál a fordítónak meg kell adni, hogy használhja az openmp library-ket is. Ezt a '''-qopenmp''' kapcsolóval tudjuk elérni.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
icpc -o matrix-CPU -qopenmp matrix.cc&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====MPI + Offload + OpenMP====&lt;br /&gt;
Abban az esetben, ha 1 számítási node nem elég, MPI segítségével össze tudunk kötni több nodet, heterogén rendszert alkotva, és azokon alkalmazható a fenti Offload + OpenMP modell. &amp;lt;br /&amp;gt;&lt;br /&gt;
[[Fájl:Mpi+offload+openmp.png|MPI elosztás valamint Offload modell és OpenMP]] &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Alkalmazások optimalizálása a kártyára==&lt;br /&gt;
Ebben a fejezetben optimalizálási lehetőségeket mutatunk be, melyek segítségül szolgálhatnak az egyes alkalmazások hatékonyabb futása érdekében ezzel jobban kihasználni a rendelkezésre álló erőforrások adta számítási kapacitás.&lt;br /&gt;
&lt;br /&gt;
===Környezeti változók===&lt;br /&gt;
Lehetséges csak a Phi kártyákon használt környezeti változók beállítása, amit az alkalmazásunk használni is tud. &amp;lt;br /&amp;gt;&lt;br /&gt;
Példánkban az OpenMP esetén használható szálak számát állíthatjuk be:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export MIC_ENV_PREFIX=XEONPHI&lt;br /&gt;
export OMP_NUM_THREADS=12&lt;br /&gt;
export XEONPHI_OMP_NUM_THREADS=240&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
A beállított prefix tetszőleges karaktersorozat lehet, a lényeg, hogy ezt a beállított prefixet használjuk a környezeti változók esetén is!&lt;br /&gt;
 &lt;br /&gt;
===Hosszredukció===&lt;br /&gt;
Többnyire a fordítók, ha nem mondjuk meg explicite, a dupla pontosságú változatot választják a lehetséges változó- és függvény típusok közül. Amennyiben a számításunk nem igényli a dupla pontosságot, akkor használjuk a forrásban mindenhol az adott típus vagy függvény egyszeres pontosságú változatát, mellyel jelentős teljesítmény növekedést tudunk elérni. Továbbá az egyes műveletek futási ideje nem egyforma, így a műveletet helyettesíthetjük más művelettel, ami gyorsabb, akkor ismét javíthatunk a hatékonyságon. &amp;lt;br /&amp;gt;&lt;br /&gt;
Íme pár példa, amit alkalmazni lehet:&lt;br /&gt;
* &amp;quot;''/''&amp;quot; művelet helyett &amp;quot;''*''&amp;quot; -t használjunk, mert azt gyorsabban végre tudja hajtani a processzor&lt;br /&gt;
* ''pow'' =&amp;gt; ''1.0 / sqrt()''    , tetszőleges hatványozás, pl.: 3/2-ik hatvány&lt;br /&gt;
* hardver áltatl támogatott műveletek:&lt;br /&gt;
** ''exp'' =&amp;gt; ''exp2''&lt;br /&gt;
** ''log'' =&amp;gt; ''log2''&lt;br /&gt;
* dupla pontosság -&amp;gt; egyszeres pontosság lebegőpontos számok és műveletek esetén &lt;br /&gt;
** ''1.0'' =&amp;gt; ''1.0f''&lt;br /&gt;
** ''pow'' =&amp;gt; ''powf''&lt;br /&gt;
&lt;br /&gt;
===Vektorizáció===&lt;br /&gt;
&lt;br /&gt;
====SIMD====&lt;br /&gt;
====AoS -&amp;gt; SoA====&lt;br /&gt;
====Hatékony hossz választás====&lt;br /&gt;
Olyan esetben jelent előnyt, ha például az n*n-es mátrix esetén n nem osztható 16-val, mert akkor a vektorizáció nem teljesen hajtható végre. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// n%16 != 0&lt;br /&gt;
float* A = _mm_malloc(sizeof(float)*n*n, 64);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Ilyen esetben mindig marad egy kis vektor részlet, amire elemenként kell elvégezni a műveletet.&amp;lt;br /&amp;gt;&lt;br /&gt;
A megoldást az jelenti, hogy a memória allokálást kiterjesztjük és nagyobb memória területet foglalunk a változónak, így már alkalmazható lesz a vektorizáció, mivel a maradvány területen is el tudjuk végezni a műveletet egy lépésben.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int lda=n;&lt;br /&gt;
if (n % 16 != 0) lda += (16 - n%16); // most lda%16==0&lt;br /&gt;
float* A = _mm_malloc(sizeof(float)*n*lda, 64);&lt;br /&gt;
&lt;br /&gt;
// használat&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
  for (int j = 0; j &amp;lt; n; j++)&lt;br /&gt;
    A[i*lda + j] = ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====&amp;quot;Héj ciklusok&amp;quot; kiküszöbölése====&lt;br /&gt;
Ciklusok esetén lehetséges, hogy a vektorizációt nem teljes mértékben ismeri fel a fordító program. &lt;br /&gt;
Ekkor segíthetünk a fordítónak egy fordítási direktívával ('''#pragma vector aligned'''), ha teljes mértékben biztosak vagyunk, hogy a ciklusunk vektorizálható!&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma vector aligned&lt;br /&gt;
  for (int j = 0; j &amp;lt; n; j++)&lt;br /&gt;
    A[i*lda + j] -= ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Ettől jelentős futás idejű javulást érhetünk el, mivel ezzel a #pragma-val kikapcsoltuk a futás idejű ellenőrzést, de előfordulhatnak olyan esetek, főleg amikor nem jól mértük fel a ciklusunkat és nem sikerült a vektorizálás, hogy ''Segmentation fault'' hibára fogunk futni.&lt;br /&gt;
&lt;br /&gt;
====Regularizáció====&lt;br /&gt;
Van egy ciklusunk, ami egy vektor elemeit módosítja. A processzor az adatokat &amp;quot;csokorba szedve&amp;quot; emeli be a belső kis méretű cache-be. Egyszerre természetesen több vektor elem is bekerül a a cache-be ezzel gyorsítva a feldolgozást. Ezen vektor iterációk valahogyan ráilleszkedik a vektorra.&lt;br /&gt;
&lt;br /&gt;
[[Fájl:regularization.png|Vektor regularizáció]]&lt;br /&gt;
&lt;br /&gt;
Abban az esetben, amikor ez az illesztés nem teljesen tökéletes (lsd. piros elemek), keletkeznem héj ciklusok, melyeket a regularizációval is eliminálni lehet. &amp;lt;br /&amp;gt;&lt;br /&gt;
A feladat eredetileg valahogyan így nézhet ki:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
for (int i = 0; i&amp;lt;n; i++)&lt;br /&gt;
  A[i]=...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ez a következő átalakítással már alkalmas vektorizációra és nem lesz &amp;quot;peel loop&amp;quot; probléma.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
for (int b=0; b&amp;lt;n; b++) {&lt;br /&gt;
  const int jStart = b- b%16; // mindig osztható 16-val&lt;br /&gt;
  ...&lt;br /&gt;
#pragma simd&lt;br /&gt;
  for (int j = jStart; j&amp;lt;n; j++)&lt;br /&gt;
    A[j]=...&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Párhuzamosítás===&lt;br /&gt;
====Párhuzamos szálak ütemezése====&lt;br /&gt;
====Ciklus kifejtés====&lt;br /&gt;
====Automatikus ciklusösszevonás====&lt;/div&gt;</summary>
		<author><name>Szigeti(AT)niif.hu</name></author>	</entry>

	<entry>
		<id>https://wiki.niif.hu/index.php?title=F%C3%A1jl:Regularization.png&amp;diff=3741</id>
		<title>Fájl:Regularization.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.niif.hu/index.php?title=F%C3%A1jl:Regularization.png&amp;diff=3741"/>
				<updated>2016-05-03T12:41:26Z</updated>
		
		<summary type="html">&lt;p&gt;Szigeti(AT)niif.hu: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Szigeti(AT)niif.hu</name></author>	</entry>

	<entry>
		<id>https://wiki.niif.hu/index.php?title=Intel_Xeon_Phi&amp;diff=3740</id>
		<title>Intel Xeon Phi</title>
		<link rel="alternate" type="text/html" href="https://wiki.niif.hu/index.php?title=Intel_Xeon_Phi&amp;diff=3740"/>
				<updated>2016-05-03T12:17:07Z</updated>
		
		<summary type="html">&lt;p&gt;Szigeti(AT)niif.hu: /* Regularizáció */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Az oldalon található információk segítséget nyújtanak a HPC felhasználóinknak, hogy alkalmazásaikat miként portolhatják valamint optimalizálhatják a hatékonyabb futást szem előtt tartva Intel Xeon Phi koprocesszor kártyákon.&lt;br /&gt;
&lt;br /&gt;
==Hardver bemutatása==&lt;br /&gt;
Intel® Xeon Phi™ Coprocessor 7120P&lt;br /&gt;
&amp;lt;gallery&amp;gt;&lt;br /&gt;
Fájl:Xeon-phi-family-rwd.png.rendition.intel.web.720.405.png|&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-  align=&amp;quot;center&amp;quot;&lt;br /&gt;
! CPU&lt;br /&gt;
! threads/Core&lt;br /&gt;
!max threads&lt;br /&gt;
! CPU Freq&lt;br /&gt;
! Memória&lt;br /&gt;
! L1 cache&lt;br /&gt;
! L2 cache&lt;br /&gt;
! Interfész&lt;br /&gt;
! Ptot&lt;br /&gt;
! Számítási kapacitás&lt;br /&gt;
|-  align=&amp;quot;center&amp;quot;&lt;br /&gt;
| 61&lt;br /&gt;
| 4 Hardver&lt;br /&gt;
| 244&lt;br /&gt;
| 1238 GHz&lt;br /&gt;
| 16GB GDDR5&lt;br /&gt;
| 32KB/core&lt;br /&gt;
| 512KB/core&lt;br /&gt;
| PCIe&lt;br /&gt;
| 300W&lt;br /&gt;
| 1,2 TFlop/s dupla pontosság&lt;br /&gt;
|-  align=&amp;quot;center&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
| OpenMP-nél csak '''240'''&lt;br /&gt;
|&lt;br /&gt;
| ~100 cycles&lt;br /&gt;
~174 GB/s&lt;br /&gt;
| ~3 cycles&lt;br /&gt;
| ~10 cycles&lt;br /&gt;
| ~7 GB/s&lt;br /&gt;
~1 microsecond&lt;br /&gt;
|&lt;br /&gt;
| 2,4 TFlop/s egyszeres pontosság&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Az Intel® 1. generációs Xeon Phi™ koprocesszorában található 61 processzor ugyanazt a 16GB memóriát éri el, de semmilyen módon se fér hozzá közvetlenül a hoszt gép fizikai memóriájához.&amp;lt;br /&amp;gt;&lt;br /&gt;
[[Budapest2_klaszter|Budapest2]] és [[HPC#Szupersz.C3.A1m.C3.ADt.C3.B3g.C3.A9peink_.C3.B6sszehasonl.C3.ADt.C3.A1sa|Debrecen3-Phi]] gépeinkben számítási nodonként 2-2 Phi™ koprocesszor található. &amp;lt;br/&amp;gt;&lt;br /&gt;
A kártyákon speciális célú Linux kernel fut melyre a belépés limitáltan csak a rendszergazdák számára lehetséges SSH-n keresztül a hoszt gépről. Természetesen a kártyára belépve olyan érzésünk lehet, mintha egy sok processzoros gépen dolgoznánk, ami rendelkezik rengeteg memóriával. Ez így is van, de a hatékonyabb kihasználás érdekében az SSH-n keresztüli elérés nem lehetséges, csak a Slurm segítségével valamint a programozási nyelvek által támogatott csatornákon keresztül. Ennek részletezése lentebb megtalálható.&lt;br /&gt;
&lt;br /&gt;
==Használati módok==&lt;br /&gt;
A koprocesszor használatai módjait két csoportba lehet sorolni. A '''natív''' és az '''offload''' használatot.  &amp;lt;br/&amp;gt;&lt;br /&gt;
Ezen használati módokról a lenti fejezetekben több részletet is megtudhat. Az itt megfogalmazott &amp;quot;támogatás&amp;quot; csak a mi infrastruktúránkra érvényes! &amp;lt;br /&amp;gt;&lt;br /&gt;
A bemutatott példák ''c++'' nyelven vannak, de természetesen ''c'' és ''fortran'' esetén is megtalálható az ekvivalens módszer.&lt;br /&gt;
&lt;br /&gt;
===a) nem támogatott mód===&lt;br /&gt;
====közvetlen natív használat====&lt;br /&gt;
A natív mód arra szolgál, hogy a MIC kártyára  lefordított alkalmazásunkat felmásoljuk a kártyára, majd belépünk SSH-n a kártyára és azon futtatjuk a bináris kódunkat. &amp;lt;br /&amp;gt;&lt;br /&gt;
Ez a legegyszerűbb használati mód, ugyanis nincs más dolgunk, csak a '''-mmic''' kapcsolót használni a fordításnál és a fordító egy máris a MIC architektúrára kész bináris készít nekünk. &amp;lt;br/&amp;gt;&lt;br /&gt;
Workflow a következő lenne:&lt;br /&gt;
# Alkalmazás megírása. például [[Média:hello.cc|''hello.cc'']]&lt;br /&gt;
# Alkalmazás fordítása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;icpc -o hello-MIC -mmic hello.cc&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Másolás a MIC kártyára &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;scp hello-MIC mic0:&amp;lt;/pre&amp;gt;&lt;br /&gt;
# belépés a kártyára &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;ssh mic0&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Alkalmazás futtatása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;./hello-MIC&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===b) támogatott, de nem preferált használat===&lt;br /&gt;
====automatikus kódátvitel====&lt;br /&gt;
Ebben az esetben nem kell átmásolnunk a bináris alkalmazásunkat a kártyára sőt még csak be se kell lépnünk, mert ezt a két lépést egy beépített tool végzi el. &amp;lt;br/&amp;gt;&lt;br /&gt;
A tool eléréséhez szükséges parancs neve: '''micnativeloadex'''&lt;br /&gt;
&lt;br /&gt;
Ebben az esetben a workflow a következőképpen módosul:&lt;br /&gt;
# Alkalmazás megírása. például [[Média:hello.cc|''hello.cc'']]&lt;br /&gt;
# Alkalmazás fordítása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;icpc -o hello-MIC -mmic hello.cc&amp;lt;/pre&amp;gt;&lt;br /&gt;
# A tool futásához szükséges library-k megadása &amp;lt;br&amp;gt; &amp;lt;pre&amp;gt;export SINK_LD_LIBRARY_PATH=/opt/intel/composerxe/compiler/lib/mic&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Alkalmazás futtatása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;micnativeloadex hello-MIC&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| BUDAPEST2[cn10] phi (0)$ export SINK_LD_LIBRARY_PATH=/opt/intel/composerxe/compiler/lib/mic &amp;lt;br/&amp;gt;BUDAPEST2[cn10] phi (0)$ micnativeloadex hello-MIC &amp;lt;br/&amp;gt;Hello world! I have 244 logical cores.&amp;lt;br/&amp;gt;BUDAPEST2[cn10] phi (0)$&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===c) támogatott módok===&lt;br /&gt;
====Offload====&lt;br /&gt;
Az Offload modell esetén szükséges a forráskód módosítása, mégpedig úgy, hogy azon kódrészletet, amit a koprocesszoron szeretnénk futtatni, ki kell egészíteni egy fordítási direktívával. Az alkalmazás fordításakor elegendő csak a CPU-ra való binárist készíteni mivel a modell végzi a megfelelő forrás részlet mozgatását és futtatását.&amp;lt;br /&amp;gt;&lt;br /&gt;
[[Fájl:offload.png|Offload modell]] &amp;lt;br /&amp;gt;&lt;br /&gt;
Példánkban jól látható, hogy a második kiíratás már a koprocesszoron fut.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
int main(int argc, char * argv[]) {&lt;br /&gt;
  printf(&amp;quot;Hello world from host!\n&amp;quot;);&lt;br /&gt;
#pragma offload target(mic)&lt;br /&gt;
  {&lt;br /&gt;
    printf(&amp;quot;Hello world from coprocessor!\n&amp;quot;); fflush(stdout);&lt;br /&gt;
  }&lt;br /&gt;
  printf(&amp;quot;Bye\n&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ebben a példában az operációs rendszer választja ki, hogy melyik Phi kártyán fusson le a kódrészlet. Amennyiben mi szeretnénk explicite megmondani, hogy melyik kártyán fusson, akkor a fordítási direktívában a mic kulcsszó után kell tenni egy &amp;quot;:&amp;quot;-t és a kártya sorszámát [mi esetünkben 0 vagy 1]. Ekkor így nézne ki a direktíva: ''#pragma offload target(mic:1)''&lt;br /&gt;
&lt;br /&gt;
Amennyiben az offload szekcióban függvény hívás is szerepel, akkor szükségeltetik az függvény futásához szükséges változók adatterületének átmásolása a Phi kártyára, majd a lefutás után visszamásolni a hoszt gépre. Ezen másolást az ''offload_attribute'' ''push'' és ''pop'' paraméterei segítségével határozhatjuk meg. &amp;lt;br /&amp;gt;&lt;br /&gt;
A következő mátrix-vektor szorzás példánál az A mátrix globális változó, míg a b és c vektorok a függvény paraméterei.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//A változók felmásolásra kerülnek a Xeon Phi-re&lt;br /&gt;
#pragma offload_attribute(push, target(mic))&lt;br /&gt;
  double* A;&lt;br /&gt;
  void MatrixVector(double *b, double *c, double m, double n) {&lt;br /&gt;
    ...&lt;br /&gt;
    c[] = A[..]*b[]&lt;br /&gt;
  }&lt;br /&gt;
// felmásolt objektumok visszamásolása&lt;br /&gt;
#pragma offload_attribute(pop)&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
  ...&lt;br /&gt;
#pragma offload target(mic) in(A[0:n*m])&lt;br /&gt;
// így is meg lehetne adni: #pragma offload target(mic:0) in(A : length(n*m))	        &lt;br /&gt;
  {&lt;br /&gt;
    MatrixVector(b, c, m, n);&lt;br /&gt;
  }&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ekkor minden memória területet 2x kell másolni, egyszer fel a kártyára, majd másodszor vissza a hoszt gépre, pedig elegendő lenne csak az eredmény vektort visszamásolni. Ezen optimalizálással egy későbbi fejezetben foglalkozunk. &amp;lt;p /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Futás során kaphatunk részletes riportot is, hogy pontosan milyen adatmozgatások történnek az alkalmazás futása során. Ehhez nincs szükség másra csak az '''OFFLOAD_REPORT''' környezeti változó megfelelő beállítása. A beállított értékek 1, 2 vagy 3 lehetnek, attól függően mennyire részletes riportot szeretnénk kapni. &amp;lt;br /&amp;gt;&lt;br /&gt;
beállítás:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export OFFLOAD_REPORT=3&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
példa kimenet a hello alkalmazásra:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Hello world from host!&lt;br /&gt;
[Offload] [MIC 0] [File]                    hello_offload.cc&lt;br /&gt;
[Offload] [MIC 0] [Line]                    5&lt;br /&gt;
[Offload] [MIC 0] [Tag]                     Tag 0&lt;br /&gt;
Hello world from coprocessor!&lt;br /&gt;
[Offload] [HOST]  [Tag 0] [CPU Time]        0.554653(seconds)&lt;br /&gt;
[Offload] [MIC 0] [Tag 0] [CPU-&amp;gt;MIC Data]   0 (bytes)&lt;br /&gt;
[Offload] [MIC 0] [Tag 0] [MIC Time]        0.000140(seconds)&lt;br /&gt;
[Offload] [MIC 0] [Tag 0] [MIC-&amp;gt;CPU Data]   0 (bytes)&lt;br /&gt;
Bye&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Offload + OpenMP====&lt;br /&gt;
Amennyiben nem csak 1 processzort szeretnénk használni a Phi kártyánkon és az alkalmazásunkban használt számítás megengedi a párhuzamosítást, akkor az OpenMP segítségével párhuzamosíthatjuk a megfelelő számítást. &amp;lt;br /&amp;gt;&lt;br /&gt;
[[Fájl:Offload+openmp.png|Offload modell és OpenMP]] &amp;lt;br /&amp;gt;&lt;br /&gt;
Példánk maradjon ugyanúgy a mátrix-vektor szorzás. Amennyiben a teljes kártya adta kapacitást ki szeretnénk használni, akkor az OMP threadek számát 240-re kell beállítani. Azért nem 244-re, mivel 1 CPU-t dedikáltan csak a kommunikációra használ a rendszer és 1 CPU 4 hardver threaddel rendelkezik.&lt;br /&gt;
Ekkor a példánk így változik:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;omp.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#pragma offload_attribute(push, target(mic))&lt;br /&gt;
void multiply(int n, int m, double* A, double* b, double* c){&lt;br /&gt;
  const int nCPUs = omp_get_max_threads();   //240&lt;br /&gt;
  omp_set_num_threads(nCPUs);&lt;br /&gt;
#pragma omp parallel for schedule(dynamic, 1)&lt;br /&gt;
  for ( int i = 0 ; i &amp;lt; m ; i++)&lt;br /&gt;
     for ( int j = 0 ; j &amp;lt; n ; j++)&lt;br /&gt;
       c[i] += A[i*n+j] * b[j];&lt;br /&gt;
}&lt;br /&gt;
#pragma offload_attribute(pop)&lt;br /&gt;
&lt;br /&gt;
int main(){&lt;br /&gt;
  double * A = (double*) malloc(sizeof(double)*n*m);&lt;br /&gt;
  ...&lt;br /&gt;
#pragma offload target(mic) in(A:length(n*m))&lt;br /&gt;
     multiply(n, m, A, b, c);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Az alkalmazás fordításánál a fordítónak meg kell adni, hogy használhja az openmp library-ket is. Ezt a '''-qopenmp''' kapcsolóval tudjuk elérni.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
icpc -o matrix-CPU -qopenmp matrix.cc&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====MPI + Offload + OpenMP====&lt;br /&gt;
Abban az esetben, ha 1 számítási node nem elég, MPI segítségével össze tudunk kötni több nodet, heterogén rendszert alkotva, és azokon alkalmazható a fenti Offload + OpenMP modell. &amp;lt;br /&amp;gt;&lt;br /&gt;
[[Fájl:Mpi+offload+openmp.png|MPI elosztás valamint Offload modell és OpenMP]] &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Alkalmazások optimalizálása a kártyára==&lt;br /&gt;
Ebben a fejezetben optimalizálási lehetőségeket mutatunk be, melyek segítségül szolgálhatnak az egyes alkalmazások hatékonyabb futása érdekében ezzel jobban kihasználni a rendelkezésre álló erőforrások adta számítási kapacitás.&lt;br /&gt;
&lt;br /&gt;
===Környezeti változók===&lt;br /&gt;
Lehetséges csak a Phi kártyákon használt környezeti változók beállítása, amit az alkalmazásunk használni is tud. &amp;lt;br /&amp;gt;&lt;br /&gt;
Példánkban az OpenMP esetén használható szálak számát állíthatjuk be:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export MIC_ENV_PREFIX=XEONPHI&lt;br /&gt;
export OMP_NUM_THREADS=12&lt;br /&gt;
export XEONPHI_OMP_NUM_THREADS=240&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
A beállított prefix tetszőleges karaktersorozat lehet, a lényeg, hogy ezt a beállított prefixet használjuk a környezeti változók esetén is!&lt;br /&gt;
 &lt;br /&gt;
===Hosszredukció===&lt;br /&gt;
Többnyire a fordítók, ha nem mondjuk meg explicite, a dupla pontosságú változatot választják a lehetséges változó- és függvény típusok közül. Amennyiben a számításunk nem igényli a dupla pontosságot, akkor használjuk a forrásban mindenhol az adott típus vagy függvény egyszeres pontosságú változatát, mellyel jelentős teljesítmény növekedést tudunk elérni. Továbbá az egyes műveletek futási ideje nem egyforma, így a műveletet helyettesíthetjük más művelettel, ami gyorsabb, akkor ismét javíthatunk a hatékonyságon. &amp;lt;br /&amp;gt;&lt;br /&gt;
Íme pár példa, amit alkalmazni lehet:&lt;br /&gt;
* &amp;quot;''/''&amp;quot; művelet helyett &amp;quot;''*''&amp;quot; -t használjunk, mert azt gyorsabban végre tudja hajtani a processzor&lt;br /&gt;
* ''pow'' =&amp;gt; ''1.0 / sqrt()''    , tetszőleges hatványozás, pl.: 3/2-ik hatvány&lt;br /&gt;
* hardver áltatl támogatott műveletek:&lt;br /&gt;
** ''exp'' =&amp;gt; ''exp2''&lt;br /&gt;
** ''log'' =&amp;gt; ''log2''&lt;br /&gt;
* dupla pontosság -&amp;gt; egyszeres pontosság lebegőpontos számok és műveletek esetén &lt;br /&gt;
** ''1.0'' =&amp;gt; ''1.0f''&lt;br /&gt;
** ''pow'' =&amp;gt; ''powf''&lt;br /&gt;
&lt;br /&gt;
===Vektorizáció===&lt;br /&gt;
&lt;br /&gt;
====SIMD====&lt;br /&gt;
====AoS -&amp;gt; SoA====&lt;br /&gt;
====Hatékony hossz választás====&lt;br /&gt;
Olyan esetben jelent előnyt, ha például az n*n-es mátrix esetén n nem osztható 16-val, mert akkor a vektorizáció nem teljesen hajtható végre. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// n%16 != 0&lt;br /&gt;
float* A = _mm_malloc(sizeof(float)*n*n, 64);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Ilyen esetben mindig marad egy kis vektor részlet, amire elemenként kell elvégezni a műveletet.&amp;lt;br /&amp;gt;&lt;br /&gt;
A megoldást az jelenti, hogy a memória allokálást kiterjesztjük és nagyobb memória területet foglalunk a változónak, így már alkalmazható lesz a vektorizáció, mivel a maradvány területen is el tudjuk végezni a műveletet egy lépésben.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int lda=n;&lt;br /&gt;
if (n % 16 != 0) lda += (16 - n%16); // most lda%16==0&lt;br /&gt;
float* A = _mm_malloc(sizeof(float)*n*lda, 64);&lt;br /&gt;
&lt;br /&gt;
// használat&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
  for (int j = 0; j &amp;lt; n; j++)&lt;br /&gt;
    A[i*lda + j] = ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====&amp;quot;Héj ciklusok&amp;quot; kiküszöbölése====&lt;br /&gt;
Ciklusok esetén lehetséges, hogy a vektorizációt nem teljes mértékben ismeri fel a fordító program. &lt;br /&gt;
Ekkor segíthetünk a fordítónak egy fordítási direktívával ('''#pragma vector aligned'''), ha teljes mértékben biztosak vagyunk, hogy a ciklusunk vektorizálható!&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma vector aligned&lt;br /&gt;
  for (int j = 0; j &amp;lt; n; j++)&lt;br /&gt;
    A[i*lda + j] -= ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Ettől jelentős futás idejű javulást érhetünk el, mivel ezzel a #pragma-val kikapcsoltuk a futás idejű ellenőrzést, de előfordulhatnak olyan esetek, főleg amikor nem jól mértük fel a ciklusunkat és nem sikerült a vektorizálás, hogy ''Segmentation fault'' hibára fogunk futni.&lt;br /&gt;
&lt;br /&gt;
====Regularizáció====&lt;br /&gt;
Van egy ciklusunk, ami egy vektor elemeit módosítja. A processzor az adatokat &amp;quot;csokorba szedve&amp;quot; emeli be a belső kis méretű cache-be. Egyszerre természetesen több vektor elem is bekerül a a cache-be ezzel gyorsítva a feldolgozást. Ezen vektor iterációk valahogyan ráilleszkedik a vektorra.&lt;br /&gt;
&lt;br /&gt;
[[Fájl:regularozation.png|Vektor regularizáció]]&lt;br /&gt;
&lt;br /&gt;
Abban az esetben, amikor ez az illesztés nem teljesen tökéletes (lsd. piros elemek), keletkeznem héj ciklusok, melyeket a regularizációval is eliminálni lehet. &amp;lt;br /&amp;gt;&lt;br /&gt;
A feladat eredetileg valahogyan így nézhet ki:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
for (int i = 0; i&amp;lt;n; i++)&lt;br /&gt;
  A[i]=...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ez a következő átalakítással már alkalmas vektorizációra és nem lesz &amp;quot;peel loop&amp;quot; probléma.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
for (int b=0; b&amp;lt;n; b++) {&lt;br /&gt;
  const int jStart = b- b%16; // mindig osztható 16-val&lt;br /&gt;
  ...&lt;br /&gt;
#pragma simd&lt;br /&gt;
  for (int j = jStart; j&amp;lt;n; j++)&lt;br /&gt;
    A[j]=...&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Párhuzamosítás===&lt;br /&gt;
====Párhuzamos szálak ütemezése====&lt;br /&gt;
====Ciklus kifejtés====&lt;br /&gt;
====Automatikus ciklusösszevonás====&lt;/div&gt;</summary>
		<author><name>Szigeti(AT)niif.hu</name></author>	</entry>

	<entry>
		<id>https://wiki.niif.hu/index.php?title=Intel_Xeon_Phi&amp;diff=3739</id>
		<title>Intel Xeon Phi</title>
		<link rel="alternate" type="text/html" href="https://wiki.niif.hu/index.php?title=Intel_Xeon_Phi&amp;diff=3739"/>
				<updated>2016-05-03T12:00:39Z</updated>
		
		<summary type="html">&lt;p&gt;Szigeti(AT)niif.hu: /* &amp;quot;Héj ciklusok&amp;quot; kiküszöbölése */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Az oldalon található információk segítséget nyújtanak a HPC felhasználóinknak, hogy alkalmazásaikat miként portolhatják valamint optimalizálhatják a hatékonyabb futást szem előtt tartva Intel Xeon Phi koprocesszor kártyákon.&lt;br /&gt;
&lt;br /&gt;
==Hardver bemutatása==&lt;br /&gt;
Intel® Xeon Phi™ Coprocessor 7120P&lt;br /&gt;
&amp;lt;gallery&amp;gt;&lt;br /&gt;
Fájl:Xeon-phi-family-rwd.png.rendition.intel.web.720.405.png|&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-  align=&amp;quot;center&amp;quot;&lt;br /&gt;
! CPU&lt;br /&gt;
! threads/Core&lt;br /&gt;
!max threads&lt;br /&gt;
! CPU Freq&lt;br /&gt;
! Memória&lt;br /&gt;
! L1 cache&lt;br /&gt;
! L2 cache&lt;br /&gt;
! Interfész&lt;br /&gt;
! Ptot&lt;br /&gt;
! Számítási kapacitás&lt;br /&gt;
|-  align=&amp;quot;center&amp;quot;&lt;br /&gt;
| 61&lt;br /&gt;
| 4 Hardver&lt;br /&gt;
| 244&lt;br /&gt;
| 1238 GHz&lt;br /&gt;
| 16GB GDDR5&lt;br /&gt;
| 32KB/core&lt;br /&gt;
| 512KB/core&lt;br /&gt;
| PCIe&lt;br /&gt;
| 300W&lt;br /&gt;
| 1,2 TFlop/s dupla pontosság&lt;br /&gt;
|-  align=&amp;quot;center&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
| OpenMP-nél csak '''240'''&lt;br /&gt;
|&lt;br /&gt;
| ~100 cycles&lt;br /&gt;
~174 GB/s&lt;br /&gt;
| ~3 cycles&lt;br /&gt;
| ~10 cycles&lt;br /&gt;
| ~7 GB/s&lt;br /&gt;
~1 microsecond&lt;br /&gt;
|&lt;br /&gt;
| 2,4 TFlop/s egyszeres pontosság&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Az Intel® 1. generációs Xeon Phi™ koprocesszorában található 61 processzor ugyanazt a 16GB memóriát éri el, de semmilyen módon se fér hozzá közvetlenül a hoszt gép fizikai memóriájához.&amp;lt;br /&amp;gt;&lt;br /&gt;
[[Budapest2_klaszter|Budapest2]] és [[HPC#Szupersz.C3.A1m.C3.ADt.C3.B3g.C3.A9peink_.C3.B6sszehasonl.C3.ADt.C3.A1sa|Debrecen3-Phi]] gépeinkben számítási nodonként 2-2 Phi™ koprocesszor található. &amp;lt;br/&amp;gt;&lt;br /&gt;
A kártyákon speciális célú Linux kernel fut melyre a belépés limitáltan csak a rendszergazdák számára lehetséges SSH-n keresztül a hoszt gépről. Természetesen a kártyára belépve olyan érzésünk lehet, mintha egy sok processzoros gépen dolgoznánk, ami rendelkezik rengeteg memóriával. Ez így is van, de a hatékonyabb kihasználás érdekében az SSH-n keresztüli elérés nem lehetséges, csak a Slurm segítségével valamint a programozási nyelvek által támogatott csatornákon keresztül. Ennek részletezése lentebb megtalálható.&lt;br /&gt;
&lt;br /&gt;
==Használati módok==&lt;br /&gt;
A koprocesszor használatai módjait két csoportba lehet sorolni. A '''natív''' és az '''offload''' használatot.  &amp;lt;br/&amp;gt;&lt;br /&gt;
Ezen használati módokról a lenti fejezetekben több részletet is megtudhat. Az itt megfogalmazott &amp;quot;támogatás&amp;quot; csak a mi infrastruktúránkra érvényes! &amp;lt;br /&amp;gt;&lt;br /&gt;
A bemutatott példák ''c++'' nyelven vannak, de természetesen ''c'' és ''fortran'' esetén is megtalálható az ekvivalens módszer.&lt;br /&gt;
&lt;br /&gt;
===a) nem támogatott mód===&lt;br /&gt;
====közvetlen natív használat====&lt;br /&gt;
A natív mód arra szolgál, hogy a MIC kártyára  lefordított alkalmazásunkat felmásoljuk a kártyára, majd belépünk SSH-n a kártyára és azon futtatjuk a bináris kódunkat. &amp;lt;br /&amp;gt;&lt;br /&gt;
Ez a legegyszerűbb használati mód, ugyanis nincs más dolgunk, csak a '''-mmic''' kapcsolót használni a fordításnál és a fordító egy máris a MIC architektúrára kész bináris készít nekünk. &amp;lt;br/&amp;gt;&lt;br /&gt;
Workflow a következő lenne:&lt;br /&gt;
# Alkalmazás megírása. például [[Média:hello.cc|''hello.cc'']]&lt;br /&gt;
# Alkalmazás fordítása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;icpc -o hello-MIC -mmic hello.cc&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Másolás a MIC kártyára &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;scp hello-MIC mic0:&amp;lt;/pre&amp;gt;&lt;br /&gt;
# belépés a kártyára &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;ssh mic0&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Alkalmazás futtatása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;./hello-MIC&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===b) támogatott, de nem preferált használat===&lt;br /&gt;
====automatikus kódátvitel====&lt;br /&gt;
Ebben az esetben nem kell átmásolnunk a bináris alkalmazásunkat a kártyára sőt még csak be se kell lépnünk, mert ezt a két lépést egy beépített tool végzi el. &amp;lt;br/&amp;gt;&lt;br /&gt;
A tool eléréséhez szükséges parancs neve: '''micnativeloadex'''&lt;br /&gt;
&lt;br /&gt;
Ebben az esetben a workflow a következőképpen módosul:&lt;br /&gt;
# Alkalmazás megírása. például [[Média:hello.cc|''hello.cc'']]&lt;br /&gt;
# Alkalmazás fordítása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;icpc -o hello-MIC -mmic hello.cc&amp;lt;/pre&amp;gt;&lt;br /&gt;
# A tool futásához szükséges library-k megadása &amp;lt;br&amp;gt; &amp;lt;pre&amp;gt;export SINK_LD_LIBRARY_PATH=/opt/intel/composerxe/compiler/lib/mic&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Alkalmazás futtatása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;micnativeloadex hello-MIC&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| BUDAPEST2[cn10] phi (0)$ export SINK_LD_LIBRARY_PATH=/opt/intel/composerxe/compiler/lib/mic &amp;lt;br/&amp;gt;BUDAPEST2[cn10] phi (0)$ micnativeloadex hello-MIC &amp;lt;br/&amp;gt;Hello world! I have 244 logical cores.&amp;lt;br/&amp;gt;BUDAPEST2[cn10] phi (0)$&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===c) támogatott módok===&lt;br /&gt;
====Offload====&lt;br /&gt;
Az Offload modell esetén szükséges a forráskód módosítása, mégpedig úgy, hogy azon kódrészletet, amit a koprocesszoron szeretnénk futtatni, ki kell egészíteni egy fordítási direktívával. Az alkalmazás fordításakor elegendő csak a CPU-ra való binárist készíteni mivel a modell végzi a megfelelő forrás részlet mozgatását és futtatását.&amp;lt;br /&amp;gt;&lt;br /&gt;
[[Fájl:offload.png|Offload modell]] &amp;lt;br /&amp;gt;&lt;br /&gt;
Példánkban jól látható, hogy a második kiíratás már a koprocesszoron fut.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
int main(int argc, char * argv[]) {&lt;br /&gt;
  printf(&amp;quot;Hello world from host!\n&amp;quot;);&lt;br /&gt;
#pragma offload target(mic)&lt;br /&gt;
  {&lt;br /&gt;
    printf(&amp;quot;Hello world from coprocessor!\n&amp;quot;); fflush(stdout);&lt;br /&gt;
  }&lt;br /&gt;
  printf(&amp;quot;Bye\n&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ebben a példában az operációs rendszer választja ki, hogy melyik Phi kártyán fusson le a kódrészlet. Amennyiben mi szeretnénk explicite megmondani, hogy melyik kártyán fusson, akkor a fordítási direktívában a mic kulcsszó után kell tenni egy &amp;quot;:&amp;quot;-t és a kártya sorszámát [mi esetünkben 0 vagy 1]. Ekkor így nézne ki a direktíva: ''#pragma offload target(mic:1)''&lt;br /&gt;
&lt;br /&gt;
Amennyiben az offload szekcióban függvény hívás is szerepel, akkor szükségeltetik az függvény futásához szükséges változók adatterületének átmásolása a Phi kártyára, majd a lefutás után visszamásolni a hoszt gépre. Ezen másolást az ''offload_attribute'' ''push'' és ''pop'' paraméterei segítségével határozhatjuk meg. &amp;lt;br /&amp;gt;&lt;br /&gt;
A következő mátrix-vektor szorzás példánál az A mátrix globális változó, míg a b és c vektorok a függvény paraméterei.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//A változók felmásolásra kerülnek a Xeon Phi-re&lt;br /&gt;
#pragma offload_attribute(push, target(mic))&lt;br /&gt;
  double* A;&lt;br /&gt;
  void MatrixVector(double *b, double *c, double m, double n) {&lt;br /&gt;
    ...&lt;br /&gt;
    c[] = A[..]*b[]&lt;br /&gt;
  }&lt;br /&gt;
// felmásolt objektumok visszamásolása&lt;br /&gt;
#pragma offload_attribute(pop)&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
  ...&lt;br /&gt;
#pragma offload target(mic) in(A[0:n*m])&lt;br /&gt;
// így is meg lehetne adni: #pragma offload target(mic:0) in(A : length(n*m))	        &lt;br /&gt;
  {&lt;br /&gt;
    MatrixVector(b, c, m, n);&lt;br /&gt;
  }&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ekkor minden memória területet 2x kell másolni, egyszer fel a kártyára, majd másodszor vissza a hoszt gépre, pedig elegendő lenne csak az eredmény vektort visszamásolni. Ezen optimalizálással egy későbbi fejezetben foglalkozunk. &amp;lt;p /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Futás során kaphatunk részletes riportot is, hogy pontosan milyen adatmozgatások történnek az alkalmazás futása során. Ehhez nincs szükség másra csak az '''OFFLOAD_REPORT''' környezeti változó megfelelő beállítása. A beállított értékek 1, 2 vagy 3 lehetnek, attól függően mennyire részletes riportot szeretnénk kapni. &amp;lt;br /&amp;gt;&lt;br /&gt;
beállítás:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export OFFLOAD_REPORT=3&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
példa kimenet a hello alkalmazásra:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Hello world from host!&lt;br /&gt;
[Offload] [MIC 0] [File]                    hello_offload.cc&lt;br /&gt;
[Offload] [MIC 0] [Line]                    5&lt;br /&gt;
[Offload] [MIC 0] [Tag]                     Tag 0&lt;br /&gt;
Hello world from coprocessor!&lt;br /&gt;
[Offload] [HOST]  [Tag 0] [CPU Time]        0.554653(seconds)&lt;br /&gt;
[Offload] [MIC 0] [Tag 0] [CPU-&amp;gt;MIC Data]   0 (bytes)&lt;br /&gt;
[Offload] [MIC 0] [Tag 0] [MIC Time]        0.000140(seconds)&lt;br /&gt;
[Offload] [MIC 0] [Tag 0] [MIC-&amp;gt;CPU Data]   0 (bytes)&lt;br /&gt;
Bye&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Offload + OpenMP====&lt;br /&gt;
Amennyiben nem csak 1 processzort szeretnénk használni a Phi kártyánkon és az alkalmazásunkban használt számítás megengedi a párhuzamosítást, akkor az OpenMP segítségével párhuzamosíthatjuk a megfelelő számítást. &amp;lt;br /&amp;gt;&lt;br /&gt;
[[Fájl:Offload+openmp.png|Offload modell és OpenMP]] &amp;lt;br /&amp;gt;&lt;br /&gt;
Példánk maradjon ugyanúgy a mátrix-vektor szorzás. Amennyiben a teljes kártya adta kapacitást ki szeretnénk használni, akkor az OMP threadek számát 240-re kell beállítani. Azért nem 244-re, mivel 1 CPU-t dedikáltan csak a kommunikációra használ a rendszer és 1 CPU 4 hardver threaddel rendelkezik.&lt;br /&gt;
Ekkor a példánk így változik:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;omp.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#pragma offload_attribute(push, target(mic))&lt;br /&gt;
void multiply(int n, int m, double* A, double* b, double* c){&lt;br /&gt;
  const int nCPUs = omp_get_max_threads();   //240&lt;br /&gt;
  omp_set_num_threads(nCPUs);&lt;br /&gt;
#pragma omp parallel for schedule(dynamic, 1)&lt;br /&gt;
  for ( int i = 0 ; i &amp;lt; m ; i++)&lt;br /&gt;
     for ( int j = 0 ; j &amp;lt; n ; j++)&lt;br /&gt;
       c[i] += A[i*n+j] * b[j];&lt;br /&gt;
}&lt;br /&gt;
#pragma offload_attribute(pop)&lt;br /&gt;
&lt;br /&gt;
int main(){&lt;br /&gt;
  double * A = (double*) malloc(sizeof(double)*n*m);&lt;br /&gt;
  ...&lt;br /&gt;
#pragma offload target(mic) in(A:length(n*m))&lt;br /&gt;
     multiply(n, m, A, b, c);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Az alkalmazás fordításánál a fordítónak meg kell adni, hogy használhja az openmp library-ket is. Ezt a '''-qopenmp''' kapcsolóval tudjuk elérni.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
icpc -o matrix-CPU -qopenmp matrix.cc&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====MPI + Offload + OpenMP====&lt;br /&gt;
Abban az esetben, ha 1 számítási node nem elég, MPI segítségével össze tudunk kötni több nodet, heterogén rendszert alkotva, és azokon alkalmazható a fenti Offload + OpenMP modell. &amp;lt;br /&amp;gt;&lt;br /&gt;
[[Fájl:Mpi+offload+openmp.png|MPI elosztás valamint Offload modell és OpenMP]] &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Alkalmazások optimalizálása a kártyára==&lt;br /&gt;
Ebben a fejezetben optimalizálási lehetőségeket mutatunk be, melyek segítségül szolgálhatnak az egyes alkalmazások hatékonyabb futása érdekében ezzel jobban kihasználni a rendelkezésre álló erőforrások adta számítási kapacitás.&lt;br /&gt;
&lt;br /&gt;
===Környezeti változók===&lt;br /&gt;
Lehetséges csak a Phi kártyákon használt környezeti változók beállítása, amit az alkalmazásunk használni is tud. &amp;lt;br /&amp;gt;&lt;br /&gt;
Példánkban az OpenMP esetén használható szálak számát állíthatjuk be:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export MIC_ENV_PREFIX=XEONPHI&lt;br /&gt;
export OMP_NUM_THREADS=12&lt;br /&gt;
export XEONPHI_OMP_NUM_THREADS=240&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
A beállított prefix tetszőleges karaktersorozat lehet, a lényeg, hogy ezt a beállított prefixet használjuk a környezeti változók esetén is!&lt;br /&gt;
 &lt;br /&gt;
===Hosszredukció===&lt;br /&gt;
Többnyire a fordítók, ha nem mondjuk meg explicite, a dupla pontosságú változatot választják a lehetséges változó- és függvény típusok közül. Amennyiben a számításunk nem igényli a dupla pontosságot, akkor használjuk a forrásban mindenhol az adott típus vagy függvény egyszeres pontosságú változatát, mellyel jelentős teljesítmény növekedést tudunk elérni. Továbbá az egyes műveletek futási ideje nem egyforma, így a műveletet helyettesíthetjük más művelettel, ami gyorsabb, akkor ismét javíthatunk a hatékonyságon. &amp;lt;br /&amp;gt;&lt;br /&gt;
Íme pár példa, amit alkalmazni lehet:&lt;br /&gt;
* &amp;quot;''/''&amp;quot; művelet helyett &amp;quot;''*''&amp;quot; -t használjunk, mert azt gyorsabban végre tudja hajtani a processzor&lt;br /&gt;
* ''pow'' =&amp;gt; ''1.0 / sqrt()''    , tetszőleges hatványozás, pl.: 3/2-ik hatvány&lt;br /&gt;
* hardver áltatl támogatott műveletek:&lt;br /&gt;
** ''exp'' =&amp;gt; ''exp2''&lt;br /&gt;
** ''log'' =&amp;gt; ''log2''&lt;br /&gt;
* dupla pontosság -&amp;gt; egyszeres pontosság lebegőpontos számok és műveletek esetén &lt;br /&gt;
** ''1.0'' =&amp;gt; ''1.0f''&lt;br /&gt;
** ''pow'' =&amp;gt; ''powf''&lt;br /&gt;
&lt;br /&gt;
===Vektorizáció===&lt;br /&gt;
&lt;br /&gt;
====SIMD====&lt;br /&gt;
====AoS -&amp;gt; SoA====&lt;br /&gt;
====Hatékony hossz választás====&lt;br /&gt;
Olyan esetben jelent előnyt, ha például az n*n-es mátrix esetén n nem osztható 16-val, mert akkor a vektorizáció nem teljesen hajtható végre. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// n%16 != 0&lt;br /&gt;
float* A = _mm_malloc(sizeof(float)*n*n, 64);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Ilyen esetben mindig marad egy kis vektor részlet, amire elemenként kell elvégezni a műveletet.&amp;lt;br /&amp;gt;&lt;br /&gt;
A megoldást az jelenti, hogy a memória allokálást kiterjesztjük és nagyobb memória területet foglalunk a változónak, így már alkalmazható lesz a vektorizáció, mivel a maradvány területen is el tudjuk végezni a műveletet egy lépésben.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int lda=n;&lt;br /&gt;
if (n % 16 != 0) lda += (16 - n%16); // most lda%16==0&lt;br /&gt;
float* A = _mm_malloc(sizeof(float)*n*lda, 64);&lt;br /&gt;
&lt;br /&gt;
// használat&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
  for (int j = 0; j &amp;lt; n; j++)&lt;br /&gt;
    A[i*lda + j] = ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====&amp;quot;Héj ciklusok&amp;quot; kiküszöbölése====&lt;br /&gt;
Ciklusok esetén lehetséges, hogy a vektorizációt nem teljes mértékben ismeri fel a fordító program. &lt;br /&gt;
Ekkor segíthetünk a fordítónak egy fordítási direktívával ('''#pragma vector aligned'''), ha teljes mértékben biztosak vagyunk, hogy a ciklusunk vektorizálható!&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#pragma vector aligned&lt;br /&gt;
  for (int j = 0; j &amp;lt; n; j++)&lt;br /&gt;
    A[i*lda + j] -= ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Ettől jelentős futás idejű javulást érhetünk el, mivel ezzel a #pragma-val kikapcsoltuk a futás idejű ellenőrzést, de előfordulhatnak olyan esetek, főleg amikor nem jól mértük fel a ciklusunkat és nem sikerült a vektorizálás, hogy ''Segmentation fault'' hibára fogunk futni.&lt;br /&gt;
&lt;br /&gt;
====Regularizáció====&lt;br /&gt;
===Párhuzamosítás===&lt;br /&gt;
====Párhuzamos szálak ütemezése====&lt;br /&gt;
====Ciklus kifejtés====&lt;br /&gt;
====Automatikus ciklusösszevonás====&lt;/div&gt;</summary>
		<author><name>Szigeti(AT)niif.hu</name></author>	</entry>

	<entry>
		<id>https://wiki.niif.hu/index.php?title=Intel_Xeon_Phi&amp;diff=3738</id>
		<title>Intel Xeon Phi</title>
		<link rel="alternate" type="text/html" href="https://wiki.niif.hu/index.php?title=Intel_Xeon_Phi&amp;diff=3738"/>
				<updated>2016-05-03T11:50:54Z</updated>
		
		<summary type="html">&lt;p&gt;Szigeti(AT)niif.hu: /* Hatékony hossz választás */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Az oldalon található információk segítséget nyújtanak a HPC felhasználóinknak, hogy alkalmazásaikat miként portolhatják valamint optimalizálhatják a hatékonyabb futást szem előtt tartva Intel Xeon Phi koprocesszor kártyákon.&lt;br /&gt;
&lt;br /&gt;
==Hardver bemutatása==&lt;br /&gt;
Intel® Xeon Phi™ Coprocessor 7120P&lt;br /&gt;
&amp;lt;gallery&amp;gt;&lt;br /&gt;
Fájl:Xeon-phi-family-rwd.png.rendition.intel.web.720.405.png|&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-  align=&amp;quot;center&amp;quot;&lt;br /&gt;
! CPU&lt;br /&gt;
! threads/Core&lt;br /&gt;
!max threads&lt;br /&gt;
! CPU Freq&lt;br /&gt;
! Memória&lt;br /&gt;
! L1 cache&lt;br /&gt;
! L2 cache&lt;br /&gt;
! Interfész&lt;br /&gt;
! Ptot&lt;br /&gt;
! Számítási kapacitás&lt;br /&gt;
|-  align=&amp;quot;center&amp;quot;&lt;br /&gt;
| 61&lt;br /&gt;
| 4 Hardver&lt;br /&gt;
| 244&lt;br /&gt;
| 1238 GHz&lt;br /&gt;
| 16GB GDDR5&lt;br /&gt;
| 32KB/core&lt;br /&gt;
| 512KB/core&lt;br /&gt;
| PCIe&lt;br /&gt;
| 300W&lt;br /&gt;
| 1,2 TFlop/s dupla pontosság&lt;br /&gt;
|-  align=&amp;quot;center&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
| OpenMP-nél csak '''240'''&lt;br /&gt;
|&lt;br /&gt;
| ~100 cycles&lt;br /&gt;
~174 GB/s&lt;br /&gt;
| ~3 cycles&lt;br /&gt;
| ~10 cycles&lt;br /&gt;
| ~7 GB/s&lt;br /&gt;
~1 microsecond&lt;br /&gt;
|&lt;br /&gt;
| 2,4 TFlop/s egyszeres pontosság&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Az Intel® 1. generációs Xeon Phi™ koprocesszorában található 61 processzor ugyanazt a 16GB memóriát éri el, de semmilyen módon se fér hozzá közvetlenül a hoszt gép fizikai memóriájához.&amp;lt;br /&amp;gt;&lt;br /&gt;
[[Budapest2_klaszter|Budapest2]] és [[HPC#Szupersz.C3.A1m.C3.ADt.C3.B3g.C3.A9peink_.C3.B6sszehasonl.C3.ADt.C3.A1sa|Debrecen3-Phi]] gépeinkben számítási nodonként 2-2 Phi™ koprocesszor található. &amp;lt;br/&amp;gt;&lt;br /&gt;
A kártyákon speciális célú Linux kernel fut melyre a belépés limitáltan csak a rendszergazdák számára lehetséges SSH-n keresztül a hoszt gépről. Természetesen a kártyára belépve olyan érzésünk lehet, mintha egy sok processzoros gépen dolgoznánk, ami rendelkezik rengeteg memóriával. Ez így is van, de a hatékonyabb kihasználás érdekében az SSH-n keresztüli elérés nem lehetséges, csak a Slurm segítségével valamint a programozási nyelvek által támogatott csatornákon keresztül. Ennek részletezése lentebb megtalálható.&lt;br /&gt;
&lt;br /&gt;
==Használati módok==&lt;br /&gt;
A koprocesszor használatai módjait két csoportba lehet sorolni. A '''natív''' és az '''offload''' használatot.  &amp;lt;br/&amp;gt;&lt;br /&gt;
Ezen használati módokról a lenti fejezetekben több részletet is megtudhat. Az itt megfogalmazott &amp;quot;támogatás&amp;quot; csak a mi infrastruktúránkra érvényes! &amp;lt;br /&amp;gt;&lt;br /&gt;
A bemutatott példák ''c++'' nyelven vannak, de természetesen ''c'' és ''fortran'' esetén is megtalálható az ekvivalens módszer.&lt;br /&gt;
&lt;br /&gt;
===a) nem támogatott mód===&lt;br /&gt;
====közvetlen natív használat====&lt;br /&gt;
A natív mód arra szolgál, hogy a MIC kártyára  lefordított alkalmazásunkat felmásoljuk a kártyára, majd belépünk SSH-n a kártyára és azon futtatjuk a bináris kódunkat. &amp;lt;br /&amp;gt;&lt;br /&gt;
Ez a legegyszerűbb használati mód, ugyanis nincs más dolgunk, csak a '''-mmic''' kapcsolót használni a fordításnál és a fordító egy máris a MIC architektúrára kész bináris készít nekünk. &amp;lt;br/&amp;gt;&lt;br /&gt;
Workflow a következő lenne:&lt;br /&gt;
# Alkalmazás megírása. például [[Média:hello.cc|''hello.cc'']]&lt;br /&gt;
# Alkalmazás fordítása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;icpc -o hello-MIC -mmic hello.cc&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Másolás a MIC kártyára &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;scp hello-MIC mic0:&amp;lt;/pre&amp;gt;&lt;br /&gt;
# belépés a kártyára &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;ssh mic0&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Alkalmazás futtatása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;./hello-MIC&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===b) támogatott, de nem preferált használat===&lt;br /&gt;
====automatikus kódátvitel====&lt;br /&gt;
Ebben az esetben nem kell átmásolnunk a bináris alkalmazásunkat a kártyára sőt még csak be se kell lépnünk, mert ezt a két lépést egy beépített tool végzi el. &amp;lt;br/&amp;gt;&lt;br /&gt;
A tool eléréséhez szükséges parancs neve: '''micnativeloadex'''&lt;br /&gt;
&lt;br /&gt;
Ebben az esetben a workflow a következőképpen módosul:&lt;br /&gt;
# Alkalmazás megírása. például [[Média:hello.cc|''hello.cc'']]&lt;br /&gt;
# Alkalmazás fordítása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;icpc -o hello-MIC -mmic hello.cc&amp;lt;/pre&amp;gt;&lt;br /&gt;
# A tool futásához szükséges library-k megadása &amp;lt;br&amp;gt; &amp;lt;pre&amp;gt;export SINK_LD_LIBRARY_PATH=/opt/intel/composerxe/compiler/lib/mic&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Alkalmazás futtatása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;micnativeloadex hello-MIC&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| BUDAPEST2[cn10] phi (0)$ export SINK_LD_LIBRARY_PATH=/opt/intel/composerxe/compiler/lib/mic &amp;lt;br/&amp;gt;BUDAPEST2[cn10] phi (0)$ micnativeloadex hello-MIC &amp;lt;br/&amp;gt;Hello world! I have 244 logical cores.&amp;lt;br/&amp;gt;BUDAPEST2[cn10] phi (0)$&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===c) támogatott módok===&lt;br /&gt;
====Offload====&lt;br /&gt;
Az Offload modell esetén szükséges a forráskód módosítása, mégpedig úgy, hogy azon kódrészletet, amit a koprocesszoron szeretnénk futtatni, ki kell egészíteni egy fordítási direktívával. Az alkalmazás fordításakor elegendő csak a CPU-ra való binárist készíteni mivel a modell végzi a megfelelő forrás részlet mozgatását és futtatását.&amp;lt;br /&amp;gt;&lt;br /&gt;
[[Fájl:offload.png|Offload modell]] &amp;lt;br /&amp;gt;&lt;br /&gt;
Példánkban jól látható, hogy a második kiíratás már a koprocesszoron fut.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
int main(int argc, char * argv[]) {&lt;br /&gt;
  printf(&amp;quot;Hello world from host!\n&amp;quot;);&lt;br /&gt;
#pragma offload target(mic)&lt;br /&gt;
  {&lt;br /&gt;
    printf(&amp;quot;Hello world from coprocessor!\n&amp;quot;); fflush(stdout);&lt;br /&gt;
  }&lt;br /&gt;
  printf(&amp;quot;Bye\n&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ebben a példában az operációs rendszer választja ki, hogy melyik Phi kártyán fusson le a kódrészlet. Amennyiben mi szeretnénk explicite megmondani, hogy melyik kártyán fusson, akkor a fordítási direktívában a mic kulcsszó után kell tenni egy &amp;quot;:&amp;quot;-t és a kártya sorszámát [mi esetünkben 0 vagy 1]. Ekkor így nézne ki a direktíva: ''#pragma offload target(mic:1)''&lt;br /&gt;
&lt;br /&gt;
Amennyiben az offload szekcióban függvény hívás is szerepel, akkor szükségeltetik az függvény futásához szükséges változók adatterületének átmásolása a Phi kártyára, majd a lefutás után visszamásolni a hoszt gépre. Ezen másolást az ''offload_attribute'' ''push'' és ''pop'' paraméterei segítségével határozhatjuk meg. &amp;lt;br /&amp;gt;&lt;br /&gt;
A következő mátrix-vektor szorzás példánál az A mátrix globális változó, míg a b és c vektorok a függvény paraméterei.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//A változók felmásolásra kerülnek a Xeon Phi-re&lt;br /&gt;
#pragma offload_attribute(push, target(mic))&lt;br /&gt;
  double* A;&lt;br /&gt;
  void MatrixVector(double *b, double *c, double m, double n) {&lt;br /&gt;
    ...&lt;br /&gt;
    c[] = A[..]*b[]&lt;br /&gt;
  }&lt;br /&gt;
// felmásolt objektumok visszamásolása&lt;br /&gt;
#pragma offload_attribute(pop)&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
  ...&lt;br /&gt;
#pragma offload target(mic) in(A[0:n*m])&lt;br /&gt;
// így is meg lehetne adni: #pragma offload target(mic:0) in(A : length(n*m))	        &lt;br /&gt;
  {&lt;br /&gt;
    MatrixVector(b, c, m, n);&lt;br /&gt;
  }&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ekkor minden memória területet 2x kell másolni, egyszer fel a kártyára, majd másodszor vissza a hoszt gépre, pedig elegendő lenne csak az eredmény vektort visszamásolni. Ezen optimalizálással egy későbbi fejezetben foglalkozunk. &amp;lt;p /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Futás során kaphatunk részletes riportot is, hogy pontosan milyen adatmozgatások történnek az alkalmazás futása során. Ehhez nincs szükség másra csak az '''OFFLOAD_REPORT''' környezeti változó megfelelő beállítása. A beállított értékek 1, 2 vagy 3 lehetnek, attól függően mennyire részletes riportot szeretnénk kapni. &amp;lt;br /&amp;gt;&lt;br /&gt;
beállítás:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export OFFLOAD_REPORT=3&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
példa kimenet a hello alkalmazásra:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Hello world from host!&lt;br /&gt;
[Offload] [MIC 0] [File]                    hello_offload.cc&lt;br /&gt;
[Offload] [MIC 0] [Line]                    5&lt;br /&gt;
[Offload] [MIC 0] [Tag]                     Tag 0&lt;br /&gt;
Hello world from coprocessor!&lt;br /&gt;
[Offload] [HOST]  [Tag 0] [CPU Time]        0.554653(seconds)&lt;br /&gt;
[Offload] [MIC 0] [Tag 0] [CPU-&amp;gt;MIC Data]   0 (bytes)&lt;br /&gt;
[Offload] [MIC 0] [Tag 0] [MIC Time]        0.000140(seconds)&lt;br /&gt;
[Offload] [MIC 0] [Tag 0] [MIC-&amp;gt;CPU Data]   0 (bytes)&lt;br /&gt;
Bye&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Offload + OpenMP====&lt;br /&gt;
Amennyiben nem csak 1 processzort szeretnénk használni a Phi kártyánkon és az alkalmazásunkban használt számítás megengedi a párhuzamosítást, akkor az OpenMP segítségével párhuzamosíthatjuk a megfelelő számítást. &amp;lt;br /&amp;gt;&lt;br /&gt;
[[Fájl:Offload+openmp.png|Offload modell és OpenMP]] &amp;lt;br /&amp;gt;&lt;br /&gt;
Példánk maradjon ugyanúgy a mátrix-vektor szorzás. Amennyiben a teljes kártya adta kapacitást ki szeretnénk használni, akkor az OMP threadek számát 240-re kell beállítani. Azért nem 244-re, mivel 1 CPU-t dedikáltan csak a kommunikációra használ a rendszer és 1 CPU 4 hardver threaddel rendelkezik.&lt;br /&gt;
Ekkor a példánk így változik:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;omp.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#pragma offload_attribute(push, target(mic))&lt;br /&gt;
void multiply(int n, int m, double* A, double* b, double* c){&lt;br /&gt;
  const int nCPUs = omp_get_max_threads();   //240&lt;br /&gt;
  omp_set_num_threads(nCPUs);&lt;br /&gt;
#pragma omp parallel for schedule(dynamic, 1)&lt;br /&gt;
  for ( int i = 0 ; i &amp;lt; m ; i++)&lt;br /&gt;
     for ( int j = 0 ; j &amp;lt; n ; j++)&lt;br /&gt;
       c[i] += A[i*n+j] * b[j];&lt;br /&gt;
}&lt;br /&gt;
#pragma offload_attribute(pop)&lt;br /&gt;
&lt;br /&gt;
int main(){&lt;br /&gt;
  double * A = (double*) malloc(sizeof(double)*n*m);&lt;br /&gt;
  ...&lt;br /&gt;
#pragma offload target(mic) in(A:length(n*m))&lt;br /&gt;
     multiply(n, m, A, b, c);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Az alkalmazás fordításánál a fordítónak meg kell adni, hogy használhja az openmp library-ket is. Ezt a '''-qopenmp''' kapcsolóval tudjuk elérni.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
icpc -o matrix-CPU -qopenmp matrix.cc&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====MPI + Offload + OpenMP====&lt;br /&gt;
Abban az esetben, ha 1 számítási node nem elég, MPI segítségével össze tudunk kötni több nodet, heterogén rendszert alkotva, és azokon alkalmazható a fenti Offload + OpenMP modell. &amp;lt;br /&amp;gt;&lt;br /&gt;
[[Fájl:Mpi+offload+openmp.png|MPI elosztás valamint Offload modell és OpenMP]] &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Alkalmazások optimalizálása a kártyára==&lt;br /&gt;
Ebben a fejezetben optimalizálási lehetőségeket mutatunk be, melyek segítségül szolgálhatnak az egyes alkalmazások hatékonyabb futása érdekében ezzel jobban kihasználni a rendelkezésre álló erőforrások adta számítási kapacitás.&lt;br /&gt;
&lt;br /&gt;
===Környezeti változók===&lt;br /&gt;
Lehetséges csak a Phi kártyákon használt környezeti változók beállítása, amit az alkalmazásunk használni is tud. &amp;lt;br /&amp;gt;&lt;br /&gt;
Példánkban az OpenMP esetén használható szálak számát állíthatjuk be:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export MIC_ENV_PREFIX=XEONPHI&lt;br /&gt;
export OMP_NUM_THREADS=12&lt;br /&gt;
export XEONPHI_OMP_NUM_THREADS=240&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
A beállított prefix tetszőleges karaktersorozat lehet, a lényeg, hogy ezt a beállított prefixet használjuk a környezeti változók esetén is!&lt;br /&gt;
 &lt;br /&gt;
===Hosszredukció===&lt;br /&gt;
Többnyire a fordítók, ha nem mondjuk meg explicite, a dupla pontosságú változatot választják a lehetséges változó- és függvény típusok közül. Amennyiben a számításunk nem igényli a dupla pontosságot, akkor használjuk a forrásban mindenhol az adott típus vagy függvény egyszeres pontosságú változatát, mellyel jelentős teljesítmény növekedést tudunk elérni. Továbbá az egyes műveletek futási ideje nem egyforma, így a műveletet helyettesíthetjük más művelettel, ami gyorsabb, akkor ismét javíthatunk a hatékonyságon. &amp;lt;br /&amp;gt;&lt;br /&gt;
Íme pár példa, amit alkalmazni lehet:&lt;br /&gt;
* &amp;quot;''/''&amp;quot; művelet helyett &amp;quot;''*''&amp;quot; -t használjunk, mert azt gyorsabban végre tudja hajtani a processzor&lt;br /&gt;
* ''pow'' =&amp;gt; ''1.0 / sqrt()''    , tetszőleges hatványozás, pl.: 3/2-ik hatvány&lt;br /&gt;
* hardver áltatl támogatott műveletek:&lt;br /&gt;
** ''exp'' =&amp;gt; ''exp2''&lt;br /&gt;
** ''log'' =&amp;gt; ''log2''&lt;br /&gt;
* dupla pontosság -&amp;gt; egyszeres pontosság lebegőpontos számok és műveletek esetén &lt;br /&gt;
** ''1.0'' =&amp;gt; ''1.0f''&lt;br /&gt;
** ''pow'' =&amp;gt; ''powf''&lt;br /&gt;
&lt;br /&gt;
===Vektorizáció===&lt;br /&gt;
&lt;br /&gt;
====SIMD====&lt;br /&gt;
====AoS -&amp;gt; SoA====&lt;br /&gt;
====Hatékony hossz választás====&lt;br /&gt;
Olyan esetben jelent előnyt, ha például az n*n-es mátrix esetén n nem osztható 16-val, mert akkor a vektorizáció nem teljesen hajtható végre. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// n%16 != 0&lt;br /&gt;
float* A = _mm_malloc(sizeof(float)*n*n, 64);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Ilyen esetben mindig marad egy kis vektor részlet, amire elemenként kell elvégezni a műveletet.&amp;lt;br /&amp;gt;&lt;br /&gt;
A megoldást az jelenti, hogy a memória allokálást kiterjesztjük és nagyobb memória területet foglalunk a változónak, így már alkalmazható lesz a vektorizáció, mivel a maradvány területen is el tudjuk végezni a műveletet egy lépésben.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int lda=n;&lt;br /&gt;
if (n % 16 != 0) lda += (16 - n%16); // most lda%16==0&lt;br /&gt;
float* A = _mm_malloc(sizeof(float)*n*lda, 64);&lt;br /&gt;
&lt;br /&gt;
// használat&lt;br /&gt;
for (int i = 0; i &amp;lt; n; i++)&lt;br /&gt;
  for (int j = 0; j &amp;lt; n; j++)&lt;br /&gt;
    A[i*lda + j] = ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====&amp;quot;Héj ciklusok&amp;quot; kiküszöbölése====&lt;br /&gt;
Ciklusok esetén lehetséges, hogy a vektorizáció során&lt;br /&gt;
&lt;br /&gt;
====Regularizáció====&lt;br /&gt;
===Párhuzamosítás===&lt;br /&gt;
====Párhuzamos szálak ütemezése====&lt;br /&gt;
====Ciklus kifejtés====&lt;br /&gt;
====Automatikus ciklusösszevonás====&lt;/div&gt;</summary>
		<author><name>Szigeti(AT)niif.hu</name></author>	</entry>

	<entry>
		<id>https://wiki.niif.hu/index.php?title=Intel_Xeon_Phi&amp;diff=3737</id>
		<title>Intel Xeon Phi</title>
		<link rel="alternate" type="text/html" href="https://wiki.niif.hu/index.php?title=Intel_Xeon_Phi&amp;diff=3737"/>
				<updated>2016-05-03T11:49:48Z</updated>
		
		<summary type="html">&lt;p&gt;Szigeti(AT)niif.hu: /* &amp;quot;Héj ciklusok&amp;quot; kiköszöbölése */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Az oldalon található információk segítséget nyújtanak a HPC felhasználóinknak, hogy alkalmazásaikat miként portolhatják valamint optimalizálhatják a hatékonyabb futást szem előtt tartva Intel Xeon Phi koprocesszor kártyákon.&lt;br /&gt;
&lt;br /&gt;
==Hardver bemutatása==&lt;br /&gt;
Intel® Xeon Phi™ Coprocessor 7120P&lt;br /&gt;
&amp;lt;gallery&amp;gt;&lt;br /&gt;
Fájl:Xeon-phi-family-rwd.png.rendition.intel.web.720.405.png|&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-  align=&amp;quot;center&amp;quot;&lt;br /&gt;
! CPU&lt;br /&gt;
! threads/Core&lt;br /&gt;
!max threads&lt;br /&gt;
! CPU Freq&lt;br /&gt;
! Memória&lt;br /&gt;
! L1 cache&lt;br /&gt;
! L2 cache&lt;br /&gt;
! Interfész&lt;br /&gt;
! Ptot&lt;br /&gt;
! Számítási kapacitás&lt;br /&gt;
|-  align=&amp;quot;center&amp;quot;&lt;br /&gt;
| 61&lt;br /&gt;
| 4 Hardver&lt;br /&gt;
| 244&lt;br /&gt;
| 1238 GHz&lt;br /&gt;
| 16GB GDDR5&lt;br /&gt;
| 32KB/core&lt;br /&gt;
| 512KB/core&lt;br /&gt;
| PCIe&lt;br /&gt;
| 300W&lt;br /&gt;
| 1,2 TFlop/s dupla pontosság&lt;br /&gt;
|-  align=&amp;quot;center&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
| OpenMP-nél csak '''240'''&lt;br /&gt;
|&lt;br /&gt;
| ~100 cycles&lt;br /&gt;
~174 GB/s&lt;br /&gt;
| ~3 cycles&lt;br /&gt;
| ~10 cycles&lt;br /&gt;
| ~7 GB/s&lt;br /&gt;
~1 microsecond&lt;br /&gt;
|&lt;br /&gt;
| 2,4 TFlop/s egyszeres pontosság&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Az Intel® 1. generációs Xeon Phi™ koprocesszorában található 61 processzor ugyanazt a 16GB memóriát éri el, de semmilyen módon se fér hozzá közvetlenül a hoszt gép fizikai memóriájához.&amp;lt;br /&amp;gt;&lt;br /&gt;
[[Budapest2_klaszter|Budapest2]] és [[HPC#Szupersz.C3.A1m.C3.ADt.C3.B3g.C3.A9peink_.C3.B6sszehasonl.C3.ADt.C3.A1sa|Debrecen3-Phi]] gépeinkben számítási nodonként 2-2 Phi™ koprocesszor található. &amp;lt;br/&amp;gt;&lt;br /&gt;
A kártyákon speciális célú Linux kernel fut melyre a belépés limitáltan csak a rendszergazdák számára lehetséges SSH-n keresztül a hoszt gépről. Természetesen a kártyára belépve olyan érzésünk lehet, mintha egy sok processzoros gépen dolgoznánk, ami rendelkezik rengeteg memóriával. Ez így is van, de a hatékonyabb kihasználás érdekében az SSH-n keresztüli elérés nem lehetséges, csak a Slurm segítségével valamint a programozási nyelvek által támogatott csatornákon keresztül. Ennek részletezése lentebb megtalálható.&lt;br /&gt;
&lt;br /&gt;
==Használati módok==&lt;br /&gt;
A koprocesszor használatai módjait két csoportba lehet sorolni. A '''natív''' és az '''offload''' használatot.  &amp;lt;br/&amp;gt;&lt;br /&gt;
Ezen használati módokról a lenti fejezetekben több részletet is megtudhat. Az itt megfogalmazott &amp;quot;támogatás&amp;quot; csak a mi infrastruktúránkra érvényes! &amp;lt;br /&amp;gt;&lt;br /&gt;
A bemutatott példák ''c++'' nyelven vannak, de természetesen ''c'' és ''fortran'' esetén is megtalálható az ekvivalens módszer.&lt;br /&gt;
&lt;br /&gt;
===a) nem támogatott mód===&lt;br /&gt;
====közvetlen natív használat====&lt;br /&gt;
A natív mód arra szolgál, hogy a MIC kártyára  lefordított alkalmazásunkat felmásoljuk a kártyára, majd belépünk SSH-n a kártyára és azon futtatjuk a bináris kódunkat. &amp;lt;br /&amp;gt;&lt;br /&gt;
Ez a legegyszerűbb használati mód, ugyanis nincs más dolgunk, csak a '''-mmic''' kapcsolót használni a fordításnál és a fordító egy máris a MIC architektúrára kész bináris készít nekünk. &amp;lt;br/&amp;gt;&lt;br /&gt;
Workflow a következő lenne:&lt;br /&gt;
# Alkalmazás megírása. például [[Média:hello.cc|''hello.cc'']]&lt;br /&gt;
# Alkalmazás fordítása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;icpc -o hello-MIC -mmic hello.cc&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Másolás a MIC kártyára &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;scp hello-MIC mic0:&amp;lt;/pre&amp;gt;&lt;br /&gt;
# belépés a kártyára &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;ssh mic0&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Alkalmazás futtatása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;./hello-MIC&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===b) támogatott, de nem preferált használat===&lt;br /&gt;
====automatikus kódátvitel====&lt;br /&gt;
Ebben az esetben nem kell átmásolnunk a bináris alkalmazásunkat a kártyára sőt még csak be se kell lépnünk, mert ezt a két lépést egy beépített tool végzi el. &amp;lt;br/&amp;gt;&lt;br /&gt;
A tool eléréséhez szükséges parancs neve: '''micnativeloadex'''&lt;br /&gt;
&lt;br /&gt;
Ebben az esetben a workflow a következőképpen módosul:&lt;br /&gt;
# Alkalmazás megírása. például [[Média:hello.cc|''hello.cc'']]&lt;br /&gt;
# Alkalmazás fordítása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;icpc -o hello-MIC -mmic hello.cc&amp;lt;/pre&amp;gt;&lt;br /&gt;
# A tool futásához szükséges library-k megadása &amp;lt;br&amp;gt; &amp;lt;pre&amp;gt;export SINK_LD_LIBRARY_PATH=/opt/intel/composerxe/compiler/lib/mic&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Alkalmazás futtatása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;micnativeloadex hello-MIC&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| BUDAPEST2[cn10] phi (0)$ export SINK_LD_LIBRARY_PATH=/opt/intel/composerxe/compiler/lib/mic &amp;lt;br/&amp;gt;BUDAPEST2[cn10] phi (0)$ micnativeloadex hello-MIC &amp;lt;br/&amp;gt;Hello world! I have 244 logical cores.&amp;lt;br/&amp;gt;BUDAPEST2[cn10] phi (0)$&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===c) támogatott módok===&lt;br /&gt;
====Offload====&lt;br /&gt;
Az Offload modell esetén szükséges a forráskód módosítása, mégpedig úgy, hogy azon kódrészletet, amit a koprocesszoron szeretnénk futtatni, ki kell egészíteni egy fordítási direktívával. Az alkalmazás fordításakor elegendő csak a CPU-ra való binárist készíteni mivel a modell végzi a megfelelő forrás részlet mozgatását és futtatását.&amp;lt;br /&amp;gt;&lt;br /&gt;
[[Fájl:offload.png|Offload modell]] &amp;lt;br /&amp;gt;&lt;br /&gt;
Példánkban jól látható, hogy a második kiíratás már a koprocesszoron fut.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
int main(int argc, char * argv[]) {&lt;br /&gt;
  printf(&amp;quot;Hello world from host!\n&amp;quot;);&lt;br /&gt;
#pragma offload target(mic)&lt;br /&gt;
  {&lt;br /&gt;
    printf(&amp;quot;Hello world from coprocessor!\n&amp;quot;); fflush(stdout);&lt;br /&gt;
  }&lt;br /&gt;
  printf(&amp;quot;Bye\n&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ebben a példában az operációs rendszer választja ki, hogy melyik Phi kártyán fusson le a kódrészlet. Amennyiben mi szeretnénk explicite megmondani, hogy melyik kártyán fusson, akkor a fordítási direktívában a mic kulcsszó után kell tenni egy &amp;quot;:&amp;quot;-t és a kártya sorszámát [mi esetünkben 0 vagy 1]. Ekkor így nézne ki a direktíva: ''#pragma offload target(mic:1)''&lt;br /&gt;
&lt;br /&gt;
Amennyiben az offload szekcióban függvény hívás is szerepel, akkor szükségeltetik az függvény futásához szükséges változók adatterületének átmásolása a Phi kártyára, majd a lefutás után visszamásolni a hoszt gépre. Ezen másolást az ''offload_attribute'' ''push'' és ''pop'' paraméterei segítségével határozhatjuk meg. &amp;lt;br /&amp;gt;&lt;br /&gt;
A következő mátrix-vektor szorzás példánál az A mátrix globális változó, míg a b és c vektorok a függvény paraméterei.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//A változók felmásolásra kerülnek a Xeon Phi-re&lt;br /&gt;
#pragma offload_attribute(push, target(mic))&lt;br /&gt;
  double* A;&lt;br /&gt;
  void MatrixVector(double *b, double *c, double m, double n) {&lt;br /&gt;
    ...&lt;br /&gt;
    c[] = A[..]*b[]&lt;br /&gt;
  }&lt;br /&gt;
// felmásolt objektumok visszamásolása&lt;br /&gt;
#pragma offload_attribute(pop)&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
  ...&lt;br /&gt;
#pragma offload target(mic) in(A[0:n*m])&lt;br /&gt;
// így is meg lehetne adni: #pragma offload target(mic:0) in(A : length(n*m))	        &lt;br /&gt;
  {&lt;br /&gt;
    MatrixVector(b, c, m, n);&lt;br /&gt;
  }&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ekkor minden memória területet 2x kell másolni, egyszer fel a kártyára, majd másodszor vissza a hoszt gépre, pedig elegendő lenne csak az eredmény vektort visszamásolni. Ezen optimalizálással egy későbbi fejezetben foglalkozunk. &amp;lt;p /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Futás során kaphatunk részletes riportot is, hogy pontosan milyen adatmozgatások történnek az alkalmazás futása során. Ehhez nincs szükség másra csak az '''OFFLOAD_REPORT''' környezeti változó megfelelő beállítása. A beállított értékek 1, 2 vagy 3 lehetnek, attól függően mennyire részletes riportot szeretnénk kapni. &amp;lt;br /&amp;gt;&lt;br /&gt;
beállítás:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export OFFLOAD_REPORT=3&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
példa kimenet a hello alkalmazásra:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Hello world from host!&lt;br /&gt;
[Offload] [MIC 0] [File]                    hello_offload.cc&lt;br /&gt;
[Offload] [MIC 0] [Line]                    5&lt;br /&gt;
[Offload] [MIC 0] [Tag]                     Tag 0&lt;br /&gt;
Hello world from coprocessor!&lt;br /&gt;
[Offload] [HOST]  [Tag 0] [CPU Time]        0.554653(seconds)&lt;br /&gt;
[Offload] [MIC 0] [Tag 0] [CPU-&amp;gt;MIC Data]   0 (bytes)&lt;br /&gt;
[Offload] [MIC 0] [Tag 0] [MIC Time]        0.000140(seconds)&lt;br /&gt;
[Offload] [MIC 0] [Tag 0] [MIC-&amp;gt;CPU Data]   0 (bytes)&lt;br /&gt;
Bye&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Offload + OpenMP====&lt;br /&gt;
Amennyiben nem csak 1 processzort szeretnénk használni a Phi kártyánkon és az alkalmazásunkban használt számítás megengedi a párhuzamosítást, akkor az OpenMP segítségével párhuzamosíthatjuk a megfelelő számítást. &amp;lt;br /&amp;gt;&lt;br /&gt;
[[Fájl:Offload+openmp.png|Offload modell és OpenMP]] &amp;lt;br /&amp;gt;&lt;br /&gt;
Példánk maradjon ugyanúgy a mátrix-vektor szorzás. Amennyiben a teljes kártya adta kapacitást ki szeretnénk használni, akkor az OMP threadek számát 240-re kell beállítani. Azért nem 244-re, mivel 1 CPU-t dedikáltan csak a kommunikációra használ a rendszer és 1 CPU 4 hardver threaddel rendelkezik.&lt;br /&gt;
Ekkor a példánk így változik:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;omp.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#pragma offload_attribute(push, target(mic))&lt;br /&gt;
void multiply(int n, int m, double* A, double* b, double* c){&lt;br /&gt;
  const int nCPUs = omp_get_max_threads();   //240&lt;br /&gt;
  omp_set_num_threads(nCPUs);&lt;br /&gt;
#pragma omp parallel for schedule(dynamic, 1)&lt;br /&gt;
  for ( int i = 0 ; i &amp;lt; m ; i++)&lt;br /&gt;
     for ( int j = 0 ; j &amp;lt; n ; j++)&lt;br /&gt;
       c[i] += A[i*n+j] * b[j];&lt;br /&gt;
}&lt;br /&gt;
#pragma offload_attribute(pop)&lt;br /&gt;
&lt;br /&gt;
int main(){&lt;br /&gt;
  double * A = (double*) malloc(sizeof(double)*n*m);&lt;br /&gt;
  ...&lt;br /&gt;
#pragma offload target(mic) in(A:length(n*m))&lt;br /&gt;
     multiply(n, m, A, b, c);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Az alkalmazás fordításánál a fordítónak meg kell adni, hogy használhja az openmp library-ket is. Ezt a '''-qopenmp''' kapcsolóval tudjuk elérni.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
icpc -o matrix-CPU -qopenmp matrix.cc&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====MPI + Offload + OpenMP====&lt;br /&gt;
Abban az esetben, ha 1 számítási node nem elég, MPI segítségével össze tudunk kötni több nodet, heterogén rendszert alkotva, és azokon alkalmazható a fenti Offload + OpenMP modell. &amp;lt;br /&amp;gt;&lt;br /&gt;
[[Fájl:Mpi+offload+openmp.png|MPI elosztás valamint Offload modell és OpenMP]] &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Alkalmazások optimalizálása a kártyára==&lt;br /&gt;
Ebben a fejezetben optimalizálási lehetőségeket mutatunk be, melyek segítségül szolgálhatnak az egyes alkalmazások hatékonyabb futása érdekében ezzel jobban kihasználni a rendelkezésre álló erőforrások adta számítási kapacitás.&lt;br /&gt;
&lt;br /&gt;
===Környezeti változók===&lt;br /&gt;
Lehetséges csak a Phi kártyákon használt környezeti változók beállítása, amit az alkalmazásunk használni is tud. &amp;lt;br /&amp;gt;&lt;br /&gt;
Példánkban az OpenMP esetén használható szálak számát állíthatjuk be:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export MIC_ENV_PREFIX=XEONPHI&lt;br /&gt;
export OMP_NUM_THREADS=12&lt;br /&gt;
export XEONPHI_OMP_NUM_THREADS=240&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
A beállított prefix tetszőleges karaktersorozat lehet, a lényeg, hogy ezt a beállított prefixet használjuk a környezeti változók esetén is!&lt;br /&gt;
 &lt;br /&gt;
===Hosszredukció===&lt;br /&gt;
Többnyire a fordítók, ha nem mondjuk meg explicite, a dupla pontosságú változatot választják a lehetséges változó- és függvény típusok közül. Amennyiben a számításunk nem igényli a dupla pontosságot, akkor használjuk a forrásban mindenhol az adott típus vagy függvény egyszeres pontosságú változatát, mellyel jelentős teljesítmény növekedést tudunk elérni. Továbbá az egyes műveletek futási ideje nem egyforma, így a műveletet helyettesíthetjük más művelettel, ami gyorsabb, akkor ismét javíthatunk a hatékonyságon. &amp;lt;br /&amp;gt;&lt;br /&gt;
Íme pár példa, amit alkalmazni lehet:&lt;br /&gt;
* &amp;quot;''/''&amp;quot; művelet helyett &amp;quot;''*''&amp;quot; -t használjunk, mert azt gyorsabban végre tudja hajtani a processzor&lt;br /&gt;
* ''pow'' =&amp;gt; ''1.0 / sqrt()''    , tetszőleges hatványozás, pl.: 3/2-ik hatvány&lt;br /&gt;
* hardver áltatl támogatott műveletek:&lt;br /&gt;
** ''exp'' =&amp;gt; ''exp2''&lt;br /&gt;
** ''log'' =&amp;gt; ''log2''&lt;br /&gt;
* dupla pontosság -&amp;gt; egyszeres pontosság lebegőpontos számok és műveletek esetén &lt;br /&gt;
** ''1.0'' =&amp;gt; ''1.0f''&lt;br /&gt;
** ''pow'' =&amp;gt; ''powf''&lt;br /&gt;
&lt;br /&gt;
===Vektorizáció===&lt;br /&gt;
&lt;br /&gt;
====SIMD====&lt;br /&gt;
====AoS -&amp;gt; SoA====&lt;br /&gt;
====Hatékony hossz választás====&lt;br /&gt;
Olyan esetben jelent előnyt, ha például az n*n-es mátrix esetén n nem osztható 16-val, mert akkor a vektorizáció nem teljesen hajtható végre. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// n%16 != 0&lt;br /&gt;
float* A = _mm_malloc(sizeof(float)*n*n, 64);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Ilyen esetben mindig marad egy kis vektor részlet, amire elemenként kell elvégezni a műveletet.&amp;lt;br /&amp;gt;&lt;br /&gt;
A megoldást az jelenti, hogy a memória allokálást kiterjesztjük és nagyobb memória területet foglalunk a változónak, így már alkalmazható lesz a vektorizáció, mivel a maradvány területen is el tudjuk végezni a műveletet egy lépésben.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int lda=n;&lt;br /&gt;
if (n % 16 != 0) lda += (16 - n%16); // most lda%16==0&lt;br /&gt;
float* A = _mm_malloc(sizeof(float)*n*lda, 64);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====&amp;quot;Héj ciklusok&amp;quot; kiküszöbölése====&lt;br /&gt;
Ciklusok esetén lehetséges, hogy a vektorizáció során&lt;br /&gt;
&lt;br /&gt;
====Regularizáció====&lt;br /&gt;
===Párhuzamosítás===&lt;br /&gt;
====Párhuzamos szálak ütemezése====&lt;br /&gt;
====Ciklus kifejtés====&lt;br /&gt;
====Automatikus ciklusösszevonás====&lt;/div&gt;</summary>
		<author><name>Szigeti(AT)niif.hu</name></author>	</entry>

	<entry>
		<id>https://wiki.niif.hu/index.php?title=Intel_Xeon_Phi&amp;diff=3736</id>
		<title>Intel Xeon Phi</title>
		<link rel="alternate" type="text/html" href="https://wiki.niif.hu/index.php?title=Intel_Xeon_Phi&amp;diff=3736"/>
				<updated>2016-05-03T11:40:09Z</updated>
		
		<summary type="html">&lt;p&gt;Szigeti(AT)niif.hu: /* Hatékony hossz választás */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Az oldalon található információk segítséget nyújtanak a HPC felhasználóinknak, hogy alkalmazásaikat miként portolhatják valamint optimalizálhatják a hatékonyabb futást szem előtt tartva Intel Xeon Phi koprocesszor kártyákon.&lt;br /&gt;
&lt;br /&gt;
==Hardver bemutatása==&lt;br /&gt;
Intel® Xeon Phi™ Coprocessor 7120P&lt;br /&gt;
&amp;lt;gallery&amp;gt;&lt;br /&gt;
Fájl:Xeon-phi-family-rwd.png.rendition.intel.web.720.405.png|&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-  align=&amp;quot;center&amp;quot;&lt;br /&gt;
! CPU&lt;br /&gt;
! threads/Core&lt;br /&gt;
!max threads&lt;br /&gt;
! CPU Freq&lt;br /&gt;
! Memória&lt;br /&gt;
! L1 cache&lt;br /&gt;
! L2 cache&lt;br /&gt;
! Interfész&lt;br /&gt;
! Ptot&lt;br /&gt;
! Számítási kapacitás&lt;br /&gt;
|-  align=&amp;quot;center&amp;quot;&lt;br /&gt;
| 61&lt;br /&gt;
| 4 Hardver&lt;br /&gt;
| 244&lt;br /&gt;
| 1238 GHz&lt;br /&gt;
| 16GB GDDR5&lt;br /&gt;
| 32KB/core&lt;br /&gt;
| 512KB/core&lt;br /&gt;
| PCIe&lt;br /&gt;
| 300W&lt;br /&gt;
| 1,2 TFlop/s dupla pontosság&lt;br /&gt;
|-  align=&amp;quot;center&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
| OpenMP-nél csak '''240'''&lt;br /&gt;
|&lt;br /&gt;
| ~100 cycles&lt;br /&gt;
~174 GB/s&lt;br /&gt;
| ~3 cycles&lt;br /&gt;
| ~10 cycles&lt;br /&gt;
| ~7 GB/s&lt;br /&gt;
~1 microsecond&lt;br /&gt;
|&lt;br /&gt;
| 2,4 TFlop/s egyszeres pontosság&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Az Intel® 1. generációs Xeon Phi™ koprocesszorában található 61 processzor ugyanazt a 16GB memóriát éri el, de semmilyen módon se fér hozzá közvetlenül a hoszt gép fizikai memóriájához.&amp;lt;br /&amp;gt;&lt;br /&gt;
[[Budapest2_klaszter|Budapest2]] és [[HPC#Szupersz.C3.A1m.C3.ADt.C3.B3g.C3.A9peink_.C3.B6sszehasonl.C3.ADt.C3.A1sa|Debrecen3-Phi]] gépeinkben számítási nodonként 2-2 Phi™ koprocesszor található. &amp;lt;br/&amp;gt;&lt;br /&gt;
A kártyákon speciális célú Linux kernel fut melyre a belépés limitáltan csak a rendszergazdák számára lehetséges SSH-n keresztül a hoszt gépről. Természetesen a kártyára belépve olyan érzésünk lehet, mintha egy sok processzoros gépen dolgoznánk, ami rendelkezik rengeteg memóriával. Ez így is van, de a hatékonyabb kihasználás érdekében az SSH-n keresztüli elérés nem lehetséges, csak a Slurm segítségével valamint a programozási nyelvek által támogatott csatornákon keresztül. Ennek részletezése lentebb megtalálható.&lt;br /&gt;
&lt;br /&gt;
==Használati módok==&lt;br /&gt;
A koprocesszor használatai módjait két csoportba lehet sorolni. A '''natív''' és az '''offload''' használatot.  &amp;lt;br/&amp;gt;&lt;br /&gt;
Ezen használati módokról a lenti fejezetekben több részletet is megtudhat. Az itt megfogalmazott &amp;quot;támogatás&amp;quot; csak a mi infrastruktúránkra érvényes! &amp;lt;br /&amp;gt;&lt;br /&gt;
A bemutatott példák ''c++'' nyelven vannak, de természetesen ''c'' és ''fortran'' esetén is megtalálható az ekvivalens módszer.&lt;br /&gt;
&lt;br /&gt;
===a) nem támogatott mód===&lt;br /&gt;
====közvetlen natív használat====&lt;br /&gt;
A natív mód arra szolgál, hogy a MIC kártyára  lefordított alkalmazásunkat felmásoljuk a kártyára, majd belépünk SSH-n a kártyára és azon futtatjuk a bináris kódunkat. &amp;lt;br /&amp;gt;&lt;br /&gt;
Ez a legegyszerűbb használati mód, ugyanis nincs más dolgunk, csak a '''-mmic''' kapcsolót használni a fordításnál és a fordító egy máris a MIC architektúrára kész bináris készít nekünk. &amp;lt;br/&amp;gt;&lt;br /&gt;
Workflow a következő lenne:&lt;br /&gt;
# Alkalmazás megírása. például [[Média:hello.cc|''hello.cc'']]&lt;br /&gt;
# Alkalmazás fordítása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;icpc -o hello-MIC -mmic hello.cc&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Másolás a MIC kártyára &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;scp hello-MIC mic0:&amp;lt;/pre&amp;gt;&lt;br /&gt;
# belépés a kártyára &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;ssh mic0&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Alkalmazás futtatása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;./hello-MIC&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===b) támogatott, de nem preferált használat===&lt;br /&gt;
====automatikus kódátvitel====&lt;br /&gt;
Ebben az esetben nem kell átmásolnunk a bináris alkalmazásunkat a kártyára sőt még csak be se kell lépnünk, mert ezt a két lépést egy beépített tool végzi el. &amp;lt;br/&amp;gt;&lt;br /&gt;
A tool eléréséhez szükséges parancs neve: '''micnativeloadex'''&lt;br /&gt;
&lt;br /&gt;
Ebben az esetben a workflow a következőképpen módosul:&lt;br /&gt;
# Alkalmazás megírása. például [[Média:hello.cc|''hello.cc'']]&lt;br /&gt;
# Alkalmazás fordítása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;icpc -o hello-MIC -mmic hello.cc&amp;lt;/pre&amp;gt;&lt;br /&gt;
# A tool futásához szükséges library-k megadása &amp;lt;br&amp;gt; &amp;lt;pre&amp;gt;export SINK_LD_LIBRARY_PATH=/opt/intel/composerxe/compiler/lib/mic&amp;lt;/pre&amp;gt;&lt;br /&gt;
# Alkalmazás futtatása &amp;lt;br/&amp;gt; &amp;lt;pre&amp;gt;micnativeloadex hello-MIC&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| BUDAPEST2[cn10] phi (0)$ export SINK_LD_LIBRARY_PATH=/opt/intel/composerxe/compiler/lib/mic &amp;lt;br/&amp;gt;BUDAPEST2[cn10] phi (0)$ micnativeloadex hello-MIC &amp;lt;br/&amp;gt;Hello world! I have 244 logical cores.&amp;lt;br/&amp;gt;BUDAPEST2[cn10] phi (0)$&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===c) támogatott módok===&lt;br /&gt;
====Offload====&lt;br /&gt;
Az Offload modell esetén szükséges a forráskód módosítása, mégpedig úgy, hogy azon kódrészletet, amit a koprocesszoron szeretnénk futtatni, ki kell egészíteni egy fordítási direktívával. Az alkalmazás fordításakor elegendő csak a CPU-ra való binárist készíteni mivel a modell végzi a megfelelő forrás részlet mozgatását és futtatását.&amp;lt;br /&amp;gt;&lt;br /&gt;
[[Fájl:offload.png|Offload modell]] &amp;lt;br /&amp;gt;&lt;br /&gt;
Példánkban jól látható, hogy a második kiíratás már a koprocesszoron fut.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
int main(int argc, char * argv[]) {&lt;br /&gt;
  printf(&amp;quot;Hello world from host!\n&amp;quot;);&lt;br /&gt;
#pragma offload target(mic)&lt;br /&gt;
  {&lt;br /&gt;
    printf(&amp;quot;Hello world from coprocessor!\n&amp;quot;); fflush(stdout);&lt;br /&gt;
  }&lt;br /&gt;
  printf(&amp;quot;Bye\n&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ebben a példában az operációs rendszer választja ki, hogy melyik Phi kártyán fusson le a kódrészlet. Amennyiben mi szeretnénk explicite megmondani, hogy melyik kártyán fusson, akkor a fordítási direktívában a mic kulcsszó után kell tenni egy &amp;quot;:&amp;quot;-t és a kártya sorszámát [mi esetünkben 0 vagy 1]. Ekkor így nézne ki a direktíva: ''#pragma offload target(mic:1)''&lt;br /&gt;
&lt;br /&gt;
Amennyiben az offload szekcióban függvény hívás is szerepel, akkor szükségeltetik az függvény futásához szükséges változók adatterületének átmásolása a Phi kártyára, majd a lefutás után visszamásolni a hoszt gépre. Ezen másolást az ''offload_attribute'' ''push'' és ''pop'' paraméterei segítségével határozhatjuk meg. &amp;lt;br /&amp;gt;&lt;br /&gt;
A következő mátrix-vektor szorzás példánál az A mátrix globális változó, míg a b és c vektorok a függvény paraméterei.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//A változók felmásolásra kerülnek a Xeon Phi-re&lt;br /&gt;
#pragma offload_attribute(push, target(mic))&lt;br /&gt;
  double* A;&lt;br /&gt;
  void MatrixVector(double *b, double *c, double m, double n) {&lt;br /&gt;
    ...&lt;br /&gt;
    c[] = A[..]*b[]&lt;br /&gt;
  }&lt;br /&gt;
// felmásolt objektumok visszamásolása&lt;br /&gt;
#pragma offload_attribute(pop)&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
  ...&lt;br /&gt;
#pragma offload target(mic) in(A[0:n*m])&lt;br /&gt;
// így is meg lehetne adni: #pragma offload target(mic:0) in(A : length(n*m))	        &lt;br /&gt;
  {&lt;br /&gt;
    MatrixVector(b, c, m, n);&lt;br /&gt;
  }&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ekkor minden memória területet 2x kell másolni, egyszer fel a kártyára, majd másodszor vissza a hoszt gépre, pedig elegendő lenne csak az eredmény vektort visszamásolni. Ezen optimalizálással egy későbbi fejezetben foglalkozunk. &amp;lt;p /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Futás során kaphatunk részletes riportot is, hogy pontosan milyen adatmozgatások történnek az alkalmazás futása során. Ehhez nincs szükség másra csak az '''OFFLOAD_REPORT''' környezeti változó megfelelő beállítása. A beállított értékek 1, 2 vagy 3 lehetnek, attól függően mennyire részletes riportot szeretnénk kapni. &amp;lt;br /&amp;gt;&lt;br /&gt;
beállítás:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export OFFLOAD_REPORT=3&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
példa kimenet a hello alkalmazásra:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Hello world from host!&lt;br /&gt;
[Offload] [MIC 0] [File]                    hello_offload.cc&lt;br /&gt;
[Offload] [MIC 0] [Line]                    5&lt;br /&gt;
[Offload] [MIC 0] [Tag]                     Tag 0&lt;br /&gt;
Hello world from coprocessor!&lt;br /&gt;
[Offload] [HOST]  [Tag 0] [CPU Time]        0.554653(seconds)&lt;br /&gt;
[Offload] [MIC 0] [Tag 0] [CPU-&amp;gt;MIC Data]   0 (bytes)&lt;br /&gt;
[Offload] [MIC 0] [Tag 0] [MIC Time]        0.000140(seconds)&lt;br /&gt;
[Offload] [MIC 0] [Tag 0] [MIC-&amp;gt;CPU Data]   0 (bytes)&lt;br /&gt;
Bye&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Offload + OpenMP====&lt;br /&gt;
Amennyiben nem csak 1 processzort szeretnénk használni a Phi kártyánkon és az alkalmazásunkban használt számítás megengedi a párhuzamosítást, akkor az OpenMP segítségével párhuzamosíthatjuk a megfelelő számítást. &amp;lt;br /&amp;gt;&lt;br /&gt;
[[Fájl:Offload+openmp.png|Offload modell és OpenMP]] &amp;lt;br /&amp;gt;&lt;br /&gt;
Példánk maradjon ugyanúgy a mátrix-vektor szorzás. Amennyiben a teljes kártya adta kapacitást ki szeretnénk használni, akkor az OMP threadek számát 240-re kell beállítani. Azért nem 244-re, mivel 1 CPU-t dedikáltan csak a kommunikációra használ a rendszer és 1 CPU 4 hardver threaddel rendelkezik.&lt;br /&gt;
Ekkor a példánk így változik:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#include &amp;lt;omp.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#pragma offload_attribute(push, target(mic))&lt;br /&gt;
void multiply(int n, int m, double* A, double* b, double* c){&lt;br /&gt;
  const int nCPUs = omp_get_max_threads();   //240&lt;br /&gt;
  omp_set_num_threads(nCPUs);&lt;br /&gt;
#pragma omp parallel for schedule(dynamic, 1)&lt;br /&gt;
  for ( int i = 0 ; i &amp;lt; m ; i++)&lt;br /&gt;
     for ( int j = 0 ; j &amp;lt; n ; j++)&lt;br /&gt;
       c[i] += A[i*n+j] * b[j];&lt;br /&gt;
}&lt;br /&gt;
#pragma offload_attribute(pop)&lt;br /&gt;
&lt;br /&gt;
int main(){&lt;br /&gt;
  double * A = (double*) malloc(sizeof(double)*n*m);&lt;br /&gt;
  ...&lt;br /&gt;
#pragma offload target(mic) in(A:length(n*m))&lt;br /&gt;
     multiply(n, m, A, b, c);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Az alkalmazás fordításánál a fordítónak meg kell adni, hogy használhja az openmp library-ket is. Ezt a '''-qopenmp''' kapcsolóval tudjuk elérni.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
icpc -o matrix-CPU -qopenmp matrix.cc&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====MPI + Offload + OpenMP====&lt;br /&gt;
Abban az esetben, ha 1 számítási node nem elég, MPI segítségével össze tudunk kötni több nodet, heterogén rendszert alkotva, és azokon alkalmazható a fenti Offload + OpenMP modell. &amp;lt;br /&amp;gt;&lt;br /&gt;
[[Fájl:Mpi+offload+openmp.png|MPI elosztás valamint Offload modell és OpenMP]] &amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Alkalmazások optimalizálása a kártyára==&lt;br /&gt;
Ebben a fejezetben optimalizálási lehetőségeket mutatunk be, melyek segítségül szolgálhatnak az egyes alkalmazások hatékonyabb futása érdekében ezzel jobban kihasználni a rendelkezésre álló erőforrások adta számítási kapacitás.&lt;br /&gt;
&lt;br /&gt;
===Környezeti változók===&lt;br /&gt;
Lehetséges csak a Phi kártyákon használt környezeti változók beállítása, amit az alkalmazásunk használni is tud. &amp;lt;br /&amp;gt;&lt;br /&gt;
Példánkban az OpenMP esetén használható szálak számát állíthatjuk be:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export MIC_ENV_PREFIX=XEONPHI&lt;br /&gt;
export OMP_NUM_THREADS=12&lt;br /&gt;
export XEONPHI_OMP_NUM_THREADS=240&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
A beállított prefix tetszőleges karaktersorozat lehet, a lényeg, hogy ezt a beállított prefixet használjuk a környezeti változók esetén is!&lt;br /&gt;
 &lt;br /&gt;
===Hosszredukció===&lt;br /&gt;
Többnyire a fordítók, ha nem mondjuk meg explicite, a dupla pontosságú változatot választják a lehetséges változó- és függvény típusok közül. Amennyiben a számításunk nem igényli a dupla pontosságot, akkor használjuk a forrásban mindenhol az adott típus vagy függvény egyszeres pontosságú változatát, mellyel jelentős teljesítmény növekedést tudunk elérni. Továbbá az egyes műveletek futási ideje nem egyforma, így a műveletet helyettesíthetjük más művelettel, ami gyorsabb, akkor ismét javíthatunk a hatékonyságon. &amp;lt;br /&amp;gt;&lt;br /&gt;
Íme pár példa, amit alkalmazni lehet:&lt;br /&gt;
* &amp;quot;''/''&amp;quot; művelet helyett &amp;quot;''*''&amp;quot; -t használjunk, mert azt gyorsabban végre tudja hajtani a processzor&lt;br /&gt;
* ''pow'' =&amp;gt; ''1.0 / sqrt()''    , tetszőleges hatványozás, pl.: 3/2-ik hatvány&lt;br /&gt;
* hardver áltatl támogatott műveletek:&lt;br /&gt;
** ''exp'' =&amp;gt; ''exp2''&lt;br /&gt;
** ''log'' =&amp;gt; ''log2''&lt;br /&gt;
* dupla pontosság -&amp;gt; egyszeres pontosság lebegőpontos számok és műveletek esetén &lt;br /&gt;
** ''1.0'' =&amp;gt; ''1.0f''&lt;br /&gt;
** ''pow'' =&amp;gt; ''powf''&lt;br /&gt;
&lt;br /&gt;
===Vektorizáció===&lt;br /&gt;
&lt;br /&gt;
====SIMD====&lt;br /&gt;
====AoS -&amp;gt; SoA====&lt;br /&gt;
====Hatékony hossz választás====&lt;br /&gt;
Olyan esetben jelent előnyt, ha például az n*n-es mátrix esetén n nem osztható 16-val, mert akkor a vektorizáció nem teljesen hajtható végre. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// n%16 != 0&lt;br /&gt;
float* A = _mm_malloc(sizeof(float)*n*n, 64);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Ilyen esetben mindig marad egy kis vektor részlet, amire elemenként kell elvégezni a műveletet.&amp;lt;br /&amp;gt;&lt;br /&gt;
A megoldást az jelenti, hogy a memória allokálást kiterjesztjük és nagyobb memória területet foglalunk a változónak, így már alkalmazható lesz a vektorizáció, mivel a maradvány területen is el tudjuk végezni a műveletet egy lépésben.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int lda=n;&lt;br /&gt;
if (n % 16 != 0) lda += (16 - n%16); // most lda%16==0&lt;br /&gt;
float* A = _mm_malloc(sizeof(float)*n*lda, 64);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====&amp;quot;Héj ciklusok&amp;quot; kiköszöbölése====&lt;br /&gt;
====Regularizáció====&lt;br /&gt;
===Párhuzamosítás===&lt;br /&gt;
====Párhuzamos szálak ütemezése====&lt;br /&gt;
====Ciklus kifejtés====&lt;br /&gt;
====Automatikus ciklusösszevonás====&lt;/div&gt;</summary>
		<author><name>Szigeti(AT)niif.hu</name></author>	</entry>

	</feed>