ฉันถามคำถามก่อนหน้านี้เพื่อลองและแยกแหล่งที่มาของการเพิ่มขึ้นของการใช้งาน CPU เมื่อย้ายแอปพลิเคชันจาก RHEL 5 ไปยัง RHEL 6 การวิเคราะห์ที่ฉันทำเพื่อดูเหมือนจะบ่งชี้ว่าเกิดจาก CFS ในเคอร์เนล ผมเขียนแอพลิเคชันทดสอบและพยายามตรวจสอบว่านี้คือกรณี (การประยุกต์ใช้การทดสอบเดิมลบออกให้พอดีกับขนาด จำกัด แต่ยังคงมีอยู่ในrepo คอมไพล์
ฉันรวบรวมมันด้วยคำสั่งต่อไปนี้ใน RHEL 5:
cc test_select_work.c -O2 -DSLEEP_TYPE=0 -Wall -Wextra -lm -lpthread -o test_select_work
จากนั้นฉันก็เล่นกับพารามิเตอร์จนกระทั่งเวลาดำเนินการต่อการทำซ้ำประมาณ 1 ms ใน Dell Precision m6500
ฉันได้ผลลัพธ์ต่อไปนี้ใน RHEL 5:
./test_select_work 1000 10000 300 4
time_per_iteration: min: 911.5 us avg: 913.7 us max: 917.1 us stddev: 2.4 us
./test_select_work 1000 10000 300 8
time_per_iteration: min: 1802.6 us avg: 1803.9 us max: 1809.1 us stddev: 2.1 us
./test_select_work 1000 10000 300 40
time_per_iteration: min: 7580.4 us avg: 8567.3 us max: 9022.0 us stddev: 299.6 us
และต่อไปนี้ใน RHEL 6:
./test_select_work 1000 10000 300 4
time_per_iteration: min: 914.6 us avg: 975.7 us max: 1034.5 us stddev: 50.0 us
./test_select_work 1000 10000 300 8
time_per_iteration: min: 1683.9 us avg: 1771.8 us max: 1810.8 us stddev: 43.4 us
./test_select_work 1000 10000 300 40
time_per_iteration: min: 7997.1 us avg: 8709.1 us max: 9061.8 us stddev: 310.0 us
ในทั้งสองเวอร์ชันผลลัพธ์เหล่านี้เกี่ยวกับสิ่งที่ฉันคาดหวังกับจำนวนเวลาเฉลี่ยต่อการทำซ้ำโดยประมาณจะค่อนข้างเป็นเส้นตรง จากนั้นฉันก็คอมไพล์ใหม่-DSLEEP_TYPE=1
และได้ผลลัพธ์ต่อไปนี้บน RHEL 5:
./test_select_work 1000 10000 300 4
time_per_iteration: min: 1803.3 us avg: 1902.8 us max: 2001.5 us stddev: 113.8 us
./test_select_work 1000 10000 300 8
time_per_iteration: min: 1997.1 us avg: 2002.0 us max: 2010.8 us stddev: 5.0 us
./test_select_work 1000 10000 300 40
time_per_iteration: min: 6958.4 us avg: 8397.9 us max: 9423.7 us stddev: 619.7 us
และผลลัพธ์ต่อไปนี้บน RHEL 6:
./test_select_work 1000 10000 300 4
time_per_iteration: min: 2107.1 us avg: 2143.1 us max: 2177.7 us stddev: 30.3 us
./test_select_work 1000 10000 300 8
time_per_iteration: min: 2903.3 us avg: 2903.8 us max: 2904.3 us stddev: 0.3 us
./test_select_work 1000 10000 300 40
time_per_iteration: min: 8877.7.1 us avg: 9016.3 us max: 9112.6 us stddev: 62.9 us
ใน RHEL 5 ผลลัพธ์นั้นเกี่ยวกับสิ่งที่ฉันคาดไว้ (4 เธรดใช้เวลานานสองเท่าเนื่องจากการนอนหลับ 1 มิลลิวินาที แต่ 8 เธรดใช้เวลาเท่ากันเนื่องจากเธรดแต่ละชุดนั้นนอนประมาณครึ่งเวลาและยังค่อนข้างยุติธรรม เพิ่มเชิงเส้น)
อย่างไรก็ตามด้วย RHEL 6 เวลาที่ใช้กับ 4 เธรดเพิ่มขึ้นประมาณ 15% มากกว่าที่คาดไว้สองเท่าและเคส 8 เธรดเพิ่มขึ้นประมาณ 45% สูงกว่าการเพิ่มขึ้นเล็กน้อยที่คาดไว้เล็กน้อย การเพิ่มขึ้นของเธรด 4 เธรดดูเหมือนจะเป็นไปได้ว่า RHEL 6 กำลังนอนหลับอยู่นานหนึ่งไมโครวินาทีมากกว่า 1 มิลลิวินาทีในขณะที่ RHEL 5 นอนเพียง 900 เราเท่านั้น แต่นี่ไม่ได้อธิบายการเพิ่มขึ้นอย่างกะทันหันของ 8 และ 40 กรณีด้าย
ฉันเห็นประเภทพฤติกรรมที่คล้ายกันซึ่งมีค่า -DSLEEP_TYPE ทั้งหมด 3 ค่า ฉันยังลองเล่นกับพารามิเตอร์ตัวกำหนดตารางเวลาใน sysctl แต่ดูเหมือนว่าไม่มีอะไรที่จะส่งผลกระทบอย่างมีนัยสำคัญต่อผลลัพธ์ มีแนวคิดใดบ้างที่ฉันสามารถวินิจฉัยปัญหานี้เพิ่มเติม
ปรับปรุง: 2012-05-07
ฉันเพิ่มการวัดการใช้งาน CPU ของผู้ใช้และระบบจาก / proc / stat // task // stat เป็นผลลัพธ์ของการทดสอบเพื่อลองและรับการสังเกตอีกจุดหนึ่ง ฉันยังพบปัญหาเกี่ยวกับวิธีการปรับปรุงค่าเฉลี่ยและส่วนเบี่ยงเบนมาตรฐานที่ได้รับการแนะนำเมื่อฉันเพิ่มลูปการวนซ้ำรอบนอกดังนั้นฉันจะเพิ่มพล็อตใหม่ที่มีการวัดค่าเฉลี่ยและส่วนเบี่ยงเบนมาตรฐานที่ถูกต้อง ฉันได้รวมโปรแกรมที่อัปเดตแล้ว ฉันยังทำ repo คอมไพล์เพื่อติดตามรหัสและมันมีให้ที่นี่
#include <limits.h>
#include <math.h>
#include <poll.h>
#include <pthread.h>
#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include <sys/select.h>
#include <sys/syscall.h>
#include <sys/time.h>
// Apparently GLIBC doesn't provide a wrapper for this function so provide it here
#ifndef HAS_GETTID
pid_t gettid(void)
{
return syscall(SYS_gettid);
}
#endif
// The different type of sleep that are supported
enum sleep_type {
SLEEP_TYPE_NONE,
SLEEP_TYPE_SELECT,
SLEEP_TYPE_POLL,
SLEEP_TYPE_USLEEP,
SLEEP_TYPE_YIELD,
SLEEP_TYPE_PTHREAD_COND,
SLEEP_TYPE_NANOSLEEP,
};
// Information returned by the processing thread
struct thread_res {
long long clock;
long long user;
long long sys;
};
// Function type for doing work with a sleep
typedef struct thread_res *(*work_func)(const int pid, const int sleep_time, const int num_iterations, const int work_size);
// Information passed to the thread
struct thread_info {
pid_t pid;
int sleep_time;
int num_iterations;
int work_size;
work_func func;
};
inline void get_thread_times(pid_t pid, pid_t tid, unsigned long long *utime, unsigned long long *stime)
{
char filename[FILENAME_MAX];
FILE *f;
sprintf(filename, "/proc/%d/task/%d/stat", pid, tid);
f = fopen(filename, "r");
if (f == NULL) {
*utime = 0;
*stime = 0;
return;
}
fscanf(f, "%*d %*s %*c %*d %*d %*d %*d %*d %*u %*u %*u %*u %*u %Lu %Lu", utime, stime);
fclose(f);
}
// In order to make SLEEP_TYPE a run-time parameter function pointers are used.
// The function pointer could have been to the sleep function being used, but
// then that would mean an extra function call inside of the "work loop" and I
// wanted to keep the measurements as tight as possible and the extra work being
// done to be as small/controlled as possible so instead the work is declared as
// a seriees of macros that are called in all of the sleep functions. The code
// is a bit uglier this way, but I believe it results in a more accurate test.
// Fill in a buffer with random numbers (taken from latt.c by Jens Axboe <jens.axboe@oracle.com>)
#define DECLARE_FUNC(NAME) struct thread_res *do_work_##NAME(const int pid, const int sleep_time, const int num_iterations, const int work_size)
#define DECLARE_WORK() \
int *buf; \
int pseed; \
int inum, bnum; \
pid_t tid; \
struct timeval clock_before, clock_after; \
unsigned long long user_before, user_after; \
unsigned long long sys_before, sys_after; \
struct thread_res *diff; \
tid = gettid(); \
buf = malloc(work_size * sizeof(*buf)); \
diff = malloc(sizeof(*diff)); \
get_thread_times(pid, tid, &user_before, &sys_before); \
gettimeofday(&clock_before, NULL)
#define DO_WORK(SLEEP_FUNC) \
for (inum=0; inum<num_iterations; ++inum) { \
SLEEP_FUNC \
\
pseed = 1; \
for (bnum=0; bnum<work_size; ++bnum) { \
pseed = pseed * 1103515245 + 12345; \
buf[bnum] = (pseed / 65536) % 32768; \
} \
} \
#define FINISH_WORK() \
gettimeofday(&clock_after, NULL); \
get_thread_times(pid, tid, &user_after, &sys_after); \
diff->clock = 1000000LL * (clock_after.tv_sec - clock_before.tv_sec); \
diff->clock += clock_after.tv_usec - clock_before.tv_usec; \
diff->user = user_after - user_before; \
diff->sys = sys_after - sys_before; \
free(buf); \
return diff
DECLARE_FUNC(nosleep)
{
DECLARE_WORK();
// Let the compiler know that sleep_time isn't used in this function
(void)sleep_time;
DO_WORK();
FINISH_WORK();
}
DECLARE_FUNC(select)
{
struct timeval ts;
DECLARE_WORK();
DO_WORK(
ts.tv_sec = 0;
ts.tv_usec = sleep_time;
select(0, 0, 0, 0, &ts);
);
FINISH_WORK();
}
DECLARE_FUNC(poll)
{
struct pollfd pfd;
const int sleep_time_ms = sleep_time / 1000;
DECLARE_WORK();
pfd.fd = 0;
pfd.events = 0;
DO_WORK(
poll(&pfd, 1, sleep_time_ms);
);
FINISH_WORK();
}
DECLARE_FUNC(usleep)
{
DECLARE_WORK();
DO_WORK(
usleep(sleep_time);
);
FINISH_WORK();
}
DECLARE_FUNC(yield)
{
DECLARE_WORK();
// Let the compiler know that sleep_time isn't used in this function
(void)sleep_time;
DO_WORK(
sched_yield();
);
FINISH_WORK();
}
DECLARE_FUNC(pthread_cond)
{
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
struct timespec ts;
const int sleep_time_ns = sleep_time * 1000;
DECLARE_WORK();
pthread_mutex_lock(&mutex);
DO_WORK(
clock_gettime(CLOCK_REALTIME, &ts);
ts.tv_nsec += sleep_time_ns;
if (ts.tv_nsec >= 1000000000) {
ts.tv_sec += 1;
ts.tv_nsec -= 1000000000;
}
pthread_cond_timedwait(&cond, &mutex, &ts);
);
pthread_mutex_unlock(&mutex);
pthread_cond_destroy(&cond);
pthread_mutex_destroy(&mutex);
FINISH_WORK();
}
DECLARE_FUNC(nanosleep)
{
struct timespec req, rem;
const int sleep_time_ns = sleep_time * 1000;
DECLARE_WORK();
DO_WORK(
req.tv_sec = 0;
req.tv_nsec = sleep_time_ns;
nanosleep(&req, &rem);
);
FINISH_WORK();
}
void *do_test(void *arg)
{
const struct thread_info *tinfo = (struct thread_info *)arg;
// Call the function to do the work
return (*tinfo->func)(tinfo->pid, tinfo->sleep_time, tinfo->num_iterations, tinfo->work_size);
}
struct thread_res_stats {
double min;
double max;
double avg;
double stddev;
double prev_avg;
};
#ifdef LLONG_MAX
#define THREAD_RES_STATS_INITIALIZER {LLONG_MAX, LLONG_MIN, 0, 0, 0}
#else
#define THREAD_RES_STATS_INITIALIZER {LONG_MAX, LONG_MIN, 0, 0, 0}
#endif
void update_stats(struct thread_res_stats *stats, long long value, int num_samples, int num_iterations, double scale_to_usecs)
{
// Calculate the average time per iteration
double value_per_iteration = value * scale_to_usecs / num_iterations;
// Update the max and min
if (value_per_iteration < stats->min)
stats->min = value_per_iteration;
if (value_per_iteration > stats->max)
stats->max = value_per_iteration;
// Update the average
stats->avg += (value_per_iteration - stats->avg) / (double)(num_samples);
// Update the standard deviation
stats->stddev += (value_per_iteration - stats->prev_avg) * (value_per_iteration - stats->avg);
// And record the current average for use in the next update
stats->prev_avg= stats->avg;
}
void print_stats(const char *name, const struct thread_res_stats *stats)
{
printf("%s: min: %.1f us avg: %.1f us max: %.1f us stddev: %.1f us\n",
name,
stats->min,
stats->avg,
stats->max,
stats->stddev);
}
int main(int argc, char **argv)
{
if (argc <= 6) {
printf("Usage: %s <sleep_time> <outer_iterations> <inner_iterations> <work_size> <num_threads> <sleep_type>\n", argv[0]);
printf(" outer_iterations: Number of iterations for each thread (used to calculate statistics)\n");
printf(" inner_iterations: Number of work/sleep cycles performed in each thread (used to improve consistency/observability))\n");
printf(" work_size: Number of array elements (in kb) that are filled with psuedo-random numbers\n");
printf(" num_threads: Number of threads to spawn and perform work/sleep cycles in\n");
printf(" sleep_type: 0=none 1=select 2=poll 3=usleep 4=yield 5=pthread_cond 6=nanosleep\n");
return -1;
}
struct thread_info tinfo;
int outer_iterations;
int sleep_type;
int s, inum, tnum, num_samples, num_threads;
pthread_attr_t attr;
pthread_t *threads;
struct thread_res *res;
struct thread_res **times;
// Track the stats for each of the measurements
struct thread_res_stats stats_clock = THREAD_RES_STATS_INITIALIZER;
struct thread_res_stats stats_user = THREAD_RES_STATS_INITIALIZER;
struct thread_res_stats stats_sys = THREAD_RES_STATS_INITIALIZER;
// Calculate the conversion factor from clock_t to seconds
const long clocks_per_sec = sysconf(_SC_CLK_TCK);
const double clocks_to_usec = 1000000 / (double)clocks_per_sec;
// Get the parameters
tinfo.pid = getpid();
tinfo.sleep_time = atoi(argv[1]);
outer_iterations = atoi(argv[2]);
tinfo.num_iterations = atoi(argv[3]);
tinfo.work_size = atoi(argv[4]) * 1024;
num_threads = atoi(argv[5]);
sleep_type = atoi(argv[6]);
switch (sleep_type) {
case SLEEP_TYPE_NONE: tinfo.func = &do_work_nosleep; break;
case SLEEP_TYPE_SELECT: tinfo.func = &do_work_select; break;
case SLEEP_TYPE_POLL: tinfo.func = &do_work_poll; break;
case SLEEP_TYPE_USLEEP: tinfo.func = &do_work_usleep; break;
case SLEEP_TYPE_YIELD: tinfo.func = &do_work_yield; break;
case SLEEP_TYPE_PTHREAD_COND: tinfo.func = &do_work_pthread_cond; break;
case SLEEP_TYPE_NANOSLEEP: tinfo.func = &do_work_nanosleep; break;
default:
printf("Invalid sleep type: %d\n", sleep_type);
return -7;
}
// Initialize the thread creation attributes
s = pthread_attr_init(&attr);
if (s != 0) {
printf("Error initializing thread attributes\n");
return -2;
}
// Allocate the memory to track the threads
threads = calloc(num_threads, sizeof(*threads));
times = calloc(num_threads, sizeof(*times));
if (threads == NULL) {
printf("Error allocating memory to track threads\n");
return -3;
}
// Initialize the number of samples
num_samples = 0;
// Perform the requested number of outer iterations
for (inum=0; inum<outer_iterations; ++inum) {
// Start all of the threads
for (tnum=0; tnum<num_threads; ++tnum) {
s = pthread_create(&threads[tnum], &attr, &do_test, &tinfo);
if (s != 0) {
printf("Error starting thread\n");
return -4;
}
}
// Wait for all the threads to finish
for (tnum=0; tnum<num_threads; ++tnum) {
s = pthread_join(threads[tnum], (void **)(&res));
if (s != 0) {
printf("Error waiting for thread\n");
return -6;
}
// Save the result for processing when they're all done
times[tnum] = res;
}
// For each of the threads
for (tnum=0; tnum<num_threads; ++tnum) {
// Increment the number of samples in the statistics
++num_samples;
// Update the statistics with this measurement
update_stats(&stats_clock, times[tnum]->clock, num_samples, tinfo.num_iterations, 1);
update_stats(&stats_user, times[tnum]->user, num_samples, tinfo.num_iterations, clocks_to_usec);
update_stats(&stats_sys, times[tnum]->sys, num_samples, tinfo.num_iterations, clocks_to_usec);
// And clean it up
free(times[tnum]);
}
}
// Clean up the thread creation attributes
s = pthread_attr_destroy(&attr);
if (s != 0) {
printf("Error cleaning up thread attributes\n");
return -5;
}
// Finish the calculation of the standard deviation
stats_clock.stddev = sqrtf(stats_clock.stddev / (num_samples - 1));
stats_user.stddev = sqrtf(stats_user.stddev / (num_samples - 1));
stats_sys.stddev = sqrtf(stats_sys.stddev / (num_samples - 1));
// Print out the statistics of the times
print_stats("gettimeofday_per_iteration", &stats_clock);
print_stats("utime_per_iteration", &stats_user);
print_stats("stime_per_iteration", &stats_sys);
// Clean up the allocated threads and times
free(threads);
free(times);
return 0;
}
ฉันทำการทดสอบบน Dell Vostro 200 (ดูอัลคอร์ซีพียู) พร้อมกับระบบปฏิบัติการหลายเวอร์ชัน ฉันรู้ว่าหลายสิ่งเหล่านี้จะมีการใช้แพตช์ที่แตกต่างกันและจะไม่ "รหัสเคอร์เนลบริสุทธิ์" แต่นี่เป็นวิธีที่ง่ายที่สุดที่ฉันสามารถทำการทดสอบกับเคอร์เนลเวอร์ชันต่าง ๆ และรับการเปรียบเทียบ ฉันสร้างแปลงด้วย gnuplot และได้รวมรุ่นจากbugzilla เกี่ยวกับปัญหานี้
การทดสอบทั้งหมดเหล่านี้ทำงานด้วยคำสั่งต่อไปนี้พร้อมกับสคริปต์ต่อไปนี้และคำสั่ง./run_test 1000 10 1000 250 8 6 <os_name>
นี้
#!/bin/bash
if [ $# -ne 7 ]; then
echo "Usage: `basename $0` <sleep_time> <outer_iterations> <inner_iterations> <work_size> <max_num_threads> <max_sleep_type> <test_name>"
echo " max_num_threads: The highest value used for num_threads in the results"
echo " max_sleep_type: The highest value used for sleep_type in the results"
echo " test_name: The name of the directory where the results will be stored"
exit -1
fi
sleep_time=$1
outer_iterations=$2
inner_iterations=$3
work_size=$4
max_num_threads=$5
max_sleep_type=$6
test_name=$7
# Make sure this results directory doesn't already exist
if [ -e $test_name ]; then
echo "$test_name already exists";
exit -1;
fi
# Create the directory to put the results in
mkdir $test_name
# Run through the requested number of SLEEP_TYPE values
for i in $(seq 0 $max_sleep_type)
do
# Run through the requested number of threads
for j in $(seq 1 $max_num_threads)
do
# Print which settings are about to be run
echo "sleep_type: $i num_threads: $j"
# Run the test and save it to the results file
./test_sleep $sleep_time $outer_iterations $inner_iterations $work_size $j $i >> "$test_name/results_$i.txt"
done
done
นี่คือบทสรุปของสิ่งที่ฉันสังเกต ฉันจะเปรียบเทียบพวกเขาเป็นคู่ในครั้งนี้เพราะฉันคิดว่ามันเป็นวิธีที่ให้ข้อมูลมากกว่านี้
CentOS 5.6 เทียบกับ CentOS 6.2
เวลานาฬิกาแขวน (gettimeofday) ต่อการทำซ้ำใน CentOS 5.6 นั้นแตกต่างกันมากกว่า 6.2 แต่มันสมเหตุสมผลเนื่องจาก CFS ควรทำงานได้ดีขึ้นในการให้กระบวนการเท่ากันกับเวลาของ CPU ทำให้ได้ผลลัพธ์ที่สอดคล้องกันมากขึ้น เป็นที่ชัดเจนว่า CentOS 6.2 นั้นมีความแม่นยำและสม่ำเสมอมากขึ้นตามระยะเวลาที่ใช้ในการนอนด้วยกลไกการนอนที่แตกต่างกัน
"การลงโทษ" นั้นชัดเจนใน 6.2 ที่มีจำนวนเธรดต่ำ (ปรากฏใน gettimeofday และผู้ใช้เวลา) แต่ดูเหมือนว่าจะลดลงด้วยจำนวนเธรดที่สูงขึ้น (ความแตกต่างในเวลาของผู้ใช้อาจเป็นเรื่องการบัญชีตั้งแต่ การวัดเวลาของผู้ใช้เป็นไปอย่างแน่นอน)
พล็อตเวลาของระบบแสดงให้เห็นว่ากลไกการนอนหลับใน 6.2 นั้นกินระบบมากกว่าที่ทำใน 5.6 ซึ่งสอดคล้องกับผลลัพธ์ก่อนหน้าของการทดสอบอย่างง่ายของ 50 กระบวนการเพียงแค่เรียกใช้ตัวเลือกที่บริโภค CPU ที่ไม่สำคัญใน 6.2 แต่ไม่ใช่ 5.6 .
สิ่งที่ฉันเชื่อว่าควรทราบคือการใช้ sched_yield () ไม่ได้ก่อให้เกิดโทษเช่นเดียวกับวิธีการนอนหลับ ข้อสรุปของฉันจากสิ่งนี้คือมันไม่ใช่ตัวกำหนดตารางเวลาเองซึ่งเป็นแหล่งที่มาของปัญหา แต่เป็นการโต้ตอบระหว่างวิธีการนอนหลับกับตัวกำหนดตารางเวลาที่เป็นปัญหา
Ubuntu 7.10 กับ Ubuntu 8.04-4
ความแตกต่างในเวอร์ชั่นเคอร์เนลระหว่างสองนี้มีขนาดเล็กกว่า CentOS 5.6 และ 6.2 แต่พวกเขายังคงขยายช่วงเวลาเมื่อเปิดตัว CFS ผลลัพธ์ที่น่าสนใจแรกคือการเลือกและการสำรวจความคิดเห็นเป็นกลไกการนอนหลับเพียงอย่างเดียวที่มี "การลงโทษ" ใน 8.04 และการลงโทษนั้นยังคงมีจำนวนเธรดที่สูงกว่า CentOS 6.2
เวลาของผู้ใช้สำหรับการเลือกและแบบสำรวจและ Ubuntu 7.10 ต่ำเกินไปอย่างไม่มีเหตุผลดังนั้นนี่จึงเป็นปัญหาทางบัญชีบางประเภทที่มีอยู่แล้ว แต่ฉันเชื่อว่าไม่เกี่ยวข้องกับปัญหา / การสนทนาในปัจจุบัน
เวลาของระบบดูเหมือนจะสูงกว่ากับ Ubuntu 8.04 มากกว่ากับ Ubuntu 7.10 แต่ความแตกต่างนี้คือความแตกต่างที่น้อยกว่าสิ่งที่เห็นด้วย CentOS 5.6 vs 6.2
หมายเหตุเกี่ยวกับ Ubuntu 11.10 และ Ubuntu 12.04
สิ่งแรกที่ควรทราบที่นี่คือพล็อตสำหรับ Ubuntu 12.04 นั้นเทียบได้กับ 11.10 ดังนั้นจึงไม่แสดงเพื่อป้องกันการซ้ำซ้อนที่ไม่จำเป็น
โดยรวมแล้วแปลงสำหรับ Ubuntu 11.10 แสดงแนวโน้มแบบเดียวกันกับที่พบใน CentOS 6.2 (ซึ่งบ่งชี้ว่านี่เป็นปัญหาเคอร์เนลโดยทั่วไปและไม่ใช่แค่เรื่อง RHEL) ข้อยกเว้นหนึ่งคือเวลาของระบบดูเหมือนจะสูงขึ้นเล็กน้อยเมื่อเทียบกับ Ubuntu 11.10 กว่ากับ CentOS 6.2 แต่อีกครั้งความละเอียดในการวัดนี้แน่นอนมากดังนั้นฉันคิดว่าข้อสรุปอื่น ๆ ที่นอกเหนือจาก "ดูเหมือนว่าจะสูงขึ้นเล็กน้อย "จะเหยียบลงบนน้ำแข็งบาง ๆ
Ubuntu 11.10 กับ Ubuntu 11.10 พร้อม BFS
PPA ที่ใช้ BFS กับเคอร์เนล Ubuntu สามารถดูได้ที่https://launchpad.net/~chogydan/+archive/ppaและสิ่งนี้ได้รับการติดตั้งเพื่อสร้างการเปรียบเทียบนี้ ฉันไม่พบวิธีที่ง่ายในการรัน CentOS 6.2 กับ BFS ดังนั้นฉันจึงวิ่งด้วยการเปรียบเทียบนี้และเนื่องจากผลลัพธ์ของ Ubuntu 11.10 เปรียบเทียบได้ดีกับ CentOS 6.2 ฉันเชื่อว่ามันเป็นการเปรียบเทียบที่ยุติธรรมและมีความหมาย
จุดสำคัญของโน้ตคือการที่ BFS เลือกเพียงอย่างเดียวและ nanosleep ทำให้เกิด "การลงโทษ" ที่จำนวนของเธรดที่ต่ำ แต่ดูเหมือนว่าจะทำให้เกิด "การลงโทษ" ที่คล้ายกัน จำนวนเธรด
จุดที่น่าสนใจอื่น ๆ คือเวลาของระบบจะลดลงด้วย BFS น้อยกว่าด้วย CFS อีกครั้งนี่เป็นการเริ่มต้นบนน้ำแข็งบางเนื่องจากความหยาบของข้อมูล แต่ความแตกต่างบางอย่างปรากฏขึ้นและผลลัพธ์นี้ตรงกับการทดสอบการเลือกลูป 50 กระบวนการอย่างง่ายแสดงการใช้ CPU น้อยกว่า BFS กว่ากับ CFS .
ข้อสรุปที่ฉันได้จากสองประเด็นนี้คือ BFS ไม่ได้แก้ปัญหา แต่อย่างน้อยดูเหมือนว่าจะลดผลกระทบในบางพื้นที่
ข้อสรุป
ตามที่ระบุไว้ก่อนหน้านี้ฉันไม่เชื่อว่านี่เป็นปัญหาของตัวจัดตารางเวลา แต่ด้วยการทำงานร่วมกันระหว่างกลไกการนอนหลับและตัวกำหนดตารางเวลา ฉันพิจารณาการใช้ CPU ที่เพิ่มขึ้นนี้ในกระบวนการที่ควรจะหลับและใช้ CPU เพียงเล็กน้อยหรือไม่มีเลยการถดถอยจาก CentOS 5.6 และอุปสรรคสำคัญสำหรับโปรแกรมใด ๆ ที่ต้องการใช้วนรอบเหตุการณ์หรือลักษณะการโพลของกลไก
มีข้อมูลอื่น ๆ ที่ฉันสามารถรับหรือทดสอบที่ฉันสามารถเรียกใช้เพื่อช่วยวินิจฉัยปัญหาเพิ่มเติมหรือไม่
อัพเดทเมื่อวันที่ 29 มิ.ย. 2555
ฉันทำให้โปรแกรมการทดสอบง่ายขึ้นเล็กน้อยและสามารถพบได้ที่นี่ (โพสต์เริ่มเกินขีดจำกัดความยาวดังนั้นจึงต้องย้าย)