in reply to Why use threads over processes, or why use processes over threads?

I did some benchmarking of forking vs threading. Below is a program that compares the running times of programs that fork and that use threads. Two programs are compared: first a program where the children don't do much (exit for the fork, empty sub for the threads). In the second program, the children print a single character to /dev/null.
#!/usr/bin/perl use strict; use warnings; my $perl = "/opt/perl/bin/perl"; my $thrperl = "/opt/perl/bin/thrperl"; my $action = << '--'; open my $fh => "> /dev/null" or die "open: $!"; print $fh "."; close $fh; -- my $prog = << '--'; use strict; use warnings; my $times = shift; my @pids; foreach (1 .. $times) { defined (my $pid = fork) or die "fork: $!"; unless ($pid) { ACTION; exit; } push @pids => $pid; } map {waitpid $_ => 0} @pids; -- my $thrprog = << '--'; use strict; use warnings; use threads; my $times = shift; my @threads; foreach (1 .. $times) { my $thread = threads -> create (sub {ACTION}) or die "threads: $!" +; push @threads => $thread; } map {$_ -> join} @threads; -- my @amounts = (0, 5, 10, 25, 50, 100, 250, 500, 1000, 2500); print STDERR "Empty childs\n"; (my $t_prog = $prog) =~ s/ACTION//; (my $t_thrprog = $thrprog) =~ s/ACTION//; for my $amount (@amounts) { my $format = sprintf "%5d: %%U user, %%S system, %%E elapsed" => $ +amount; system time => '-f', "(F) $format", $perl, '-e', $t_prog, $a +mount; system time => '-f', "(T) $format", $thrperl, '-e', $t_thrprog, $a +mount; } print STDERR "\nChilds writing\n"; ($t_prog = $prog) =~ s/ACTION/$action/; ($t_thrprog = $thrprog) =~ s/ACTION/$action/; for my $amount (@amounts) { my $format = sprintf "%5d: %%U user, %%S system, %%E elapsed" => $ +amount; system time => '-f', "(F) $format", $perl, '-e', $t_prog, $a +mount; system time => '-f', "(T) $format", $thrperl, '-e', $t_thrprog, $a +mount; } __END__ Empty childs (F) 0: 0.01 user, 0.00 system, 0:00.01 elapsed (T) 0: 0.04 user, 0.00 system, 0:00.03 elapsed (F) 5: 0.01 user, 0.00 system, 0:00.01 elapsed (T) 5: 0.09 user, 0.00 system, 0:00.09 elapsed (F) 10: 0.01 user, 0.01 system, 0:00.02 elapsed (T) 10: 0.16 user, 0.00 system, 0:00.15 elapsed (F) 25: 0.01 user, 0.02 system, 0:00.03 elapsed (T) 25: 0.32 user, 0.02 system, 0:00.34 elapsed (F) 50: 0.03 user, 0.04 system, 0:00.06 elapsed (T) 50: 0.60 user, 0.08 system, 0:00.70 elapsed (F) 100: 0.04 user, 0.08 system, 0:00.11 elapsed (T) 100: 1.25 user, 0.11 system, 0:01.42 elapsed (F) 250: 0.04 user, 0.12 system, 0:00.50 elapsed (T) 250: 3.41 user, 0.24 system, 0:03.87 elapsed (F) 500: 0.02 user, 0.14 system, 0:01.16 elapsed (T) 500: 7.37 user, 0.55 system, 0:08.42 elapsed (F) 1000: 0.05 user, 0.19 system, 0:02.49 elapsed A thread exited while 46 threads were running. (T) 1000: 15.02 user, 1.36 system, 0:17.42 elapsed (F) 2500: 0.04 user, 0.71 system, 0:06.73 elapsed Command terminated by signal 9 (T) 2500: 37.39 user, 3.30 system, 2:10.56 elapsed Childs writing (F) 0: 0.06 user, 0.21 system, 0:01.60 elapsed (T) 0: 0.20 user, 0.23 system, 0:01.66 elapsed (F) 5: 0.01 user, 0.01 system, 0:00.03 elapsed (T) 5: 0.12 user, 0.02 system, 0:00.28 elapsed (F) 10: 0.03 user, 0.00 system, 0:00.07 elapsed (T) 10: 0.17 user, 0.04 system, 0:00.37 elapsed (F) 25: 0.02 user, 0.06 system, 0:00.08 elapsed (T) 25: 0.49 user, 0.09 system, 0:01.07 elapsed (F) 50: 0.04 user, 0.21 system, 0:00.46 elapsed (T) 50: 0.73 user, 0.12 system, 0:01.72 elapsed (F) 100: 0.04 user, 0.11 system, 0:00.14 elapsed (T) 100: 1.22 user, 0.06 system, 0:01.29 elapsed (F) 250: 0.08 user, 0.12 system, 0:00.52 elapsed (T) 250: 2.93 user, 0.30 system, 0:04.29 elapsed (F) 500: 0.06 user, 0.20 system, 0:01.30 elapsed (T) 500: 6.29 user, 0.67 system, 0:07.79 elapsed (F) 1000: 0.03 user, 0.24 system, 0:02.50 elapsed A thread exited while 46 threads were running. (T) 1000: 11.52 user, 1.46 system, 0:13.84 elapsed (F) 2500: 0.06 user, 0.72 system, 0:06.78 elapsed Command terminated by signal 9 (T) 2500: 26.72 user, 13.98 system, 3:57.20 elapsed

