У меня возникла проблема с высокой загрузкой процессора ядром linux, при загрузке моих java-приложений на сервере. Эта проблема возникает только в производстве, на серверах dev все работает быстро.
upd9: В этой проблеме было два вопроса:
-
Как это исправить? - Номинальное животное предложило синхронизировать и отбрасывать все, и это действительно помогает.
sudo sh -c 'sync ; echo 3 > /proc/sys/vm/drop_caches ;
Работает. upd12: Но действительноsync
достаточно. -
Почему это происходит?. Он по-прежнему открыт для меня, я понимаю, что кратковременные страницы на диске потребляют процессор ядра и время ввода-вывода, это нормально. Но что такое страх, почему даже однопоточное приложение, написанное на "C", загружает все ядра на 100% в пространстве ядра?
Из-за ref upd10 и ref upd11 У меня есть идея, что echo 3 > /proc/sys/vm/drop_caches
не требуется для исправления моей проблемы при медленном распределении памяти.
Достаточно запустить `sync ' перед запуском приложения, потребляющего память.
Вероятно, вы попробуете это сделать в производстве и опубликовать результаты здесь.
upd10: Потерянный файл кеша кеша:
- Я выполнил
cat 10GB.fiel > /dev/null
, затем -
sync
чтобы убедиться, что никакие страницы durty (cat /proc/meminfo |grep ^Dirty
не отображаются 184kb. - Проверка
cat /proc/meminfo |grep ^Cached
Я получил: 4 ГБ кэширования - Запуск
int main(char**)
У меня нормальная производительность (например, 50 мс для инициализации 32 МБ выделенных данных). - Объем кэшированной памяти до 900 МБ
- Сводка тестов: Я думаю, что Linux не проблема для восстановления страниц, используемых в кеше FS, в выделенную память.
upd11: МНОГО грязных страниц.
-
Элемент списка
-
Я запускаю свой
HowMongoDdWorks
пример с комментариямиread
и через некоторое время -
/proc/meminfo
сказал, что 2,8 ГБDirty
, а 3,6 ГБ -Cached
. -
Я остановился
HowMongoDdWorks
и запустил мойint main(char**)
. -
Вот часть результата:
init 15, время 0.00s x 0 [попытка 1/часть 0] время 1.11s x 1 [попытка 2/часть 0] время 0,04 с x 0 [попытка 1/часть 1] время 1.04s x 1 [попытка 2/часть 1] время 0,05 с x 0 [попытка 1/часть 2] время 0.42s x 1 [попытка 2/часть 2] время 0,04 с
-
Сводка по тесту: потерянные страницы долготы значительно замедляют первый доступ к выделенной памяти (честно говоря, это начинает происходить только тогда, когда общая память приложения начинает сравниваться со всей памятью ОС, т.е. если у вас есть 8 из 16 ГБ бесплатно, без проблем выделять 1 ГБ, замедление от 3 ГБ или около того).
Теперь мне удалось воспроизвести эту ситуацию в моей среде dev, так что вот новые подробности.
Конфигурация машины Dev:
- Linux 2.6.32-220.13.1.el6.x86_64 - Scientific Linux release 6.1 (Carbon)
- Оперативная память: 15,55 ГБ
- Процессор: 1 X Intel (R) Core i5-2300 CPU @2,80 ГГц (4 потока) (физический)
Это 99,9%, что проблема вызвана большим количеством страниц durty в кеше FS. Вот приложение, которое создает партии на грязных страницах:
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.Random;
/**
* @author dmitry.mamonov
* Created: 10/2/12 2:53 PM
*/
public class HowMongoDdWorks{
public static void main(String[] args) throws IOException {
final long length = 10L*1024L*1024L*1024L;
final int pageSize = 4*1024;
final int lengthPages = (int) (length/pageSize);
final byte[] buffer = new byte[pageSize];
final Random random = new Random();
System.out.println("Init file");
final RandomAccessFile raf = new RandomAccessFile("random.file","rw");
raf.setLength(length);
int written = 0;
int readed = 0;
System.out.println("Test started");
while(true){
{ //write.
random.nextBytes(buffer);
final long randomPageLocation = (long)random.nextInt(lengthPages)*(long)pageSize;
raf.seek(randomPageLocation);
raf.write(buffer);
written++;
}
{ //read.
random.nextBytes(buffer);
final long randomPageLocation = (long)random.nextInt(lengthPages)*(long)pageSize;
raf.seek(randomPageLocation);
raf.read(buffer);
readed++;
}
if (written % 1024==0 || readed%1024==0){
System.out.printf("W %10d R %10d pages\n", written, readed);
}
}
}
}
И вот тестовое приложение, которое вызывает загрузку процессора HI (до 100% по всем ядрам) в ядре ядра (то же, что и ниже, но я его еще раз скопирую).
#include<stdlib.h>
#include<stdio.h>
#include<time.h>
int main(char** argv){
int last = clock(); //remember the time
for(int i=0;i<16;i++){ //repeat test several times
int size = 256 * 1024 * 1024;
int size4=size/4;
int* buffer = malloc(size); //allocate 256MB of memory
for(int k=0;k<2;k++){ //initialize allocated memory twice
for(int j=0;j<size4;j++){
//memory initialization (if I skip this step my test ends in
buffer[j]=k; 0.000s
}
//printing
printf(x "[%d] %.2f\n",k+1, (clock()-last)/(double)CLOCKS_PER_SEC); stat
last = clock();
}
}
return 0;
}
Пока работает предыдущая программа HowMongoDdWorks
, int main(char** argv)
будет показывать такие результаты:
x [1] 0.23
x [2] 0.19
x [1] 0.24
x [2] 0.19
x [1] 1.30 -- first initialization takes significantly longer
x [2] 0.19 -- then seconds one (6x times slowew)
x [1] 10.94 -- and some times it is 50x slower!!!
x [2] 0.19
x [1] 1.10
x [2] 0.21
x [1] 1.52
x [2] 0.19
x [1] 0.94
x [2] 0.21
x [1] 2.36
x [2] 0.20
x [1] 3.20
x [2] 0.20 -- and the results is totally unstable
...
Я держу все ниже этой строки только для исторических целей.
upd1: обе, dev и производственные системы достаточно велики для этого теста. upd7: это не пейджинг, по крайней мере, я не видел активности IO хранения во время проблемного времени.
- dev ~ 4 ядра, 16 ГМ оперативной памяти, ~ 8 ГБ бесплатно
- производство ~ 12 ядер, 24 ГБ RAM, ~ 16 ГБ бесплатно (от 8 до 10 ГМ находится под кешем FS, но нет разница, такие же результаты, даже если все 16GM полностью бесплатны), также эта машина загружается процессором, но не слишком высока ~ 10%.
upd8 (ref): Новый тестовый пример и потенциальное объяснение см. в хвосте.
Вот мой тестовый пример (я также тестировал java и python, но "c" должен быть максимально понятным):
#include<stdlib.h>
#include<stdio.h>
#include<time.h>
int main(char** argv){
int last = clock(); //remember the time
for(int i=0;i<16;i++){ //repeat test several times
int size = 256 * 1024 * 1024;
int size4=size/4;
int* buffer = malloc(size); //allocate 256MB of memory
for(int k=0;k<2;k++){ //initialize allocated memory twice
for(int j=0;j<size4;j++){
//memory initialization (if I skip this step my test ends in
buffer[j]=k; 0.000s
}
//printing
printf(x "[%d] %.2f\n",k+1, (clock()-last)/(double)CLOCKS_PER_SEC); stat
last = clock();
}
}
return 0;
}
Выход на машине dev (частичный):
x [1] 0.13 --first initialization takes a bit longer
x [2] 0.12 --then second one, but the different is not significant.
x [1] 0.13
x [2] 0.12
x [1] 0.15
x [2] 0.11
x [1] 0.14
x [2] 0.12
x [1] 0.14
x [2] 0.12
x [1] 0.13
x [2] 0.12
x [1] 0.14
x [2] 0.11
x [1] 0.14
x [2] 0.12 -- and the results is quite stable
...
Выход на производственной машине (частичный):
x [1] 0.23
x [2] 0.19
x [1] 0.24
x [2] 0.19
x [1] 1.30 -- first initialization takes significantly longer
x [2] 0.19 -- then seconds one (6x times slowew)
x [1] 10.94 -- and some times it is 50x slower!!!
x [2] 0.19
x [1] 1.10
x [2] 0.21
x [1] 1.52
x [2] 0.19
x [1] 0.94
x [2] 0.21
x [1] 2.36
x [2] 0.20
x [1] 3.20
x [2] 0.20 -- and the results is totally unstable
...
При запуске этого теста на машине разработки использование ЦП даже не выросло из gound, так как все ядра имеют менее 5% использования в htop.
Но выполнение этого теста на производственной машине я вижу до 100% использования ЦП всеми ядрами (средняя загрузка увеличивается до 50% на машине с 12 ядрами), и все это время ядра.
UPD2:все машины имеют один и тот же centos linux 2.6, я работаю с ними с помощью ssh.
upd3: A: вряд ли это будет замена, не видели активности диска во время моего теста, и много ОЗУ также бесплатное. (также обновляется описатель). - Дмитрий 9 мин назад
upd4: htop говорит о загрузке процессора HI ядром, до 100% использования al core (на prod).
upd5: работает ли загрузка процессора после завершения инициализации? В моем простом тесте - Да. Для реального применения это помогает остановить все остальное, чтобы начать новую программу (что является бессмыслицей).
У меня есть два вопроса:
-
Почему это происходит?
-
Как это исправить?
upd8: Улучшена проверка и объяснение.
#include<stdlib.h>
#include<stdio.h>
#include<time.h>
int main(char** argv){
const int partition = 8;
int last = clock();
for(int i=0;i<16;i++){
int size = 256 * 1024 * 1024;
int size4=size/4;
int* buffer = malloc(size);
buffer[0]=123;
printf("init %d, time %.2fs\n",i, (clock()-last)/(double)CLOCKS_PER_SEC);
last = clock();
for(int p=0;p<partition;p++){
for(int k=0;k<2;k++){
for(int j=p*size4/partition;j<(p+1)*size4/partition;j++){
buffer[j]=k;
}
printf("x [try %d/part %d] time %.2fs\n",k+1, p, (clock()-last)/(double)CLOCKS_PER_SEC);
last = clock();
}
}
}
return 0;
}
И результат выглядит следующим образом:
init 15, time 0.00s -- malloc call takes nothing.
x [try 1/part 0] time 0.07s -- usually first try to fill buffer part with values is fast enough.
x [try 2/part 0] time 0.04s -- second try to fill buffer part with values is always fast.
x [try 1/part 1] time 0.17s
x [try 2/part 1] time 0.05s -- second try...
x [try 1/part 2] time 0.07s
x [try 2/part 2] time 0.05s -- second try...
x [try 1/part 3] time 0.07s
x [try 2/part 3] time 0.04s -- second try...
x [try 1/part 4] time 0.08s
x [try 2/part 4] time 0.04s -- second try...
x [try 1/part 5] time 0.39s -- BUT some times it takes significantly longer then average to fill part of allocated buffer with values.
x [try 2/part 5] time 0.05s -- second try...
x [try 1/part 6] time 0.35s
x [try 2/part 6] time 0.05s -- second try...
x [try 1/part 7] time 0.16s
x [try 2/part 7] time 0.04s -- second try...
Факты, которые я узнал из этого теста.
- Распределение памяти происходит быстро.
- Первый доступ к выделенной памяти выполняется быстро (так что это не проблема с ленивым распределением буфера).
- Я разбил выделенный буфер на части (8 в тесте).
- И заполняя каждую часть буфера значением 0, а затем со значением 1, время, затрачиваемое на печать.
- Заполнение второй буфером всегда быстрая.
- НО заполнение части буфера furst всегда немного медленнее, чем второе заполнение (я считаю, что некоторые дополнительные работы выполняются с моим ядром при доступе к первой странице).
- В некоторых случаях для заполнения части буфера значения в первый раз требуется ЗНАЧИТЕЛЬНО дольше.
Я попробовал предложить anwser, и, похоже, это помогло. Я снова проведу и снова отправлю результаты.
Похоже, что на картах linux выделены страницы для страниц кэша файловой системы durty, и требуется много времени, чтобы сбрасывать страницы на диск один за другим. Но полная синхронизация работает быстро и устраняет проблему.