CFS에서 CPU 사용량이 높습니까?


25

나는 물었다 이전 질문을 나는 그게이 커널의 CFS에 의해 발생되고 있음을 나타내는 것으로 보인다 위해 한 것을 분석은 RHEL 6으로 RHEL 5에서 응용 프로그램을 이동할 때 시도하고 CPU 사용량의 증가의 원인을 분리 할 수 있습니다. 나는 이것이 사실인지 확인하기 위해 테스트 응용 프로그램을 작성했습니다 (원래 테스트 응용 프로그램은 크기 제한에 맞게 제거되었지만 여전히 git repo 에서 사용할 수 있습니다) .

RHEL 5에서 다음 명령으로 컴파일했습니다.

cc test_select_work.c -O2 -DSLEEP_TYPE=0 -Wall -Wextra -lm -lpthread -o test_select_work

그런 다음 Dell Precision m6500에서 반복 당 실행 시간이 약 1ms가 될 때까지 매개 변수를 사용했습니다.

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에서 결과는 내가 기대했던 것입니다 (1ms 휴면으로 인해 4 개의 스레드가 두 배의 시간이 걸리지 만 각 스레드가 약 절반의 시간 동안 휴면 상태이므로 8 개의 스레드가 동일한 시간을 소비합니다. 선형 증가).

그러나 RHEL 6에서는 4 개의 스레드로 걸리는 시간이 예상되는 두 배보다 약 15 % 증가했으며 8 개의 스레드는 예상되는 약간의 증가보다 약 45 % 더 증가했습니다. 4 스레드 사례의 증가는 RHEL 6이 실제로 1ms 이상 몇 마이크로 초 동안 잠자고있는 반면 RHEL 5는 약 900 만 잠자지만 8과 40의 예기치 않은 큰 증가를 설명하지는 않습니다. 스레드 케이스.

3 -DSLEEP_TYPE 값이 모두 비슷한 유형의 동작을 보았습니다. 나는 또한 sysctl에서 스케줄러 매개 변수를 사용하여 시도했지만 결과에 큰 영향을 미치지 않는 것 같습니다. 이 문제를 더 진단 할 수있는 방법에 대한 아이디어가 있습니까?

업데이트 : 2012-05-07

또 다른 관찰 지점을 얻기 위해 테스트 결과로 / proc / stat // tasks // stat에서 사용자 및 시스템 CPU 사용량 측정 값을 추가했습니다. 또한 외부 반복 루프를 추가 할 때 도입 된 평균 및 표준 편차가 업데이트되는 방식에 문제가 있음을 발견하여 올바른 평균 및 표준 편차 측정 값이있는 새 플롯을 추가 할 것입니다. 업데이트 된 프로그램을 포함 시켰습니다. 또한 코드를 추적하기 위해 git 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;
}

몇 가지 다른 OS 버전의 Dell Vostro 200 (이중 코어 CPU)에서 테스트를 다시 실행했습니다. 나는 이것들 중 몇몇이 다른 패치를 적용 할 것이고 "순수한 커널 코드"가 아니라는 것을 알고 있지만, 이것은 다른 버전의 커널에서 테스트를 실행하고 비교할 수있는 가장 간단한 방법이었습니다. 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 vs CentOS 6.2

CentOS 5.6에서 반복 당 월 시계 시간 (gettimeofday)은 6.2보다 다양하지만 CFS는 프로세스에 동일한 CPU 시간을 부여하여보다 일관된 결과를 제공하는 CFS가 더 나은 작업을 수행해야하므로 이치에 맞습니다. CentOS 6.2가 다른 수면 메커니즘으로 수면 시간이 더 정확하고 일관성이 있다는 것도 분명합니다. gettimeofday CentOS 5.6 gettimeofday CentOS 6.2

