URL: https://www.opennet.me/cgi-bin/openforum/vsluhboard.cgi
Форум: vsluhforumID9
Нить номер: 7609
[ Назад ]

Исходное сообщение
"загрузка CPU и скорость работы скриптов"

Отправлено Alexander , 27-Сен-08 15:05 
Коллеги,

Подскажите, плз, что-то сам не соображу.

Итак, есть некий перл-скрипт, запускающийся из шелл-врапера, задача которого анализировать принадлежность трафика заданным подсетям и генерировать .sql файлы, для последующего импорта в БД. Скрипт крутится на линукс-боксе P4 3GHz (HT включен) и гиг оперативки, ядро 2.6.22-smp.


Когда скрипт не работает, загрузки нет совсем:
top - 14:55:23 up 24 days, 16:07,  2 users,  load average: 0.29, 0.77, 0.85
Tasks:  68 total,   1 running,  67 sleeping,   0 stopped,   0 zombie
Cpu0  :  0.0%us,  0.0%sy,  0.0%ni,100.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Cpu1  :  0.3%us,  0.0%sy,  0.0%ni, 99.7%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Mem:    906184k total,   875832k used,    30352k free,    98976k buffers
Swap:  1048568k total,       52k used,  1048516k free,   616412k cached

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
    1 root      15   0  2036  656  564 S    0  0.1   0:00.83 init


Когда работает один инстанс скрипта, загрузка системы выглядит след. образом:
top - 14:54:05 up 24 days, 16:05,  2 users,  load average: 1.01, 1.00, 0.92
Tasks:  71 total,   2 running,  69 sleeping,   0 stopped,   0 zombie
Cpu0  :  0.6%us,  0.0%sy,  0.0%ni, 98.1%id,  0.0%wa,  0.6%hi,  0.6%si,  0.0%st
Cpu1  :  0.0%us,  0.0%sy,100.0%ni,  0.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Mem:    906184k total,   880312k used,    25872k free,    98940k buffers
Swap:  1048568k total,       52k used,  1048516k free,   617392k cached

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
9763 root      26   1 11244 6148 2756 R  100  0.7   0:08.64 summary_ipcad.p
    1 root      15   0  2036  656  564 S    0  0.1   0:00.83 init

Как видно, система (проц) загружен на 50%. Причем загружено только одно ядро.
Логично предположить, что запустив второй инстанс скрипта, система загрузиться полностью и станет работать более эффективно (соседнее ядро проца не будет простаивать). Почти так и происходит:
top - 14:58:39 up 24 days, 16:10,  2 users,  load average: 0.94, 0.69, 0.78
Tasks:  73 total,   4 running,  69 sleeping,   0 stopped,   0 zombie
Cpu0  :  0.6%us,  0.6%sy, 98.2%ni,  0.0%id,  0.0%wa,  0.0%hi,  0.6%si,  0.0%st
Cpu1  :  0.0%us,  0.0%sy,100.0%ni,  0.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Mem:    906184k total,   884116k used,    22068k free,    97360k buffers
Swap:  1048568k total,       52k used,  1048516k free,   623988k cached

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
10054 root      26   1 10980 5932 2756 R  100  0.7   0:07.69 summary_ipcad.p
10081 root      26   1 14288 6324 2816 R  100  0.7   0:06.29 summary_ipcad.p

Но эффективность мало того, что не вырастает, она уменьшается в несколько раз! Вместо 60 секунд, которые в среднем работал один инстанс, обрабатывая один файл, два инстанса работают дольше 300 секунд!

Объясните, плз, в чем ошибка в моей логике? Или может где-то что-то надо подкрутить?

Заранее спасибо!


Содержание

Сообщения в этом обсуждении
"загрузка CPU и скорость работы скриптов"
Отправлено v.i.t , 27-Сен-08 15:57 
вероятнее в вашей задаче основное время затрачивается на операции I/O (чтения/записи на диск).

