[Rio-pm] Ajuda com Threads - tempo de abertura

João André Simioni jasimioni em gmail.com
Sábado Julho 10 08:50:35 PDT 2010


Fiz um teste agora colocando um sleep 10 no inicio da função checkIp.
As threads abrem rápido assim, mas o desempenho ficou ruim. Além
disso, notei que as threads não utilizam os demais processadores.

Minha solução atual está com um fork no começo, quebrando o código em
8 processos distintos (número de processadores), cada processo com um
sub-grupo e criando suas threads (30 threads por grupo).

Obrigado pela ajuda

João André

2010/7/10 João André Simioni <jasimioni em gmail.com>:
> Blabos,
>
> o loop interno não é nem usado nesse código, como observado pelo
> Bruno, porque ele define a quantidade de threads no início. Usualmente
> eu disparo uma thread para cada elemento que vou gerenciar, e defino
> um limite de threads abertas no início do código e é por isso que esse
> loop existe, para controlar o limite de threads (e funciona bem em
> diversos outros scripts que não têm o mesmo problema de desempenho).
>
> Segue os comentários no código:
>
>> #!/usr/bin/perl
>>
>> use strict;
>> use DBI;
>> use Net::Ping;
>> use Time::HiRes qw/usleep tv_interval gettimeofday/;
>> use threads ('yield', 'stack_size' => 32*4096, 'exit' =>
>> 'threads_only', 'stringify');
>>
>> my $host = '192.168.160.179';
>> my $sid  = 'HOMOLOG';
>> my $user = 'mac_user';
>> my $pass = 'mac_user';
>>
>> my $dbh = DBI->connect("dbi:Oracle:host=$host;sid=$sid", $user, $pass,
>> { AutoCommit => 1 });
>>
>> my $sth = $dbh->prepare('SELECT CPE_ID, CLIENTE_ID, CPE_DESC,
>> CPE_IP_WAN, CPE_NODE, CPE_IF FROM MAC_CPE WHERE CPE_PING = ? AND
>> CPE_ATIVO = ?');
>> $sth->execute('1', 'A');
>>
>> my @cpes;
>>
>> while (my (@row) = $sth->fetchrow_array) {
>>     push @cpes, [ @row ];
>> }
>>
>> $dbh->disconnect();
>
> Até aqui somente listei os elementos a partir do banco de dados.
>
> O código abaixo quebra o numero de elementos em 250 threads.
>
>> my $maxThreads = 250;
>> my $total      = @cpes;
>>
>> my $cpePerThread = int($total / $maxThreads);
>>
>> my @toMonitor;
>> my $i = 0;
>> my $c = 0;
>>
>> foreach my $cpe (@cpes) {
>>     push @{$toMonitor[$i]}, $cpe;
>>     $c++;
>>
>>     if ($c > $cpePerThread) {
>>         $i++;
>>         $c = 0;
>>     }
>> }
>>
>> print "Para $total cpes, e $maxThreads threads, tenho no final $i
>> grupos, com ", scalar @{$toMonitor[0]}, " elementos cada\n";
>
>
>
> E imprimo - vou ter o total de elementos dividido em 250 grupos (cada
> grupo atendido por uma thread)
>
>
>
>> sub processResult {
>>     my $r = shift;
>>     if (ref $r ne 'ARRAY') {
>>         print STDERR "Erro na thread\n";
>>         return 0;
>>     }
>>     my ($result, $host, $ifs) = @$r;
>>
>> }
>
> Funcao de processamento do resultado da thread -- aqui não faz nada
>
>> sub testeClient {
>>     my $threadNum = shift;
>>     my $cpeGroup  = shift;
>>     foreach my $cpe (@{$cpeGroup}) {
>>         my ($cpeId, $clientId, $cpeDesc, $cpeIp, $cpeNode, $cpeIf) = @$cpe;
>>         my $pingOk = &checkIp($cpeIp);
>>         my $status = $pingOk ? 'RESPONDE' : 'MORTO';
>>         # print join(", ", $threadNum, $cpeId, $clientId, $cpeDesc,
>> $cpeIp, $cpeNode, $cpeIf, $status), "\n";
>>     }
>>     # print "Finishing thread $threadNum\n";
>>     return([1, 0]);
>> }
>
> Código da thread - basicamente chama a sub 'checkIp' para ver se o
> host está vivo.
>
> Abaixo o código de criação das threads - para cada grupo, cria uma thread.
>
>> my $threadCount = 1;
>> foreach my $cpeGroup (@toMonitor) {
>>     print "Trying to create thread";
>>     my $t0 = [gettimeofday];
>>
>>     print " in ", scalar localtime $t0->[0], ".", $t0->[1], "\n";
>>
>>     my $thr = threads->create({scalar => '1'}, 'testeClient',
>> $threadCount, $cpeGroup);
>>     my $elapsed = tv_interval ( $t0, [gettimeofday]);
>>     print "Created thread $thr ($threadCount) - took $elapsed seconds
>> to create\n";
>>     $threadCount++;
>
> Esse bloco pode ser removido, pois nunca entra - foi a primeira coisa
> que tirei, porque achei que a chamada a threads->list poderia estar
> influenciando. Esse método retorna em menos de 1ms em todas as
> execuções. Mas se preferir só ignora.
>
>>     while (threads->list() >= $maxThreads) {
>>         my @joinable = threads->list(threads::joinable);
>>         for (@joinable) {
>>             my $r = $_->join();
>>             &processResult($r);
>>         }
>>         usleep(10000);
>>     }
>>     my $elapsed = tv_interval ( $t0, [gettimeofday]);
>
> Finaliza a criação de threads
>
>> }
>
> Aguarda as threads concluirem.
>
>> while (threads->list()) {
>>     my @joinable = threads->list(threads::joinable);
>>     for (@joinable) {
>>         my $r = $_->join();
>>         &processResult($r);
>>     }
>>     usleep(10000);
>> }
>
> Fim de execução
>
>> sub checkIp {
>>     my $ip = shift;
>>     my $pingOk = 0;
>
>
> Essa rotina faz 3 testes para cada elemento. Em cada teste, tenta um
> ping ICMP, um UDP e um TCP. Tenho que executar o ping do S.O. porque
> senão sou obrigado a rodar o script como root e o Net::Ping faz
> confusão com o ICMP. Se eu testo um host e ele nao responde no tempo
> de timeout e eu em seguida testar outro host, se o primeiro responder
> ele acha que o segundo respondeu.
>
>>     eval {
>>         for (1..3) {
>>             my $ping = `/bin/ping -c 1 -w 1 $ip`;
>>
>>             if (grep { / 0% packet loss/ } $ping) {
>>                 $pingOk = 1;
>>                 return 1;
>>             }
>>
>>             if ($pingOk == 0) {
>>                 my $p = Net::Ping->new('icmp');
>>                 $p->service_check(1);
>>                 if ($p->ping($ip, 1)) {
>>                     $pingOk = 1;
>>                     return 1;
>>                 }
>>             }
>>
>>             if ($pingOk == 0) {
>>                 my $p = Net::Ping->new('udp');
>>                 $p->service_check(1);
>>                 if ($p->ping($ip, 1)) {
>>                     $pingOk = 1;
>>                     return 1;
>>                 }
>>             }
>>
>>             sleep 3;
>>         }
>>     };
>>
>>     return $pingOk;
>> }
>>
>
> Quanto a usar Java, é que tenho um grupo de desenvolvimento Java do
> meu lado - eu posso solicitar a eles, mas eu não vejo porque Perl não
> pode atender bem nesse caso.
>
> Obrigado
>


Mais detalhes sobre a lista de discussão Rio-pm