CFS ile yüksek CPU kullanımı?


25

Bir uygulamayı RHEL 5'ten RHEL 6'ya taşırken CPU kullanımındaki bir artışın kaynağını denemek ve izole etmek için önceki bir soru sordum . Bunun için yaptığım analiz, çekirdekteki CFS'nin neden olduğunu gösteriyor. Durumun böyle olup olmadığını kontrol etmek için bir test uygulaması yazdım (orijinal test uygulaması boyut sınırına uyacak şekilde kaldırıldı, ancak hala git repo'da mevcut) .

RHEL 5’te aşağıdaki komutu kullandım:

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

Daha sonra, her bir yineleme için yürütme süresi bir Dell Precision m6500'de yaklaşık 1 ms olana kadar parametrelerle oynadım.

RHEL 5'te şu sonucu aldım:

./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'da ayrıca:

./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

Her iki versiyonda da, bu sonuçlar, her bir yineleme başına ortalama zamanın nispeten lineer olarak ölçeklenmesiyle beklediğimden kaynaklanıyordu. Daha sonra -DSLEEP_TYPE=1RHEL 5 ile yeniden derledim ve aşağıdaki sonuçları aldım:

./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

Ve RHEL 6'da aşağıdaki sonuçlar:

./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'de, sonuçlar beklediğim gibi oldu (1 ms uykusundan iki kat daha uzun süren 4 iplik, ancak her iplik şimdi yaklaşık yarısı boyunca uyuduğundan beri aynı miktarda zaman alan 8 iplik ve hala oldukça doğrusal artış).

Bununla birlikte, RHEL 6 ile 4 ipliğin aldığı süre beklenen ikiye katlamanın yaklaşık% 15 oranında artarken, 8 ipliğin kasası beklenen hafif artıştan yaklaşık% 45 daha fazla arttı. 4 iplikli durumdaki artış, RHEL 6'nın aslında 1 ms'den daha fazla bir mikrosaniye için uyuduğu, RHEL 5 ise sadece 900 kişi civarında uyuduğu, ancak bu durum 8 ve 40'ta beklenmedik şekilde büyük bir artış olduğunu açıklamadığı anlaşılıyor. iş parçacığı davaları.

Tüm 3 -DSLEEP_TYPE değerleri ile benzer davranış türlerini gördüm. Ayrıca sysctl'deki zamanlayıcı parametreleriyle oynamayı da denedim, ancak sonuçların üzerinde hiçbir etkisi önemli görünmedi. Bu konuyu nasıl daha iyi teşhis edebileceğim hakkında bir fikriniz var mı?

GÜNCELLEME: 2012-05-07

Başka bir gözlem noktasını denemek ve elde etmek için kullanıcı ve sistem CPU kullanımı ölçümlerini / proc / stat // görevler / stat den / testin bir çıktısı olarak ekledim. Dış yineleme döngüsünü eklediğimde ortaya çıkan ortalama ve standart sapmanın güncellenme biçimiyle ilgili bir sorun da buldum, bu yüzden düzeltilmiş ortalama ve standart sapma ölçümlerine sahip yeni grafikleri ekleyeceğim. Güncellenmiş programı dahil ettim. Ayrıca kodu izlemek için git repo yaptım ve burada mevcut.

#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'deki (çift çekirdekli CPU) testleri birkaç farklı işletim sistemi sürümüyle yeniden yaptım. Bunların birçoğunun farklı yamalar uygulanacağını ve "saf çekirdek kodu" olmayacağını fark ettim, ancak bu, çekirdeğin farklı sürümlerinde testleri çalıştırıp karşılaştırmalar yapmamın en basit yoluydu. Gnuplot ile parseller oluşturdum ve bugzilla'nın bu konudaki versiyonunu ekledim .

Bu testlerin tümü, aşağıdaki komut dosyası ve bu komut ile aşağıdaki komutla çalıştırıldı ./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

İşte gözlemlediğim şeyin özeti. Onları çiftler halinde karşılaştıracağım çünkü bu şekilde biraz daha bilgilendirici olduğunu düşünüyorum.

CentOS 5.6 vs CentOS 6.2

CentOS 5.6'daki yineleme başına duvar saati (6.2. Gün) 6.2'den daha değişkendir, ancak bu, CFS'nin, işlemlere daha tutarlı sonuçlar veren eşit CPU zamanı vermek için daha iyi bir iş yapması gerektiği için anlamlıdır. Ayrıca, CentOS 6.2'nin farklı uyku mekanizmaları ile uyuduğu süre boyunca daha doğru ve tutarlı olduğu da oldukça açık. gettimeofday CentOS 5.6 gettheofday CentOS 6.2