As we can see, forking is much faster than creating threads. Furthermore, creating more than a thousand processes is not a problem - creating a thousand threads *is*. And what you didn't see was the slow response of my system when the threads were being handled, and neither did you hear the grinding of the disk when dealing with threads (2500 processes however was hardly noticeable).

Abigail

  • Comment on Re: Why use threads over processes, or why use processes over threads?
  • Download Code

Replies are listed 'Best First'.
Re: Re: Why use threads over processes, or why use processes over threads?
by mcogan1966 (Monk) on Nov 12, 2003 at 14:31 UTC
    Excellent point made here.

    And it shows a key element of this entire debate. The decision to fork or to thread should be based on need, form and function of the code. In some cases, it's is clear that fork is the better solution, others it is thread. A good programmer / developer will try to have both skills under their belt (though I'm not quite a that level, yet).

    Another consideration, though, is the fact that not all systems have Perl threading available. Some systems do not have a version of Perl installed that allows threads, and may not be able to install an outside module that will allow it (like the place where I work). I know that will sound unusual, but consider the fact that some places hold a VERY tight control over what is installed from an outside source. If it is a secure (i.e. DoD, NSA, etc.), the site may not allow additions to the system that way becaused they are not 'blessed' by certain security-minded individuals.

    All in all, sometimes the 'better' choice isn't an option. Best to have all the tools you can available in your own head.

