J'étudie comment exécuter un processus sur une CPU dédiée afin d'éviter les changements de contexte. Sur mon Ubuntu, j'ai isolé deux CPU en utilisant les paramètres du noyau "isolcpus = 3,7" et "irqaffinity = 0-2,4-6". Je suis sûr qu'il est correctement pris en compte:Impossible d'éviter les changements de contexte sur un processus lancé seul sur un CPU
$ cat /proc/cmdline
BOOT_IMAGE=/boot/vmlinuz-4.8.0-27-generic root=UUID=58c66f12-0588-442b-9bb8-1d2dd833efe2 ro quiet splash isolcpus=3,7 irqaffinity=0-2,4-6 vt.handoff=7
Après un redémarrage, je peux vérifier que tout fonctionne comme prévu. Sur une première console je lance
$ stress -c 24
stress: info: [31717] dispatching hogs: 24 cpu, 0 io, 0 vm, 0 hdd
Et un second, en utilisant « top » Je peux vérifier l'utilisation de mes processeurs:
top - 18:39:07 up 2 days, 20:48, 18 users, load average: 23,15, 10,46, 4,53
Tasks: 457 total, 26 running, 431 sleeping, 0 stopped, 0 zombie
%Cpu0 :100,0 us, 0,0 sy, 0,0 ni, 0,0 id, 0,0 wa, 0,0 hi, 0,0 si, 0,0 st
%Cpu1 : 98,7 us, 1,3 sy, 0,0 ni, 0,0 id, 0,0 wa, 0,0 hi, 0,0 si, 0,0 st
%Cpu2 : 99,3 us, 0,7 sy, 0,0 ni, 0,0 id, 0,0 wa, 0,0 hi, 0,0 si, 0,0 st
%Cpu3 : 0,0 us, 0,0 sy, 0,0 ni,100,0 id, 0,0 wa, 0,0 hi, 0,0 si, 0,0 st
%Cpu4 : 95,7 us, 4,3 sy, 0,0 ni, 0,0 id, 0,0 wa, 0,0 hi, 0,0 si, 0,0 st
%Cpu5 : 98,0 us, 2,0 sy, 0,0 ni, 0,0 id, 0,0 wa, 0,0 hi, 0,0 si, 0,0 st
%Cpu6 : 98,7 us, 1,3 sy, 0,0 ni, 0,0 id, 0,0 wa, 0,0 hi, 0,0 si, 0,0 st
%Cpu7 : 0,0 us, 0,0 sy, 0,0 ni,100,0 id, 0,0 wa, 0,0 hi, 0,0 si, 0,0 st
KiB Mem : 7855176 total, 385736 free, 5891280 used, 1578160 buff/cache
KiB Swap: 15624188 total, 10414520 free, 5209668 used. 626872 avail Mem
processeurs 3 et 7 sont gratuits tandis que les 6 autres les sont complètement occupés. Bien.
Pour le reste de mon test, je vais utiliser une petite application qui fait le traitement presque pur
- Il utilise deux tampons int de la même taille
- Il lit un par -one toutes les valeurs de la première mémoire tampon
- chaque valeur est un indice aléatoire dans la seconde mémoire tampon
- Il lit la valeur à l'index dans le second tampon
- Il résume toutes les valeurs prises à partir du second tampon
- Il fait toutes les étapes précédentes pour de plus en plus
- A la fin, j'imprimer le nombre de contexte CPU volontaire et involontaire commutateurs
Je suis en train d'étudier ma demande quand je le lance:
- sur une CPU
- non isolé sur un CPU isolé
je fais par l'intermédiaire des lignes de commande suivantes:
$ ./TestCpuset ### launch on any non-isolated CPU
$ taskset -c 7 ./TestCpuset ### launch on isolated CPU 7
lors de son lancement sur un processeur, le nombre de changements de contexte changement de 20 à ... milliers
En lancement sur une CPU isolée, le nombre de switchs de contexte est quasi constant (entre 10 et 20), même si je lance en parallèle un "stress -c 24". (semble tout à fait normal)
Mais ma question est: pourquoi n'est-ce pas 0 absolument 0? Quand un changement est fait sur un processus, c'est pour le remplacer par un autre processus? Mais dans mon cas, il n'y a pas d'autre processus à remplacer!
J'ai une hypothèse qui est que l'option « isolcpus » isolerait CPU former tout processus (à moins que le processus d'une affinité CPU serait donné, comme ce qui se fait avec « taskset ») mais pas de tâches du noyau. Cependant, je trouve aucune documentation à ce sujet
Je vous serais reconnaissant toute aide pour atteindre 0 contexte commutateurs
Pour votre information, cette question est fermée à l'autre j'ai ouvert précédemment: Cannot allocate exclusively a CPU for my process
Voici le code du programme que je me sers:
#include <limits.h>
#include <iostream>
#include <unistd.h>
#include <sys/time.h>
#include <sys/resource.h>
const unsigned int BUFFER_SIZE = 4096;
using namespace std;
class TimedSumComputer
{
public:
TimedSumComputer() :
sum(0),
bufferSize(0),
valueBuffer(0),
indexBuffer(0)
{}
public:
virtual ~TimedSumComputer()
{
resetBuffers();
}
public:
void init(unsigned int bufferSize)
{
this->bufferSize = bufferSize;
resetBuffers();
initValueBuffer();
initIndexBuffer();
}
private:
void resetBuffers()
{
delete [] valueBuffer;
delete [] indexBuffer;
valueBuffer = 0;
indexBuffer = 0;
}
void initValueBuffer()
{
valueBuffer = new unsigned int[bufferSize];
for (unsigned int i = 0 ; i < bufferSize ; i++)
{
valueBuffer[i] = randomUint();
}
}
static unsigned int randomUint()
{
int value = rand() % UINT_MAX;
return value;
}
protected:
void initIndexBuffer()
{
indexBuffer = new unsigned int[bufferSize];
for (unsigned int i = 0 ; i < bufferSize ; i++)
{
indexBuffer[i] = rand() % bufferSize;
}
}
public:
unsigned int getSum() const
{
return sum;
}
unsigned int computeTimeInMicroSeconds()
{
struct timeval startTime, endTime;
gettimeofday(&startTime, NULL);
unsigned int sum = computeSum();
gettimeofday(&endTime, NULL);
return ((endTime.tv_sec - startTime.tv_sec) * 1000 * 1000) + (endTime.tv_usec - startTime.tv_usec);
}
unsigned int computeSum()
{
sum = 0;
for (unsigned int i = 0 ; i < bufferSize ; i++)
{
unsigned int index = indexBuffer[i];
sum += valueBuffer[index];
}
return sum;
}
protected:
unsigned int sum;
unsigned int bufferSize;
unsigned int * valueBuffer;
unsigned int * indexBuffer;
};
unsigned int runTestForBufferSize(TimedSumComputer & timedComputer, unsigned int bufferSize)
{
timedComputer.init(bufferSize);
unsigned int timeInMicroSec = timedComputer.computeTimeInMicroSeconds();
cout << "bufferSize = " << bufferSize << " - time (in micro-sec) = " << timeInMicroSec << endl;
return timedComputer.getSum();
}
void runTest(TimedSumComputer & timedComputer)
{
unsigned int result = 0;
for (unsigned int i = 1 ; i < 10 ; i++)
{
result += runTestForBufferSize(timedComputer, BUFFER_SIZE * i);
}
unsigned int factor = 1;
for (unsigned int i = 2 ; i <= 6 ; i++)
{
factor *= 10;
result += runTestForBufferSize(timedComputer, BUFFER_SIZE * factor);
}
cout << "result = " << result << endl;
}
void printPid()
{
cout << "###############################" << endl;
cout << "Pid = " << getpid() << endl;
cout << "###############################" << endl;
}
void printNbContextSwitch()
{
struct rusage usage;
getrusage(RUSAGE_THREAD, &usage);
cout << "Number of voluntary context switch: " << usage.ru_nvcsw << endl;
cout << "Number of involuntary context switch: " << usage.ru_nivcsw << endl;
}
int main()
{
printPid();
TimedSumComputer timedComputer;
runTest(timedComputer);
printNbContextSwitch();
return 0;
}
D'où proviennent vos données? Utilisez-vous plus de mémoire que votre machine a physiquement? Je m'attendrais à ce que l'accès à une section paginée de la mémoire va forcer une augmentation dans le compteur de changement de contexte quand le processus est suspendu en attendant l'opération de pagination. –
Le programme que j'utilise est juste un simple programme de test, il accède uniquement aux tampons qui sont initialisés avec des valeurs aléatoires (voir la fonction rand()) –