"Ceza", 6.2'de çok az sayıda iş parçacığıyla (açıkça görüldüğü zaman ve kullanıcı zaman çizelgelerinde görülebilir) kesinlikle belirgindir, ancak daha fazla iş parçacığıyla azalmış gibi görünmektedir (kullanıcı zamanındaki fark, kullanıcı zaman ölçümleri tabii ki).

utime CentOS 5.6 utime CentOS 6.2

Sistem zaman grafiği, 6.2'deki uyku mekanizmalarının, 5.6'da olduğundan daha fazla sistem tükettiğini göstermektedir; bu, sadece 6.2'de önemsiz olmayan bir CPU tüketen ancak 5.6'yı seçerek seçen 50 işlemin basit testinin önceki sonuçlarına tekabül etmektedir. .

stime CentOS 5,6 stime CentOS 6.2

Unutmaya değer olduğuna inandığım bir şey, sched_yield () kullanımının uyku yöntemlerinde görülenle aynı cezaya neden olmadığı. Buradaki sonucum, konunun kaynağı olan zamanlayıcının kendisi olmadığı, ancak uyku yöntemlerinin sorun olan zamanlayıcı ile etkileşimi olduğu yönündedir.

Ubuntu 7.10 vs Ubuntu 8.04-4

Bu ikisi arasındaki çekirdek versiyonundaki fark, CentOS 5.6 ve 6.2'den daha küçüktür, ancak yine de CFS'nin tanıtıldığı zaman dilimini kapsar. İlk ilginç sonuç, seçim ve anketin 8.04'te "cezası" olan tek uyku mekanizmaları gibi görünmesi ve bu cezanın, CentOS 6.2'de görülenden daha fazla sayıda ipliğe devam etmesidir.

gettimeofday Ubuntu 7.10 gettimeofday Ubuntu 8.04-4

Seçme ve yoklama ve Ubuntu 7.10 için kullanıcı zamanı makul bir şekilde düşüktür, bu nedenle o zamanlar var olan bir tür muhasebe sorunu gibi görünmektedir, ancak şu anki sorun / tartışma ile ilgili olmadığını düşünüyorum.

utime Ubuntu 7.10 utime Ubuntu 8.04-4

Sistem zamanı Ubuntu 8.04 ile Ubuntu 7.10'a göre daha yüksek görünüyor ancak bu fark, FAR'ın CentOS 5.6 ile 6.2'de görüldüğünden daha az belirgin.

stime Ubuntu 7.10 stime Ubuntu 8.04-4

Ubuntu 11.10 ve Ubuntu 12.04 ile ilgili notlar

Burada dikkat edilmesi gereken ilk şey, 12.04 Ubuntu 12.04 parsellerinin 11.10'dan gelenlerle karşılaştırılabilir olması nedeniyle gereksiz fazlalıkların önlenmesi için gösterilmemiştir.

Genel olarak Ubuntu 11.10 için verilen grafikler, CentOS 6.2 ile gözlemlenen aynı eğilim eğilimini göstermektedir (bu, bunun genel olarak bir çekirdek sorunu olduğunu ve sadece bir RHEL sorunu olmadığını göstermektedir). Bunun tek istisnası, sistem zamanının Ubuntu 11.10 ile CentOS 6.2'ye göre biraz daha yüksek görünmesidir, ancak bir kez daha, bu ölçümdeki çözünürlük çok seyrektir, bu yüzden "dışında bir sonucun biraz daha yüksek olduğunu düşünüyorum" "ince buza basmak olurdu.

Ubuntu 11.10 vs Ubuntu 11.10 BFS ile birlikte

Ubuntu çekirdeği ile BFS kullanan bir PPA, https://launchpad.net/~chogydan/+archive/ppa adresinde bulunabilir ve bu karşılaştırmayı oluşturmak için kurulmuştur. CentOS 6.2'yi BFS ile çalıştırmanın kolay bir yolunu bulamadım, bu yüzden bu karşılaştırmaya koştum ve Ubuntu 11.10'un sonuçları CentOS 6.2 ile çok iyi karşılaştığından, bunun adil ve anlamlı bir karşılaştırma olduğuna inanıyorum.

gettimeofday Ubuntu 11.10 gettimeofday Ubuntu 11.10 ile BFS

