我有一个Linux内核高CPU消耗的问题,同时在服务器上引导我的java应用程序。这个问题只发生在生产中,在开发服务器上一切都是光速。
upd9: 关于这个问题有两个问题:
怎么解决? - 名义动物 建议同步和删除所有内容,这确实有帮助。
sudo sh -c 'sync ; echo 3 > /proc/sys/vm/drop_caches ;
作品。 upd12:但确实如此sync
足够。为什么会这样? - 它对我来说仍然是开放的,我知道将durty页面刷新到磁盘会占用内核CPU和IO时间,这很正常。 但是什么是strage,为什么即使用“C”编写的单线程应用程序我在内核空间中加载所有内核100%?
由于参考upd10 和ref-upd11 我有一个想法 echo 3 > /proc/sys/vm/drop_caches
不需要用缓慢的内存分配来修复我的问题。
它应该足以运行`sync' 之前 开始消耗内存的应用程序。
可能会在生产中尝试这个tommorow并在此发布结果。
upd10: 丢失的FS缓存页面案例:
- 我执行了
cat 10GB.fiel > /dev/null
, 然后 sync
可以肯定的是,没有durty页面(cat /proc/meminfo |grep ^Dirty
显示184kb。- 检查
cat /proc/meminfo |grep ^Cached
我得到了:4GB缓存 - 运行
int main(char**)
我获得了正常的性能(比如50ms来初始化32MB的分配数据)。 - 缓存内存减少到900MB
- 测试总结: 我认为linux将用作FS缓存的页面回收到已分配的内存中是没有问题的。
upd11: 很多脏页案例。
项目清单
我跑了我的
HowMongoDdWorks
评论的例子read
部分,过了一段时间/proc/meminfo
说2.8GB是Dirty
和3.6GB是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.05s x 0 [尝试1 /部分2]时间0.42秒 x 1 [尝试2 /第2部分]时间0.04秒
测试摘要: 丢失的durty页面显着减慢了首次访问分配的内存(公平地说,只有当整个应用程序内存开始与整个OS内存相当时才开始发生,即如果你有16 GB的8个免费,那么就没问题了分配1GB,从3GB左右减速start)。
现在我设法在我的开发环境中重现这种情况,所以这里有新的细节。
开发机配置:
- Linux 2.6.32-220.13.1.el6.x86_64 - Scientific Linux版本6.1(Carbon)
- RAM:15.55 GB
- CPU:1 X Intel(R)Core(TM)i5-2300 CPU @ 2.80GHz(4线程)(物理)
由FS缓存中的大量durty页面引起的问题是99.9%。这是在脏页面上创建批量的应用程序:
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%)CPU负载(与下面相同,但我将再次复制它)。
#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:开发和生产系统都是这项测试的重中之重。 upd7:它不是分页,至少我在问题时间没有看到任何存储IO活动。
- dev~4核心,16 GM RAM,~8 GB免费
- 生产~12核,24 GB RAM,大约16 GB免费(从8到10 GM在FS Cache下,但没有 差异,即使所有16GM都是完全免费的,结果相同),这台机器也是由CPU加载的,但不要太高~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
...
在开发机器上运行此测试时,CPU使用率甚至没有从gound上升,就像所有内核在htop中的使用率低于5%一样。
但是在生产机器上运行此测试,我发现所有内核的CPU使用率高达100%(12核机器上的平均负载上升高达50%),而且这都是内核时间。
UPD2: 所有机器都安装了相同的centos linux 2.6,我使用ssh与他们合作。
upd3: 答:我不太可能进行交换,在测试期间没有看到任何磁盘活动,并且大量的RAM也是免费的。 (另外,descriptin更新)。 - 德米特里9分钟前
upd4: htop说核心的CPU利用率,al核心利用率高达100%(prod)。
upd5: 初始化完成后CPU利用率是否稳定下来?在我的简单测试中 - 是的。对于实际应用,它只是帮助阻止其他一切来启动一个新程序(这是无稽之谈)。
我有两个问题:
为什么会这样?
怎么解决?
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填充打印消耗的时间。
- 第二缓冲部分填充总是很快。
- 但是,第一次缓冲部分填充总是慢一点填充(我相信一些额外的工作是在第一页访问时完成我的内核)。
- 有些时候,第一次使用值填充缓冲区部分需要更长的时间。
我试过建议anwser,它似乎有帮助。我将在稍后再次检查并发布结果。
看起来linux将页面分配给durty文件系统缓存页面,并且需要花费大量时间将页面逐个刷新到磁盘。但总同步工作速度快,消除了问题。