我正在研究如何在专用CPU上运行进程,以避免上下文切换。在我的Ubuntu上,我使用内核参数"isolcpus=3,7“和"irqaffinity=0-2,4-6”分离出两个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重新启动后,我可以检查一切是否正常。在第一个控制台上运行
$ stress -c 24
stress: info: [31717] dispatching hogs: 24 cpu, 0 io, 0 vm, 0 hdd在第二个例子中,我可以使用"top“检查CPU的使用情况:
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 CPU 3和7是免费的,而其他6个CPU则完全繁忙。很好。
在剩下的测试中,我将使用一个几乎是纯处理的小应用程序。
我现在正在研究我的申请,当我启动它时:
我通过以下命令行执行此操作:
$ ./TestCpuset ### launch on any non-isolated CPU
$ taskset -c 7 ./TestCpuset ### launch on isolated CPU 7在任何CPU上启动时,上下文开关的数目从20变为.千人
当在孤立的CPU上启动时,上下文开关的数量几乎是恒定的(介于10到20之间),即使我并行启动一个“压力-c 24”(看起来很正常)。
但我的问题是:为什么不是0,绝对是0?当对一个进程进行切换时,它是为了用另一个进程来替换它吗?但在我的情况下,没有其他的过程可以取代!
我有一个假设,即"isolcpus“选项会将CPU与任何进程隔离开来(除非进程具有CPU亲缘关系,例如使用”任务集“所做的事情),而不是从内核任务中分离出来。然而,我没有发现任何关于它的文件。
为了达到0上下文切换,我将非常感谢您的任何帮助。
这个问题不适用于我以前打开的另一个问题:https://stackoverflow.com/questions/40692637/cannot-allocate-exclusively-a-cpu-for-my-process。
下面是我正在使用的程序的代码:
#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;
}发布于 2016-11-27 08:29:24
今天,我获得了更多关于我的问题的线索--我意识到我必须深入研究内核调度程序中发生的事情。我发现这两页:
在应用程序这样运行时,我启用了调度程序跟踪:
# sudo bash
# cd /sys/kernel/debug/tracing
# echo 1 > options/function-trace ; echo function_graph > current_tracer ; echo 1 > tracing_on ; echo 0 > tracing_max_latency ; taskset -c 7 [path-to-my-program]/TestCpuset ; echo 0 > tracing_on
# cat trace由于我的程序是在CPU 7(任务集-c 7)上启动的,所以我必须过滤“跟踪”输出。
# grep " 7)" trace然后,我可以搜索从一个过程到另一个过程的转换:
# grep " 7)" trace | grep "=>"
...
7) TestCpu-4753 => kworker-5866
7) kworker-5866 => TestCpu-4753
7) TestCpu-4753 => watchdo-26
7) watchdo-26 => TestCpu-4753
7) TestCpu-4753 => kworker-5866
7) kworker-5866 => TestCpu-4753
7) TestCpu-4753 => kworker-5866
7) kworker-5866 => TestCpu-4753
7) TestCpu-4753 => kworker-5866
7) kworker-5866 => TestCpu-4753
...对啰!我跟踪的上下文开关似乎是过渡到:
我现在必须找到:
当然,我再一次感谢你的帮助:
发布于 2016-11-27 00:03:04
任何syscall都可能涉及上下文切换。当您访问分页内存时,它可能也会增加上下文切换计数。要达到0上下文开关,您需要强制内核将您的程序使用的所有内存都映射到其地址空间,并且您需要确保您调用的任何一个系统都不需要上下文切换。我相信这在RT补丁内核上是可能的,但在标准发行版内核上可能很难实现。
发布于 2017-10-18 11:21:13
为了让那些通过google (像我一样)找到这一点的人,/sys/devices/virtual/workqueue/cpumask控制内核可能与WORK_CPU_UNBOUND排队的位置(不关心哪个cpu)。在编写此答案时,它不会设置为与isolcpus默认操作相同的掩码。
一旦我将它更改为不包括我的独立cpus,我就看到了大量的上下文切换到我的关键线程(但不是零)。我假设在我的独立cpus上运行的工作必须是特定的请求,例如使用schedule_on_each_cpu。
https://stackoverflow.com/questions/40774217
复制相似问题