"загрузка CPU и скорость работы скриптов"
Отправлено Alexander , 27-Сен-08 19:08 
>вероятнее в вашей задаче основное время затрачивается на операции I/O (чтения/записи на
>диск).

как видно из приведенного top'а - i/o wait минимален.


"загрузка CPU и скорость работы скриптов"
Отправлено angra , 27-Сен-08 15:59 
Так как текст скрипта вы не предоставили, то остается только гадать. И самое логичное предположить, что оба экземеляра обращаются к одним и тем же файлам, как следствие только мешают друг другу.

"загрузка CPU и скорость работы скриптов"
Отправлено Alexander , 27-Сен-08 19:12 
>Так как текст скрипта вы не предоставили, то остается только гадать. И
>самое логичное предположить, что оба экземеляра обращаются к одним и тем
>же файлам, как следствие только мешают друг другу.

текст я привести не могу, сорри.
к сожалению, Ваше предположение не верно, т.к. каждый инстанс работает со своим входным файлом. размеры файлов - обычно несколько мегабайт. скрипт его читает, сортирует и дальше отбирает из него только информацию о нужных подсетях.
как я уже написал выше - iowait практически нулевой. так что дело не в этом.


"загрузка CPU и скорость работы скриптов"
Отправлено vic , 29-Сен-08 20:07 
>[оверквотинг удален]
>>самое логичное предположить, что оба экземеляра обращаются к одним и тем
>>же файлам, как следствие только мешают друг другу.
>
>текст я привести не могу, сорри.
>к сожалению, Ваше предположение не верно, т.к. каждый инстанс работает со своим
>входным файлом. размеры файлов - обычно несколько мегабайт. скрипт его читает,
>сортирует и дальше отбирает из него только информацию о нужных подсетях.
>
>как я уже написал выше - iowait практически нулевой. так что дело
>не в этом.

60 сек на несколько мегобайт ради того чтобы вытащить подсети? это само по себе как-то долго.


"загрузка CPU и скорость работы скриптов"
Отправлено Alexander , 29-Сен-08 20:32 
>>как я уже написал выше - iowait практически нулевой. так что дело
>>не в этом.
>
>60 сек на несколько мегобайт ради того чтобы вытащить подсети? это само
>по себе как-то долго.

это очень сильно зависит от кол-ва подсетей.


"загрузка CPU и скорость работы скриптов"
Отправлено vic , 29-Сен-08 20:40 
>>>как я уже написал выше - iowait практически нулевой. так что дело
>>>не в этом.
>>
>>60 сек на несколько мегобайт ради того чтобы вытащить подсети? это само
>>по себе как-то долго.
>
>это очень сильно зависит от кол-ва подсетей.

Тогда вызывайте дэвида блейна =)


"загрузка CPU и скорость работы скриптов"
Отправлено Аноним , 29-Сен-08 21:19 
>>60 сек на несколько мегобайт ради того чтобы вытащить подсети? это само по себе как-то долго.
>это очень сильно зависит от кол-ва подсетей.

Ну о чем и речь! А не должно зависеть! То есть должно зависеть только от размера файла.



"загрузка CPU и скорость работы скриптов"
Отправлено Аноним , 29-Сен-08 21:25 
Это я к тому что всё таки где то инстансы лочат друг друга. Если не на входном файле - то на выходном ... без сырков трудно судить - ищи сам.
Кстати про i\o wait чего то у тебя не так, ты как его измерял?



"загрузка CPU и скорость работы скриптов"
Отправлено angra , 29-Сен-08 21:45 
Есть предположение что автор не видит разницы в iowait процессора, выдаваемое top, нагрузке на io, которую можно получить из iostat, и ситуацией взаимных локов, которые вообще не относятся к io.
Также серьезной проблемой является сам процессор, P4 с HT конечно притворяется двухядерником, но де-факто таковым не является и если что-то умудряется загрузить одно псевдо-ядро полностью, то от запуска еще одного экземпляра становится только хуже, причем иногда значительно.

