4 May 2007
Benchmarking PHP Threads
All threads can now both hear and report on conditions. A problem with the original send/recieve mechanism made threads hang the controlling thread while long operations were occurring.
I’ve spend the first part of my Friday evening getting send/receive communication working with threads of all kinds. If you were able to get the HVAC example working, you saw an object in continuous execution which was able to report back on demand. However, once that code went out, I started playing with factorials and saw that while a factorial was being calculated, the thread would not respond at all, because the tell method required a response. With PHP being singlethreaded, the thread could not reply while calculating. That wouldn’t do.
I’ve made various changes, including setting up a isResponding function for the thread class, which sends a ping call to threads. If it doesn’t recieve an “ok” within 300ms, it will fail. This doesn’t mean anything is wrong, just that the thread is not currently available for communication.
Also, the idea of stacking multiple commands is gone. Multiple commands can still be piped into a thread on a single tell message, however, only the first will be executed. The rest will appear as parameters.
The parameter passing syntax has been cleared up. It’s still functionally the same, but now you can call $thread->tell("set temp", array(50)); Each array element constitutes a parameter.
A new function, tellAndWaitForResponse replaces the old tell. It will not return until the thread offers some response (though, err is considered acceptable).
All and all, it’s now more natural to use, and therefore, easier to learn. For the first time, it was usable to compare sequential logic with multithreaded logic.
Multithreaded
[Thread ‘fact1′] 31! = 1346269 (under 25 sec)
[Thread ‘fact2′] 28! = 317811 (under 7 sec)
[Thread ‘fact3′] 22! = 17711 (under 1 sec)
[Thread ‘fact4′] 30! = 832040 (under 16 sec)
[Thread ‘fact5′] 29! = 514229 (under 10 sec)
Done, took 26 sec to setup, run and close 5 threads
This number includes the time it takes to parse code on threads. The individual thread times do not.
Sequential
time php Factor.php < synced
ok
YTozOntzOjE6IngiO3M6MjoiMzEiO3M6MzoidmFsIjtpOjEzNDYyNjk7czozOiJzZWMiO2k6OTt9
ok
YTozOntzOjE6IngiO3M6MjoiMjgiO3M6MzoidmFsIjtpOjMxNzgxMTtzOjM6InNlYyI7aToyO30=
ok
YTozOntzOjE6IngiO3M6MjoiMjIiO3M6MzoidmFsIjtpOjE3NzExO3M6Mzoic2VjIjtpOjA7fQ==
ok
YTozOntzOjE6IngiO3M6MjoiMzAiO3M6MzoidmFsIjtpOjgzMjA0MDtzOjM6InNlYyI7aTo1O30=
ok
YTozOntzOjE6IngiO3M6MjoiMjkiO3M6MzoidmFsIjtpOjUxNDIyOTtzOjM6InNlYyI7aTozO30=
real 0m19.333s
user 0m19.033s
sys 0m0.100s
Results Commentary
Factorials of 31, 28, 32, 30, and 29 were calculated.
The file synched simply contains the same parameters which are passed in the multithreaded example. It can obviously forgo checking for completion and responsiveness. This amounted to saving 6 seconds compared to using the multithreaded approach.
This suggests that a great penalty is incurred not by re-parsing the code (notice that 31! took less than 2 seconds fewer than execution took) but by the continuous pinging for quick-responding routines. I therefore ran it again, this time using 31, 25, 33, 34 and 30. This time, the single threaded approach took 2:13 to execute, while the multi-threaded approach took 2:50.
Clearly, ignorantly checking for responsiveness every second was causing great penaltys. I implemented a trivial optimization, where results are saved upon completion and than the whole accessible/responding/retrieving cycle could be forgone once finally calculated. That is the code you will see in the download, and then execution time fell to 86 seconds, which is now 47 seconds faster than linear execution.
For quick operations, linear execution is best. I’ve not benchmarked to find where the cutoff is, but I’m sure that multithreading’s overhead will defeat it for at least some operations.
Download: threads.tgz
Read more: Part 1 - Multithreading strategies in PHP | Part 2 - Communicating with threads in PHP


very interesting, this could really help mine PHP prime spiral generator by dividing the image into parts and then get them back together.. brilliant..
btw, does socket_select() function helps? because then the sub threads can notifying the main thread instead of let the main thread calculate how long it is going to take and check on them.