Notun ana noktası, BFS'nin sadece seçtiği ve nanosleep'in düşük sayıdaki dişlerde "cezayı" indüklediği, ancak CFS'de daha yüksek bir değer için görülene benzer bir "ceza" (daha büyük olmasa bile) uyardığı görünmesidir. konu sayısı.

utime Ubuntu 11.10 utime Ubuntu 11.10 ile BFS

Diğer ilginç nokta, sistem zamanının BFS'de CFS'den daha düşük görünmesidir. Bir kez daha, bu verilerin kaba olması nedeniyle ince buz üzerinde adım atmaya başlıyor, ancak bazı farklar var gibi görünüyor ve bu sonuç eşleşmiyor basit 50 işlem seçme döngüsü testi ile BFS ile CPU kullanımından daha az CPU kullanımı gösterildi. .

stime Ubuntu 11.10 stime Ubuntu 11.10 ile BFS

Bu iki noktadan çıkardığım sonuç, BFS'nin sorunu çözmediği, ancak en azından bazı alanlardaki etkilerini azalttığı görünüyor.

Sonuç

Daha önce belirtildiği gibi, bunun zamanlayıcının kendisiyle değil, uyku mekanizmaları ile zamanlayıcı arasındaki etkileşimle ilgili bir sorun olduğuna inanmıyorum. Bu artmış CPU kullanımını uykuda ve işlemcilerin CentOS 5.6'dan çok az veya hiç CPU kullanmaması ve bir olay döngüsü veya yoklama tarzı bir mekanizma kullanmak isteyen herhangi bir program için büyük bir engel kullanması gerektiğini düşünüyorum.

Sorunu daha iyi teşhis edebilmem için alabileceğim veya test edebileceğim başka veriler var mı?

29 Haz 2012 tarihinde güncelleme

Test programını biraz basitleştirdim ve burada bulunabildim (Gönderi uzunluğu sınırını aşmaya başlamıştı, bu yüzden onu taşımak zorunda kaldım).


3
Vay, ayrıntılı analiz - ama o kadar çok veriyle asıl soru bana daha da yaklaştı. Kaynatır mısın 1) tek bir test 2) tek bir dağıtım 3) iki farklı çekirdek 4)% 15 yavaşlama? Eğer son paragraftaki hipoteziniz doğruysa, çekirdek kaynaklarını farklılaştırmaya başlamanın zamanı geldi, ancak önce diğer değişkenler ortadan kaldırılmalı gibi geliyor.
ckhan

Test uygulamasından bazı çıktılar ekledim ve şimdi tüm bilgileri sindirmeyi biraz daha kolay hale getirmek için çiftler halinde karşılaştırma yaptım.
Dave Johansen

O bugzilla'ya bakmaya çalıştım, ama Redhat, "iç bugzilla ve halka açık değil" olduğunu söylüyor. Bu konuda herhangi bir güncelleme oldu mu?

Tüm RedHat hata olayında yeniyim, bu yüzden bunu yapan hatayı oluştururken yaptığım (veya yapmadığım) bir şey olmuş olabilir, ancak şu ana kadar duyduğum tek güncelleme bir güncelleme. hiper iş parçacıklı işlemcilerle daha iyi davranmasını sağlayan parametre, ancak henüz gerçek bir düzeltme yapmadı.
Dave Johansen,

2
CFS tamamen adil zamanlayıcı mı? Bu ilginç görünüyor - SLES11 SP2'de java tabanlı bir uygulamayla da performans sorunuyla karşılaştım. Fark (SP1'e göre) CFS'deki değişim ...
Nils

Yanıtlar:


1

Göre SLES 11 SP2 sürüm-notları bu CFS uygulanmaktadır şekilde tanıtıldı bir değişiklik olabilir.

SLES 11 SP2, mevcut SLES-sürümü olduğundan, bu davranış hala geçerlidir (tüm 3.x çekirdekleri için göründüğü gibi).

Bu değişiklik planlandı - ancak "kötü" yan etkileri olabilir. Belki de açıklanan geçici çözümlerden biri sizin için yardımcı olur ...


Görünüşe göre bağlantıda yanlış bir şey var ve doğru olanı burada , ancak bu geçici çözümlere bir deneyeceğim ve performansa yardımcı olup olmadıklarına bakacağım.
Dave Johansen

Bununla ilgili başka haber var mı?
vonbrand

@ vonbrand Muhtemelen Dave'e sormak zorundasın ...
Nils
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.