"загрузка CPU и скорость работы скриптов"
Отправлено guest , 30-Сен-08 11:38 
>Есть предположение что автор не видит разницы в iowait процессора, выдаваемое top,
>нагрузке на io, которую можно получить из iostat, и ситуацией взаимных
>локов, которые вообще не относятся к io.

Как раз в этом автор разницу видит.

>Также серьезной проблемой является сам процессор, P4 с HT конечно притворяется двухядерником,
>но де-факто таковым не является и если что-то умудряется загрузить одно
>псевдо-ядро полностью, то от запуска еще одного экземпляра становится только хуже,
>причем иногда значительно.

А вот эта причина выглядит наиболее правдоподобной. Спасибо!


"загрузка CPU и скорость работы скриптов"
Отправлено guest , 30-Сен-08 11:36 
>>это очень сильно зависит от кол-ва подсетей.
>
>Ну о чем и речь! А не должно зависеть! То есть должно
>зависеть только от размера файла.

да, это в идеале. но пока не очень понимаю, как к этому придти.


"загрузка CPU и скорость работы скриптов"
Отправлено Alexander , 30-Сен-08 11:39 
сорри, guest - это я

"загрузка CPU и скорость работы скриптов"
Отправлено angra , 30-Сен-08 14:54 
Ну так покажите скрипт, поможем :)
Общий принцип работы в подобных ситуациях:
1. Формируем хеш из того, с чем надо проводить сравнение
2. Считываем данные построчно из каждой строки выкусываем регексами нужные куски
3. Проверяем куски на принадлежность хешу, для счетчиков опять таки используем хеши
4. Выводим результат
Как легко заметить скорость будет зависеть только от объема файла с данными и зачастую будет лишь незначительно превышать время нужное на io


"загрузка CPU и скорость работы скриптов"
Отправлено Alexander , 30-Сен-08 15:18 
>Ну так покажите скрипт, поможем :)

ну, давайте попробуем, спасибо :) приведу основной кусок. исходные:
трафик делится на две категории: внешний и локальный
@nonlocalhosts - ip-адреса хостов, трафик на которые считается внешним
@local_nets_dec - ip-адреса локальных подсетей в десятичном представлении, префиксы, маски локальных подсетей в десятичном представлении (соответствующие префиксам)
@client_nets_dec - тоже самое, но только клиентские подсети. плюс - id клиента
@pre_mul_256_... - массивы для быстрого перевода ip-адресов из октетного вида в десятичный

