酷!學園
技術討論區 => 程式討論版 => 主題作者是: logichom 於 2014-10-26 00:11
-
之前撰寫程式碼時只在乎perl程式消耗多少記憶體
而忽略CPU的使用情況
而目前單一一隻程式就占用CPU約65%的使用量
想請問有什麼辦法可將該程式消耗的CPU使用量降低?
為方便大家隔空抓藥
簡單說明一下該程式的功能:
1.連線至mysql資料庫建立資料表
2.開檔
3.無限迴圈讀檔
4.如果讀到關鍵訊息
5.建立與資料庫連線
6.寫入資料到資料庫
7.回到3.
-
無限迴圈內加個 sleep 讓程式睡一下就可以了
perl 不熟,自己找找相關指令
-
無限迴圈內加個 sleep 讓程式睡一下就可以了
perl 不熟,自己找找相關指令
好奇如果讓程式睡一下會不會因此錯過log?
-
2. 開檔
開什麼檔? Log ???
不讀取會馬上刪除?
調整一下程式或是目前的程式寫法就可以解決的問題,不能解決就耗 CPU 啊
-
為方便大家隔空抓藥
簡單說明一下該程式的功能:
...
3.無限迴圈讀檔
4.如果讀到關鍵訊息
...
7.回到3.
我想問題出在 4 (他是待罪羔羊)。
如果沒讀到時,它會做什麼? 我想程式可能是直接跳到 7,7 馬上再跳到 3 -> 4
cpu 什麼也沒做,一直在跳來跳去。
-
用 nice 或 batch 去跑?
-
2. 開檔
開什麼檔? Log ???
不讀取會馬上刪除?
調整一下程式或是目前的程式寫法就可以解決的問題,不能解決就耗 CPU 啊
是log檔沒錯
目前想到是在某個時段將該天log讀一次
一天讀一次log然後寫到資料庫去應該loading就不會重了
但是讀的時間要抓好
不然漏掉訊息就糟了
-
為方便大家隔空抓藥
簡單說明一下該程式的功能:
...
3.無限迴圈讀檔
4.如果讀到關鍵訊息
...
7.回到3.
我想問題出在 4 (他是待罪羔羊)。
如果沒讀到時,它會做什麼? 我想程式可能是直接跳到 7,7 馬上再跳到 3 -> 4
cpu 什麼也沒做,一直在跳來跳去。
是的
但是光讀檔跟跳來跳去就吃cpu loading
不太能理解
至於為什麼要讀到關鍵訊息
因為log是由設備丟出來
其中參雜很多不必要的訊息
過濾就成為必要手段了
-
一個東西讓你從右手拿到左手
明明就沒東西給你,你也要不斷的做動作,難道不會累嗎?
重點是,不斷的重複動作,就「一定」可以接收到資料? 誰跟你說的
-
喔,原來是這地方不理解。
cpu 只要在執行就會耗能,即使只是一個"跳"的動作。
你可以試試看,寫個程式,只是一直跳,什麼也不做。
// 語法忘了,請自行修正
while <TRUE> {
}
它會做到死,cpu loading 會飆高。
但是光讀檔跟跳來跳去就吃cpu loading
不太能理解
-
一開始 hoyo 老大就給解答了,樓主試過了嗎?
如果因為『空』迴圈 CPU 吃太重
最簡單的解法就是在迴圈裡加 sleep
下面兩個指令(ctrl-c停止)
自己比較看看吧
$ perl -e 'while(1){}'
$ perl -e 'use Time::HiRes qw(usleep); while(1){usleep(500);}'
-
或者可用 cpulimit 限制程式執行的cpu 使用率
http://cpulimit.sourceforge.net/
-
sleep 加上去試看看不就知道了嗎?
經驗上來說, 就算只停個 1ms 的時間, 也會大幅改進吃 cpu 的時間.
問題是... 如果是這麼重要又急的事情, 似乎不是用這種寫法吧.... 通常這類的寫法, 停個 1s 再處理都算快的了.
-
這邊有個難言之隱
為什麼不加sleep
為什麼要用無限迴圈
因為受限於log
要過濾log是用時間來判斷
但是要判斷時間如果不讓他一直更新就無法取得最新的時間
除非有不用無限迴圈的方法
這個我還在思考中...
先謝謝各位前輩了
-
sleep 加上去試看看不就知道了嗎?
經驗上來說, 就算只停個 1ms 的時間, 也會大幅改進吃 cpu 的時間.
問題是... 如果是這麼重要又急的事情, 似乎不是用這種寫法吧.... 通常這類的寫法, 停個 1s 再處理都算快的了.
謝謝提醒了我
因為我一直以為sleep只能到秒
後來查了一下有可以到毫秒的用法
http://stackoverflow.com/questions/896904/how-do-i-sleep-for-a-millisecond-in-perl (http://stackoverflow.com/questions/896904/how-do-i-sleep-for-a-millisecond-in-perl)
-
有無程式碼PO來瞧瞧 :)
-
不是難言之隱,只是你用的方法不好,。
用
while <> {
}
或 tail -f logfile | yourprog.pl
建議你多看範例才能快速上手,自己摸索很容易卡關。
還有 sleep 是要減少 cpu 跳來跳去避免 cpu load 太高,你又想只睡幾毫秒,那又回到老問題了: cpu load 很高。
方法不好要改方法,不要去補丁,浪費你 coding 的生命。
-
最新的時間用 date 來取也可以,可以取過去5分鐘或1分鐘啊 ...
不用依賴 log
-
或是交給 cron 去跑,如果 log 沒變更,就直接結束。
如果擔心跑太久到下次cron還沒跑完,就用 lock file 來防止。
-
程式碼在此:
my $dsn="DBI:mysql:database=search;host=localhost";
my $userid="root";
my $password="password";
my $dbh=DBI->connect($dsn,$userid,$password) or print "Could not connect to database: $DBI::errstr";
eval{
$dbh->do('CREATE TABLE mytable(SN integer auto_increment primary key,Month CHAR(3),Day CHAR(2),
Time CHAR(8),MAC CHAR(17),Username VARCHAR(64))');
$dbh->disconnect or warn $dbh->errstr;
};
warn $@ if $@;
my $file1="/var/log/xxx/mytable.log";
eval{ open(LOGFILE, $file1) or die "Can't open file , $!\n"; };
print "Error:$@" if $@;
for(;;)
{
while(<LOGFILE>)
{
chomp $_;
($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime();
my $dates = strftime "%H:%M",localtime;
@array=split(' ', $_);
if(@array[0] == "$months[$mon]" && @array[1] == "$mday" && @array[2] =~ /$dates/)
{
if($_ =~ /Action=do/)
{
@array = split(' ', $_);
$month=@array[0];
$day=@array[1];
$time=@array[2];
@array = split(',', $_);
$mac=substr @array[3],7;
$mac1=substr($mac,0,2).":".substr($mac,3,2).":".substr($mac,6,2).":"
.substr($mac,9,2).":".substr($mac,12,2).":".substr($mac,15,2);
$mac1=lc $mac1;
$username=substr @array[6],5;
$username =~ s/[\r\n]//;
if($mac1 =~ /(?:[A-Fa-f0-9]{2}[:-]){5}(?:[A-Fa-f0-9]{2})/)
{
eval{
$dbh=DBI->connect($dsn,$userid,$password) or print "Could not connect to database:$DBI::errstr";
$dbh->do("set names utf8");
my $sth=$dbh->prepare("INSERT INTO mytable(Month,Day,Time,MAC,Username)values(?,?,?,?,?)");
$sth->execute($month,$day,$time,$mac1,$username)or print $DBI::errstr;
$sth->finish();
$dbh->disconnect or warn $dbh->errstr;
};
warn $@ if $@;
}
}
}
$month="";
$day="";
$time="";
$mac="";
$username="";
$dates="";
}
$rotate=strftime "%H:%M:%S",localtime;
if($rotate eq "03:XX:XX")
{
close(LOGFILE);
sleep 1;
eval{ open(LOGFILE, $file1) or die "Can't open file , $!\n"; };
print "Error:$@" if $@;
}
}
-
方法有好幾種,我針對你寫法不好的地方提供一個樣本,參考看看。
程式: print.pl
===============
#!/usr/bin/perl
while(<STDIN>){
print $_ ."\n";
}
===============
在 terminal 打
touch my.log; tail -f my.log | ./print.pl
再開一個 terminal, 輸入:
echo AA >> my.log
你會看到第一個 terminal 會輸出 AA
上面這樣寫,cpu 很輕鬆,不會有 load 飆高的現象。
-
在 chomp $_ 的上頭加上 sleep (1) 8) 8)
操練前先休息一下
-
1. 接收資料部分使用 while read , 倒建議交給 socket
perl 開 socket 很容易 , 緩衝一定有餘又不浪費資源
2. 開 socket 接資料跟讀檔是兩件事
使用 tail -f , 若檔案重頭開始 (如 syslog 的 logrot?? <- 怎麼拼 ??)
那將會有很多行送不出來
您 /var/log/xxx/mytable.log 是怎麼來的須留意
若是 syslog() , printk() ... 這類呼叫 log deamon 寫入
那讀檔部分就交由 linux 內建 log server 較佳
若不是 , 讀檔部分要考慮重複 , 漏掉問題 ... 那將是整體中最浪費資源的
-
1. 接收資料部分使用 while read , 倒建議交給 socket
perl 開 socket 很容易 , 緩衝一定有餘又不浪費資源
2. 開 socket 接資料跟讀檔是兩件事
使用 tail -f , 若檔案重頭開始 (如 syslog 的 logrot?? <- 怎麼拼 ??)
那將會有很多行送不出來
您 /var/log/xxx/mytable.log 是怎麼來的須留意
若是 syslog() , printk() ... 這類呼叫 log deamon 寫入
那讀檔部分就交由 linux 內建 log server 較佳
若不是 , 讀檔部分要考慮重複 , 漏掉問題 ... 那將是整體中最浪費資源的
檔案是由設備丟出來的,我的server再去設定接收該log,該log會在半夜做輪替,然後我的程式要在輪替完從新開檔讀檔
-
logrotate 可以設定 prerotate 跟 postrotate 的 script 啊~
-
組合一下 tail -f | grep | awk | mysql -e
cpu 會非常低
-
如dark說所
做成daemon加上socket才是最有效率的方式
如果流量與動作不多,pipe方式是能加減的用