[SP-pm] WWW::Curl::Multi

Nilson Santos Figueiredo Jr. acid06 at gmail.com
Sun Sep 6 10:30:46 PDT 2009


2009/9/6 Luis Motta Campos <luismottacampos at yahoo.co.uk>:
> Tradicionalmente o mundo unix não emprega threads para trabalhar. É prática
> comum usar processos e fork() para servir múltiplos clientes ao mesmo tempo.

É uma tradição que tem se modificado com os tempos. Programas mais
recentes, principalmente para um ambiente desktop Linux, tendem a
utilizar vários processos para tarefas diferentes, mas threads dentros
de um mesmo programa.
Exemplos disso são quaisquer aplicações para KDE. O motivo disso é que
com threads "leves" a comunicação entre os threads é bem mais barata e
simples que entre processos, além da troca de contexto ser bem mais
barata (além do fato da diferença entre o overhead de um processo vs.
thread ter se tornado bem mais notável nos últimos anos, com os
threads gastando bem menos recursos).

Claro que a maioria desses pontos não se aplica aos threads de Perl
(exceto IPC facilitado), já que não são threads "leves".

> Não, não vem com babelas sobre performance: se você desenhar o seu serviço
> direito, tanto faz. Por exemplo, o Apache é o mais tradicional servidor
> fork-based disponível, mas muitos outros produtos open-source também o são.

Infelizmente, acredito que você não está ciente de alguns
desenvolvimentos mais recentes (últimos 3 ou 4 anos) do Apache.

Hoje em dia, o mpm_worker é bastante utilizado, que faz um híbrido
entre a utilização de multi-processos e multi-threads. Esse mpm
fornece uma latência menor e, de acordo com as características da
carga de um webserver (e do próprio webserver) pode oferecer um
throughput bem maior (até 2x), mas também pode ser ligeiramente menor
(até 20%).

Outros webservers mais modernos, como o lighttpd utilizam threads. Já
o nginx utiliza processos e também é moderno, então a situação atual é
que não existe um consenso - o que pode ser visto como um ganho de
aceitação dos threads, já que antigamente o consenso eram processos.

> O problema não está nos threads, mas no desenho da linguagem e na abordagem
> que a gente usa para programar módulos.
>
> Nem todos os módulos Perl são thread-safe. Assim, ao invés de se impôr uma
> limitação tão grande para começar, eu recomendo fortemente que ninguém use
> threads em sistemas de produção.

Todos os módulos pure-Perl são tão thread-safe quanto são "fork-safe"
- só vai dar problemas com filehandles, sockets, etc. Assim como em um
fork.
Para o módulo não ser thread-safe, tem de ser alguma extensão XS.
Código em C sempre é marcado como thread-safe ou não thread-safe.
Todas as bibliotecas *modernas* costumam ser thread-safe, mas alguns
dinossauros como o ImageMagick não são (na verdade, ImageMagick não
"nada-safe" - um dos principais problemas que motivou a migração de
mod_perl pra FastCGI pra muita gente foi o fato de que o ImageMagick
adorava dar pau e com mod_perl, o webserver inteiro caía junto).

> Bom, aí tem mais um motivo para preferir threads a processos em Perl:
> threads são tão pesados quanto processos, mas mais difíceis de usar e
> manejar.

Eles são quase tão pesados quanto processos. Eu realmente acredito que
você nunca deve ter utilizado threads em Perl. Como eles são quase tão
pesados quanto processos, a única *vantagem* é que são mais *fáceis*
de manejar e usar.
Com threads você pode ter variáveis compartilhadas entre processos da
maneira mais eficiente possível em Perl, por exemplo. Ao invés de ter
que depender de sinais, você tem uma API bem definida, fácil de usar
com a vantagem de tudo ser cross-platform, ao contrário de sinais.

> Bom, baseado no que você me disse, parece que a implementação atual de
> threads é altamente experimental. Com a minha experiência em Unix, eu
> prefiro normalmente inciar novos processos ao invés de usar Threads.

O suporte a threads em Perl 5.8.x é production-ready há bastante
tempo. Se você gosta de insistir em tecnologias ultrapassadas, tudo
bem. Tem gente que também prefere continuar utilizar CGI puro ao invés
de Catalyst, DBI na mão ao invés de DBIx::Class, etc.

Mas acho que melhor que qualquer discussão, é um exemplo de código.

Abaixo está um programa que faz download de N páginas web em paralelo
e retorna o conteúdo das N páginas concanetado para o programa
principal.
O programa principal imprime o tamanho total em bytes das páginas
concatenadas. O exemplo é simples e inútil, mas o ponto é que isso é a
base para um map/reduce.

Eu te desafio a fazer uma solução mais simples, em Perl, utilizando fork.

  use warnings;
  use strict;

  use subs::parallel;
  use LWP::Simple;

  my @urls = qw[
      http://search.cpan.org/~nilsonsfj/subs-parallel-0.08/lib/subs/parallel.pm
      http://search.cpan.org/~jdhedden/threads-1.74/threads.pm
      http://search.cpan.org/~jdhedden/threads-shared-1.31/shared.pm
      http://search.cpan.org/~dapm/perl-5.10.1/pod/perlthrtut.pod
  ];

  my $result = join "\n", map { parallelize { get $_ } } @urls;

  print length $result;

(note que o código mesmo são duas linhas, o resto são as URLs e
utilização de módulos/pragmas)

-Nilson Santos F. Jr.


More information about the SaoPaulo-pm mailing list