"벌칙"은 6.2에서 적은 수의 스레드 (gettimeofday 및 사용자 시간 플롯에 표시)로 명백하지만 스레드 수가 많을수록 감소하는 것 같습니다 (사용자 시간의 차이는 그 이후의 회계적인 문제 일 수 있음) 사용자 시간 측정은 물론입니다).

utime CentOS 5.6 utime CentOS 6.2

시스템 시간 도표는 6.2의 절전 메커니즘이 5.6에서보다 더 많은 시스템을 소비하고 있음을 보여줍니다. 이는 5.6에서 5.6이 아닌 6.2에서 사소한 양의 CPU를 소비하는 select를 호출하는 간단한 프로세스의 이전 테스트 결과에 해당합니다. .

stime CentOS 5.6 stime CentOS 6.2

내가 주목할만한 가치가 있다고 생각하는 것은 sched_yield ()를 사용하면 sleep 메소드에서 볼 때와 동일한 패널티가 발생하지 않는다는 것입니다. 이것에 대한 나의 결론은 이슈의 원인이되는 것은 스케줄러 자체가 아니라, 슬립 메소드와 이슈 인 스케줄러와의 상호 작용이다.

우분투 7.10 vs 우분투 8.04-4

이 두 버전의 커널 버전 차이는 CentOS 5.6 및 6.2 버전의 차이보다 작지만 CFS가 도입 된 기간에 걸쳐 있습니다. 첫 번째 흥미로운 결과는 select와 poll이 8.04의 "페널티"를 갖는 유일한 수면 메커니즘 인 것처럼 보이며 CentOS 6.2에서 볼 수있는 것보다 많은 수의 스레드가 계속됩니다.

gettimeofday 우분투 7.10 gettimeofday 우분투 8.04-4

select 및 poll과 Ubuntu 7.10의 사용자 시간이 부당하게 낮기 때문에 당시에 발생한 일종의 회계 문제인 것 같지만 현재 문제 / 토론과 관련이 없다고 생각합니다.

우 타임 우분투 7.10 우 타임 우분투 8.04-4

시스템 시간은 Ubuntu 7.10보다 Ubuntu 8.04에서 더 높은 것처럼 보이지만 CentOS 5.6 대 6.2에서 보였던 것보다이 차이는 덜 명확합니다.

stime 우분투 7.10 stime 우분투 8.04-4

우분투 11.10 및 우분투 12.04에 대한 참고 사항

여기서 가장 먼저 주목해야 할 것은 우분투 12.04의 플롯은 11.10의 플롯과 비교 가능하므로 불필요한 중복을 방지하지는 않습니다.

우분투 11.10에 대한 전반적인 도표는 CentOS 6.2에서 관찰 된 것과 같은 종류의 추세를 보여줍니다 (이것은 RHEL 문제가 아니라 일반적인 커널 문제임을 나타냅니다). 한 가지 예외는 CentOS 6.2보다 Ubuntu 11.10에서 시스템 시간이 약간 더 높은 것처럼 보이지만 다시 한 번이 측정의 해상도는 매우 높기 때문에 "그것은 조금 더 높은 것 같습니다. "는 얇은 얼음 위에 섰을 것입니다.

BFS를 사용하는 우분투 11.10 vs 우분투 11.10

Ubuntu 커널과 함께 BFS를 사용하는 PPA는 https://launchpad.net/~chogydan/+archive/ppa 에서 찾을 수 있으며이 비교를 생성하기 위해 설치되었습니다. BFS로 CentOS 6.2를 실행하는 쉬운 방법을 찾을 수 없으므로이 비교를 실행했으며 우분투 11.10의 결과가 CentOS 6.2와 잘 비교되므로 공정하고 의미있는 비교라고 생각합니다.

gettimeofday 우분투 11.10 BFS가 포함 된 gettimeofday Ubuntu 11.10

중요한 점은 BFS 만 선택하고 nanosleep을 사용하면 적은 수의 스레드에서 "벌금"을 유발하지만 CFS에서 볼 때와 비슷한 "벌금"(더 큰 것이 아닌 경우)을 유발하는 것으로 보입니다. 스레드 수