while ( <IN> ) {
  next if (/^$/);
  ( $ip1, $ip2, $bytes1, $bytes2 ) = split;

  $H1proxy = grep $ip1 eq $_, @nonlocalhosts;
  $H2proxy = grep $ip2 eq $_, @nonlocalhosts;

  # какой из хостов принадлежит нашим сетям?
  $H1loc = 0; $H2loc = 0;
  @ips = split(/\./, $ip1);
  #-- convert ips to dec
  $ip1_dec = $pre_mul_256_3[$ips[0]] + $pre_mul_256_2[$ips[1]] + $pre_mul_256_1[$ips[2]] + $ips[3];
  @ips = split(/\./, $ip2);
  #-- convert ip to dec
  $ip2_dec = $pre_mul_256_3[$ips[0]] + $pre_mul_256_2[$ips[1]] + $pre_mul_256_1[$ips[2]] + $ips[3];

  foreach (@local_nets_dec) {
    ($net_dec, $prefix, $mask_dec) = @{$_};
    #-- apply mask
    my $ip1_net = $ip1_dec & $mask_dec;
    my $ip2_net = $ip2_dec & $mask_dec;
    $H1loc = 1 if ($ip1_net == $net_dec && !$H1proxy && !$H1loc);
    $H2loc = 1 if ($ip2_net == $net_dec && !$H2proxy && !$H2loc);
    #-- exit from the loop if we made all checks
    last if ($H1loc && $H2loc);
  }

#-- вот это самый долгий кусок
  foreach (@client_nets_dec) {
    ($net_dec, $prefix, $mask_dec, $cid) = @{$_};
    $ip1_net = $ip1_dec & $mask_dec;
    $ip2_net = $ip2_dec & $mask_dec;
    #-- хост1 - клиент?
    $C1 = ($ip1_net == $net_dec);
    #-- хост2 - клиент?
    $C2 = ($ip2_net == $net_dec);
    unless( $C1 or $C2 ) {  next; } # оба хоста не принадлежат %client_nets

    if( $C1 and $C2 ) { # оба хоста принадлежат %client_nets -> лок трафик
      if( ($H1loc and $H2loc) || ($H1loc and $H2proxy) || ($H1proxy and $H2loc) ) { # и входят в лок сеть
        $bytes_in_loc{$cid}  += $bytes1;
        $bytes_out_loc{$cid} += $bytes2;
        next;
      }
    }

    if( !$C1 and $C2 ) { #$ip1 не принадлежат сети $net, $ip2 принадлежат сети $net
      if( (!$H1loc and $H2loc) || (!$H1loc and $H2proxy) ) { # $ip1 внешний хост -> внеш трафик
        $bytes_in{$cid}  += $bytes2; # переворачиваем
        $bytes_out{$cid} += $bytes1;
        next;
      }
      if( ($H1loc and $H2loc) || ($H1loc and $H2proxy) || ($H1proxy and $H2loc) ) { # $ip1 наш хост -> лок трафик
        $bytes_in_loc{$cid}  += $bytes2; # переворачиваем
        $bytes_out_loc{$cid} += $bytes1;
        next;
      }
    }

    if( $C1 and !$C2 ) { #$ip1 принадлежат сети $net, $ip2 не принадлежат сети $net
      if( ($H1loc and !$H2loc) || ($H1proxy and !$H2loc) ) { # $ip2 внешний хост -> внеш трафик
        $bytes_in{$cid}  += $bytes1;
        $bytes_out{$cid} += $bytes2;
        next;
      }
      if( ($H1loc and $H2loc) || ($H1loc and $H2proxy) || ($H1proxy and $H2loc) ) { # $ip2 наш хост -> лок трафик
        $bytes_in_loc{$cid}  += $bytes1;
        $bytes_out_loc{$cid} += $bytes2;
        next;
      }
    }
  }
}


"загрузка CPU и скорость работы скриптов"
Отправлено angra , 30-Сен-08 16:28 
Ну как и предполагалось - использование массивов вместо хешей, как следствие полный проход по всем значениям вместо индексов. Однако оптимизация не совсем тривиальна из-за того, что маска различается, нужно учитывать внешние условия. Сколько у вас елементов в local_nets_dec, в client_nets_dec и сколько возможных масок(понятно, что всего 32, но сколько реально используется у вас и какие именно)?
Ну а пока тривиальная оптимизация:
$H1proxy = grep $ip1 eq $_, @nonlocalhosts;
До основного цикла создаем хеш
my %nonlocalhosts;
map {$nonlocalhosts{$_}=1} @nonlocalhosts;
или так
my %nonlocalhosts = map {$_,1} @nonlocalhosts;
После чего проверка будет осуществятся следующим образом:
$H1proxy = $nonlocalhosts{$ip1}

"загрузка CPU и скорость работы скриптов"
Отправлено Alexander , 30-Сен-08 17:31 
>из-за того, что маска различается, нужно учитывать внешние условия. Сколько у
>вас елементов в local_nets_dec, в client_nets_dec и сколько возможных масок(понятно, что
>всего 32, но сколько реально используется у вас и какие именно)?

DB<8> p$#local_nets_dec
12
                                                                                                                                DB<9> p$#client_nets_dec
1047