Re: Re: Why use threads over processes, or why use processes over threads?
by pg (Canon) on Nov 11, 2003 at 18:08 UTC

    Can you do us a favor, modify your thread program a little bit, and get another set of number (under the same testing environment)?

    My suggested modification is to detach threads when they get created, so you explicitly tell Perl that, no join will be done later, and Perl doesn't need waste time/space to keep return structures. (Trust this situation will be changed in future versions.)

    threads->create(\&blah)->detach();

    I really appreciate it.

      Well, I gave you the source, so you could have easily done this yourself. Now I had to first compile a threaded Perl (as I'm on a different machine now). Anyway, I modified the program so it does it all four times: fork and wait, fork with SIGCHLD being ignored, joining threads, and detachted threads, with labels Fw, Fi, Tj and Td. Not surprisingly, there's not much difference between Fw and Fi, and between Tj and Td.
      Empty childs (Fw) 0: 0.01 user, 0.01 system, 0:00.02 elapsed (Fi) 0: 0.01 user, 0.00 system, 0:00.01 elapsed (Tj) 0: 0.05 user, 0.00 system, 0:00.06 elapsed (Td) 0: 0.05 user, 0.00 system, 0:00.06 elapsed (Fw) 5: 0.01 user, 0.00 system, 0:00.01 elapsed (Fi) 5: 0.02 user, 0.00 system, 0:00.02 elapsed (Tj) 5: 0.11 user, 0.02 system, 0:00.14 elapsed (Td) 5: 0.12 user, 0.00 system, 0:00.14 elapsed (Fw) 10: 0.01 user, 0.00 system, 0:00.03 elapsed (Fi) 10: 0.01 user, 0.00 system, 0:00.03 elapsed (Tj) 10: 0.20 user, 0.00 system, 0:00.24 elapsed (Td) 10: 0.21 user, 0.00 system, 0:00.24 elapsed (Fw) 25: 0.00 user, 0.06 system, 0:00.10 elapsed (Fi) 25: 0.01 user, 0.01 system, 0:00.09 elapsed (Tj) 25: 0.45 user, 0.47 system, 0:01.32 elapsed (Td) 25: 0.42 user, 0.04 system, 0:00.58 elapsed (Fw) 50: 0.01 user, 0.00 system, 0:00.11 elapsed (Fi) 50: 0.01 user, 0.00 system, 0:00.16 elapsed (Tj) 50: 0.86 user, 0.01 system, 0:01.07 elapsed (Td) 50: 0.79 user, 0.07 system, 0:01.00 elapsed (Fw) 100: 0.02 user, 0.00 system, 0:00.21 elapsed (Fi) 100: 0.01 user, 0.00 system, 0:00.35 elapsed (Tj) 100: 1.60 user, 0.12 system, 0:01.99 elapsed (Td) 100: 1.62 user, 0.08 system, 0:01.99 elapsed (Fw) 250: 0.01 user, 0.00 system, 0:00.50 elapsed (Fi) 250: 0.02 user, 0.00 system, 0:00.85 elapsed (Tj) 250: 4.20 user, 0.23 system, 0:05.18 elapsed (Td) 250: 4.28 user, 0.20 system, 0:05.22 elapsed (Fw) 500: 0.02 user, 0.00 system, 0:01.01 elapsed (Fi) 500: 0.02 user, 0.04 system, 0:01.73 elapsed (Tj) 500: 9.09 user, 0.56 system, 0:11.28 elapsed (Td) 500: 9.19 user, 0.46 system, 0:11.25 elapsed (Fw) 1000: 0.01 user, 0.01 system, 0:02.02 elapsed (Fi) 1000: 0.02 user, 0.12 system, 0:03.47 elapsed A thread exited while 48 threads were running. (Tj) 1000: 17.54 user, 14.73 system, 3:07.04 elapsed (Td) 1000: 22.57 user, 23.33 system, 4:38.73 elapsed Childs writing (Fw) 0: 0.03 user, 0.07 system, 0:01.50 elapsed (Fi) 0: 0.02 user, 0.00 system, 0:00.01 elapsed (Tj) 0: 0.03 user, 0.04 system, 0:00.83 elapsed (Td) 0: 0.05 user, 0.00 system, 0:00.06 elapsed (Fw) 5: 0.02 user, 0.00 system, 0:00.02 elapsed (Fi) 5: 0.01 user, 0.00 system, 0:00.02 elapsed (Tj) 5: 0.12 user, 0.02 system, 0:00.17 elapsed (Td) 5: 0.11 user, 0.02 system, 0:00.14 elapsed (Fw) 10: 0.01 user, 0.01 system, 0:00.03 elapsed (Fi) 10: 0.02 user, 0.00 system, 0:00.04 elapsed (Tj) 10: 0.19 user, 0.01 system, 0:00.23 elapsed (Td) 10: 0.20 user, 0.01 system, 0:00.26 elapsed (Fw) 25: 0.02 user, 0.00 system, 0:00.06 elapsed (Fi) 25: 0.01 user, 0.00 system, 0:00.09 elapsed (Tj) 25: 0.41 user, 0.04 system, 0:00.53 elapsed (Td) 25: 0.45 user, 0.01 system, 0:00.53 elapsed (Fw) 50: 0.01 user, 0.01 system, 0:00.11 elapsed (Fi) 50: 0.02 user, 0.00 system, 0:00.18 elapsed (Tj) 50: 0.81 user, 0.07 system, 0:01.03 elapsed (Td) 50: 0.79 user, 0.07 system, 0:01.01 elapsed (Fw) 100: 0.01 user, 0.00 system, 0:00.21 elapsed (Fi) 100: 0.02 user, 0.00 system, 0:00.34 elapsed (Tj) 100: 1.63 user, 0.11 system, 0:02.05 elapsed (Td) 100: 1.63 user, 0.09 system, 0:01.99 elapsed (Fw) 250: 0.02 user, 0.01 system, 0:00.52 elapsed (Fi) 250: 0.01 user, 0.00 system, 0:00.85 elapsed (Tj) 250: 4.22 user, 0.31 system, 0:05.29 elapsed (Td) 250: 4.28 user, 0.20 system, 0:05.24 elapsed (Fw) 500: 0.02 user, 0.00 system, 0:01.01 elapsed (Fi) 500: 0.01 user, 0.03 system, 0:01.71 elapsed (Tj) 500: 9.38 user, 0.51 system, 0:11.50 elapsed (Td) 500: 9.16 user, 0.54 system, 0:11.28 elapsed (Fw) 1000: 0.01 user, 0.00 system, 0:02.01 elapsed (Fi) 1000: 0.02 user, 0.13 system, 0:03.49 elapsed A thread exited while 51 threads were running. (Tj) 1000: 17.47 user, 15.01 system, 2:34.14 elapsed (Td) 1000: 22.57 user, 23.21 system, 5:01.54 elapsed

      Abigail