우 타임 우분투 11.10 BFS가 포함 된 utime Ubuntu 11.10

또 다른 흥미로운 점은 시스템 시간이 CFS보다 BFS보다 낮다는 것입니다. 다시 한 번, 이것은 데이터의 거칠기 때문에 얇은 얼음을 밟기 시작하지만 약간의 차이가있는 것으로 보이며이 결과는 간단한 50 프로세스 선택 루프 테스트와 일치합니다 .CFS보다 BFS의 CPU 사용량이 적었습니다. .

stime 우분투 11.10 stime 우분투 11.10과 BFS

이 두 가지 점에서 도출 한 결론은 BFS가 문제를 해결하지는 않지만 적어도 일부 영역에서는 그 영향을 줄이는 것으로 보입니다.

결론

앞서 언급했듯이, 이것은 스케줄러 자체의 문제가 아니라 수면 메커니즘과 스케줄러 간의 상호 작용에 있다고 생각합니다. 필자는 CentOS 5.6의 회귀 및 거의 또는 전혀 CPU를 사용하지 않아야하는 프로세스에서 CPU 사용량이 증가했다고 생각하고 이벤트 루프 또는 폴링 스타일 메커니즘을 사용하려는 모든 프로그램의 주요 장애물입니다.

문제를 더 진단하기 위해 얻을 수있는 다른 데이터 나 테스트가 있습니까?

2012 년 6 월 29 일 업데이트

테스트 프로그램을 약간 단순화했으며 여기 에서 찾을 수 있습니다 (포스트가 길이 제한을 초과하기 시작했기 때문에 이동해야했습니다).


3
와우, 철저한 분석-그러나 많은 양의 데이터로 인해 원래의 질문은 나에게 화를 내고 있습니다. 1) 단일 테스트 2) 단일 배포판 3) 두 개의 다른 커널 4) 15 % 둔화? 마지막 단락의 가설이 옳다면, 커널 소스를 찾기 시작해야하지만 다른 변수를 먼저 제거해야한다고 생각합니다.
ckhan

테스트 응용 프로그램에서 일부 출력을 추가했으며 이제는 모든 정보를 좀 더 쉽게 소화하기 위해 쌍으로 비교합니다.
Dave Johansen

나는 그 버그질라를 보려고 노력했지만, Redhat은 그것이 "내부 버그질라이며 대중에게는 보이지 않는다"고 말합니다. 이것에 대한 업데이트가 있습니까?

나는 전체 RedHat 버그에 익숙하지 않아서 그렇게 한 버그를 만들 때 내가했거나하지 않았을 수도 있지만 지금까지 들었던 유일한 업데이트는 하이퍼 스레드 프로세서에서 더 잘 작동하지만 아직 실제 수정 사항은 없습니다.
Dave Johansen

2
CFS는 완전히 공정한 스케줄러입니까? SLES11 SP2의 Java 기반 응용 프로그램에서도 성능 문제가 발생했습니다. SP1과의 차이점은 CFS의 변화입니다.
Nils

답변:


1

SLES 11 SP2 릴리스 노트 에 따르면 이는 CFS 구현 방식에 도입 된 변경 사항 일 수 있습니다.

SLES 11 SP2가 현재 SLES 버전이므로이 동작은 여전히 ​​유효합니다 (모든 3.x- 커널 에서처럼).

이 변경은 의도 된 것이지만 "나쁜"부작용이있을 수 있습니다. 설명 된 해결 방법 중 하나가 도움이 될 것입니다 ...


링크에 문제가있는 것으로 보이며 올바른 링크가 여기 있지만이 해결 방법을 시도해보고 성능에 도움이되는지 확인합니다.
Dave Johansen

이것에 대한 더 많은 뉴스?
vonbrand

@vonbrand 당신은 아마 Dave에게 물어봐야합니다 ...
Nils
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.