Префиксы в настоящий момент такие:
32
31
30
29
28
27
26
25
24
23
20
19
18
17
16

>До основного цикла создаем хеш
>my %nonlocalhosts;
>map {$nonlocalhosts{$_}=1} @nonlocalhosts;
>После чего проверка будет осуществятся следующим образом:
>$H1proxy = $nonlocalhosts{$ip1}

сделал. скорость не выросла совсем: было 29 сек, стало 30 сек.


"загрузка CPU и скорость работы скриптов"
Отправлено angra , 30-Сен-08 18:10 
>сделал. скорость не выросла совсем: было 29 сек, стало 30 сек.

Ну так ведь это был не самый важный участок :).

Итого имеем 15 масок. а значит на каждый ip нам нужно сделать всего 15 операций выделения сетевой части. Дальше будет цикл на эти пятнадцать сетей полученных из ip с поиском по хешу сформированному из client_nets_dec. У вас же было 1047 операций по выделению сети и сравнению. Замечу что при увеличении количества сетей время будет расти логарифмически, у вас же оно растет линейно.

Сейчас нет времени переделать ваш скрипт, но идею думаю вы поняли, попробуйте сами сделать.



"загрузка CPU и скорость работы скриптов"
Отправлено Alexander , 30-Сен-08 18:48 
>Итого имеем 15 масок. а значит на каждый ip нам нужно сделать
>всего 15 операций выделения сетевой части. Дальше будет цикл на эти
>пятнадцать сетей полученных из ip с поиском по хешу сформированному из
>client_nets_dec. У вас же было 1047 операций по выделению сети и
>сравнению.

Что-то я все-таки не очень понял. 15 масок - это только уникальные, причем только среди префиксов. Всего же - 1048 сетей.

Чтобы определить, какому (каким) клиенту (клиентам - т.к. 1 подсеть может принадлежать сразу нескольким клиентам) принадлежат ip1 и ip2, мне надо сравнивать эти ip с подсетью каждого клиента, т.е. всего - 1048 сравнений (ну, 2 по 1048).


"загрузка CPU и скорость работы скриптов"
Отправлено angra , 02-Окт-08 04:37 
Для начала преобразуем @client_nets_dec в хеш, разумеется до основного цикла
foreach (@client_nets_dec) {
  ($net_dec, $prefix, $mask_dec, $cid) = @{$_};
  $client_nets_mask{$mask_dec}{$net_dec}=$cid;
}
Дальше в самом цикле меняем ваш foreach (@client_nets_dec)  на следущий
foreach my $mask_dec (keys %client_nets_mask){
  $ip1_net = $ip1_dec & $mask_dec;
  $C1 = $client_nets_mask{$mask_dec}{$ip1_net};
  ...
В $С1 или пустота в случае если ip не принадлежит ни одной подсети данной размерности либо соответствующий cid. Аналогично для второго ip. В результате мы получим что количество проходов зависит не от размера @client_nets_dec, а от количества разных масок в нем, а у вас их всего 15.
Для local_nets_dec подобное смысла не имеет.

Если у вас несколько клиентов могут иметь одну и ту же сеть, что несколько странно, то используем arrayref для значения:
$client_nets_mask{$mask_dec}{$net_dec}=$cid;
заменяем на
push @{client_nets_mask{$mask_dec}{$net_dec}},$cid;
и соответственно в $C1 будем иметь нужный нам arrayref, по которому надо будет пройтись в foreach или map


"загрузка CPU и скорость работы скриптов"
Отправлено Alexander , 02-Окт-08 14:51 
>Если у вас несколько клиентов могут иметь одну и ту же сеть,
>что несколько странно, то используем arrayref для значения:

Не подсеть одну, а маску. В общем, теперь все понятно :)

Сделал, короче. Поразительные результаты. Даже сложно определить во сколько раз выросла скорость :)
Было - 64 секунды, стало - 1 (одна!)

В общем, спасибо большое!