I’m writing a small program that uses a certain percentage of CPU. The basic strategy is that I will continuously check the CPU usage, and make the process sleep if the level of usage is higher than the given value.
Moreover, since I’m using MacOS(no proc/stat like Linux, no PerformanceCounter in C#), I have to execute top command in another thread and get the CPU usage from it.
The problem is I keep getting a very high usage of CPU even I give a small value as argument. And after several experiments, it seems caused by shared field by multithreads.
Here are my code(code 1) and experiments:
(code 2)Initially I thought it is the shell commands make the usage very high, so I commented the infinite loop in run(), leaving only the getCpuUsage() running. However, the CPU usage is nearly zero.
(code 3)Then, I wrote another run() function independent from the cpuUsage, which is intended to use 50% of CPU. It works well! I think the only difference between code 1 and code 3 is the usage of cpuUsage. So I’m wondering if sharing field between threads will use CPU a lot?
code 1
const char CPU_COMMAND[] = "top -stats cpu -l 1 -n 0| grep CPU\\ usage | cut -c 12-15";
int cpuUsage; // shared field that stores the cpu usage
// thread that continuously check CPU usage
// and store it in cpuUsage
void getCpuUsage() {
char usage[3];
FILE *out;
while (1) {
out = popen(CPU_COMMAND, "r");
if (fgets(usage, 3, out) != NULL) {
cpuUsage = atof(usage);
} else {
cpuUsage = 0;
}
pclose(out);
}
}
// keep the CPU usage under ratio
void run(int ratio) {
pthread_t id;
int ret = pthread_create(&id, NULL, (void *)getCpuUsage, NULL);
if (ret!=0) printf("thread error!");
while (1) {
// if current cpu usage is higher than ration, make it asleep
if (cpuUsage > ratio) {
usleep(10);
}
}
pthread_join(id, NULL);
}
code 2
// keep the CPU usage under ratio
void run(int ratio) {
pthread_t id;
int ret = pthread_create(&id, NULL, (void *)getCpuUsage, NULL);
if (ret!=0) printf("thread error!");
/*while (1) {
// if current cpu usage is higher than ration, make it asleep
if (cpuUsage > ratio) {
usleep(10);
}
}*/
pthread_join(id, NULL);
}
code 3
void run() {
const clock_t busyTime = 10;
const clock_t idleTime = busyTime;
while (1) {
clock_t startTime = clock();
while (clock() - startTime <= busyTime);
usleep(idleTime);
}
}
Yes, constant reads/writes to/from a shared memory locations, by multiple threads on multiple CPUs cause a cache line to constantly move between CPUs (cache bounce). IMO, it’s the single most important reason for poor scalability in naive “parallel” applications.