Tuesday, 22 September 2015

The Worth of Advice

Fig 1. An extremely confusing sign

I've written on my blog, spoken in my talk, told strangers on the train: You should not use multi-threading at the front end of a web application, it does not make sense when the threading model is 1:1.

Regardless of the fact that I wrote pthreads, and I know very well what is suitable and what is not, my advice is ignored: I get endless bug reports and questions with example code containing HTML line breaks, and reports that Apache or FPM is not behaving itself.

This is no real surprise, why should anyone listen to me ...

You ruddy well will listen to me, this morning I released v3.0.6 of pthreads and it disables the ability to load pthreads anywhere but the CLI.

When I first wrote pthreads, I wanted the barrier to entry to be low. Years later, I can see clearly that was a stupid thing to aim for; The barrier to entry is not low no matter what I do.

I thought it's no big deal if someone wants to use pthreads in their webserver, pthreads uses all the right API's it should be safe, even if it's not a good idea.

I was totally wrong. It's not safe, and never will be.

When I say safe here, I don't necessarily mean safe in the re-entrant, thread safety sense. What I mean is logically sound, which includes safe in the concurrency sense of the word.

Rather than trying to focus on making pthreads accessible, I have switched my focus to making pthreads v3 robust, reliable, predictable. These are the things that are really important when you are writing multi-threaded code, ease of use or accessibility shouldn't even be on the list of things you want a multithreading API to be.

I think, I can claim a modicum of success, even though v3 is not widely used yet.

Code that didn't work before, or behaved strangely, just works now. pthreads is faster, it's simpler internally. It is more accessible and easier to use. But, not because I aimed for those things, but because I aimed for predictability.

All the while, people were still able to ignore probably the most important piece of advice I ever give.

To show that my distain for using threads in a webserver runs deep among all programmers that understand the systems they are using, and is not in any way specific to PHP, pthreads, Apache or FPM:

This response was given to a person asking if it's okay to multithread in a web response inside IIS (C#):

http://stackoverflow.com/a/23915256/1658631

Many such questions can be found on stackoverflow, the best answer isn't always the accepted one. People tend to accept what they wanted to hear.

EJB's expressly prohibit the programmer from creating threads by specification, and modern Java doesn't even have a 1:1 model. Obviously, this is mostly because the environment may be migrated (moved within a cluster of servers), moving threads outside of it's managed pool is difficult to imagine. It goes further than prohibiting the creation of threads though, it prohibits the use of synchronization primitives, because what if a node is moved while you retain a lock - all hell breaks loose.

In Java generally, you are discouraged from manually creating threads in Servlets, for all the same reasons I have discouraged it for pthreads.

While it's true that not every environment will actually prohibit it, it runs as deep as can be; Everyone agrees that creating real (1:1) threads in a web response context is a bad idea.

If you're creating threads inside your multi-process, multi-threaded Apache environment, and you see this as a restriction, you're wrong, just like I was wrong to allow it.

Advice is, quite obviously, only worth something if you are going to listen to it.

Maybe I wasn't ignored, maybe pthreads has a farther reach than the advice that accompanies it, I don't know. It doesn't matter now, pthreads won't work in these environments ...

It's okay to be wrong, it's an opportunity to learn, to do things the correct way ... take the opportunity.

I'm going to go back to code now ...

Tuesday, 15 September 2015

What Polly Really Wants

Fig 1. Definition of polyfill according to Wikipedia
I think that a rather narrow definition, nevertheless it shows the origin of the word to be specifically Javascript and client side development.

Slowly but surely, this has become part of the PHP vernacular, everyone has heard of password_compat. A polyfill for PHP7's random_* API is also available.

I think we know what a polyfill is, or can skim the definition ... because I'm going to keep using that word.

Recently a fellow githubber opened an issue for pthreads, they are writing a static analysis suite for PHP, and are attempting to integrate pthreads into their code. First thing to say is, I don't know where that will lead him. But it does give him the problem that a lot of environments don't have pthreads available, and or they aren't using a thread safe interpreter.

In the issue, he made the suggestion that we have a compatibility layer, a polyfill. I confess, this had never occurred to me before.

Not only does it solve his problem but it actually serves as a useful tool ... I shall explain.


The Unanswerable Question


I'm often asked the question "what is a good use case for threading?". I'll stare open mouthed at the asker, or squint and tilt my head awkwardly in response. In a way it's like being asked the question "what is a good use case for objects?".

I'll go on to try to explain this is some awkward way, never really knowing if I am making sense.

I can't answer that question, it doesn't really make sense. Maybe someone can, but not me.

A question that does make sense is "what kind of code lends itself to threading?". To this we can give a pretty good answer.

First we can make the assumption that your are only considering threading because you have a lot of work to do, if that's not the case, do something else. Assuming there is a lot of work to do, then whether you have the kind of task that could gain anything by using threads depends on the nature of the work. This seems kinda obvious, but if every detail is explained, then more people will understand, I think.

If the kind of work you have to do can be broken down into units of work, each fulfilling the following requirements:

  • does not depend on other units of work
  • does not communicate with other units of work

You can be pretty sure that you have a good candidate.

These basic guidelines are extremely useful. Good multi-threaded code avoids synchronization, because there is too much margin for error and the unexpected, even with the best API in the world. Even with the best API, and the best programmers, synchronization (which must occur for units of work to communicate) diminishes performance, creates contention for locks, and is generally a bad thing.

If you are not dependant on the order of execution, you could (and at some point might have to) write your own scheduler (Pool::submit) to exploit hardware, and your software, to the fullest.

If you know enough to see the cases where those rules can be broken, then you don't need my help and can stop reading, I bid you farewell and good luck.

If you are still reading then I must be making sense, so we will now look at how you can use the polyfill as a tool.


New Rule: Training Shoe Slogans Are Always Wrong!


When it comes to researching multi threaded php, you should not "Just Do it".

In fact, there are very few occasions where such an attitude would be advisable, so, new rule.

If you think you have the kind of code that can take advantage, then you should test the waters. Before you change your PHP installation, or development environments, you should try to write your code using the polyfill.

When you start to win, and you have things the way you want them, you can load pthreads. Save for final optimization for real threads, reducing synchronization, and communication with non Threaded objects, if at this time you do not see a visible benefit, the best advice I can give is go in another direction.

Throwing threads at any task doesn't necessarily make it faster, and if at this early stage in development you are not seeing a visible benefit, then in all likelihood, there isn't any benefit to be had, simple as that.

If you do see a benefit, then you don't need to be worried about deployment problems and dependency on pthreads, if pthreads is available the units of work execute in parallel, if not, they don't. Everything works in a predictable way, everywhere.


Final Thought


My final words should pay homage to the observation that the best ideas aren't always our own, I'm really grateful when fellow programmers try to engage on github, or by any other means. They don't have to do that.

So, thank you ovrweb for taking the time.

Tuesday, 8 September 2015

Addendum: Letter from the Future

Fig 1. Some PHP7 logos
A couple of weeks ago, I wrote about my efforts to port pthreads to PHP7, to make it worthy of the shiny new platform.

Work continued on pthreads, and today I'm going to take the opportunity to update my previous blog post with some correction and extension.

With PHP7 in RC2, I'm sure everyone is a bit tired of blog posts about how quick PHP7 is and why it is so quick, so we're not going into that today. We will be looking at the performance of pthreads in particular.

Corrections

 

Last time I wrote about v3, it was still under development, and while the pthreads test suite passed, complex code of the kind you cannot include in an extension test suite didn't work as expected. I knew about it at the time but we were talking about untagged, unreleased code.

Note that we are still talking about untagged, unreleased code. However, this is only because the route to release is not very clear for PHP7 extensions yet, with no pear/pecl support.

Serialization Hack 

 

I previously wrote that you would be able to serialize Threaded objects like any other in PHP. I have to retract that statement; I overlooked the fact that a serialization hack is required to make Threaded objects behave as expected when they themselves are members of objects which are not Threaded (and so are serialized).

So, the hack got put back in, a bit tidier this time, and is used much less frequently than before, because no need for serialization of Threaded objects when they are members of other Threaded objects.

Note that, Threaded objects are still not serialized, only the serialize/unserialize handlers manipulated to pass around the address of an object, rather than a serial representation of the objects value.

Threaded Iteration

 

This has been improved yet again, we don't waste the memory to copy the set of keys as I previously said we would. Now we just iterate over the store copying keys and values as they are required for iteration, safely, obviously.

This couldn't be more efficient, nothing else to say about that.

Performance

 

It's going to be obvious that a Threaded object has quite some overhead associated with it, in order to provide implicit safety, store in a thread safe manner, and synchronize at the programmers will. What might not be obvious is just how many hoops pthreads has to jump through so that a programmer can manipulate one object in two contexts.

You cannot actually have an object in two contexts in a shared nothing environment, so that's the first thing to observe; There are actually O(n) objects where n is the number of contexts with a reference to the object.

But it gets worse, consider the following code:

<?php
class Test extends Thread {

 public function __construct(Threaded $threaded) {
  $this->member = $threaded;
 }

 public function run() {
  while (1) {
   $this->member->doStuff();
  }
 }
}
?>

Whenever $this->member is accessed in the thread, if you don't know that another context didn't change the reference, you have to undertake an enormous amount of work to provide the new context a reference to $this->member, at worst a new object is constructed every time.

In later versions of pthreads v2 there was a mechanism to reduce the number of constructions but it wasn't wholly effective, it leaked memory, with no chance to plug the leak. So this mechanism was removed from pthreads v3.

This means that not only are there O(n) objects where n is the number of contexts, but they must be constructed O(n) times where n is the number of accesses to the object in another context.

This is obviously extremely slow, and quite scary ...

For normal objects, objects that are not Threaded, we have little choice but to put up with the performance overhead of that. It should normally be the case that objects being used by Threads are Threaded, so this isn't much of a concern.

For Threaded objects though, this is just unacceptable.

Immutability to the Rescue


In pthreads v3, setting a member of a Threaded object (A) to another Threaded object (B) makes the reference that A holds to B immutable.

This means that during execution, once a member is set it cannot be unset or reset by any context, this means that the complexity for access is no longer O(n) but O(1).

Before you go batshit crazy about forcing immutability on the programmer, take a look at some numbers, and read what I have to say about it ...

Consider the following (quite normal, not super CPU intensive) code:

<?php
class My extends Threaded {
 public function method($i) {
  return 1 * $i;
 }
}

class Test extends Thread {
 public function __construct(Threaded $threaded) {
  $this->threaded = $threaded;
 }

 public function run() {
  while (@$i++<1000000) {
   $this->threaded[] = 
    $this->threaded->method($i);
  }
 }
}

$my = new My();
$test = new Test($my);
$test->start();
$test->join();
var_dump(count($my));
?>

Pretty standard stuff, and doesn't look that expensive, but because of the complexity of access in PHP5.6 and pthreads v2, measuring the user instructions and time taken to execute yields the following result:

  Performance counter stats for 'php time.php':

    14,547,244,407      instructions:u          

       2.133205136 seconds time elapsed

Note this is a late version of pthreads v2 with the aforementioned poor mechanism to reduce access complexity.

Same method, same machine, same extensions, PHP7 and pthreads v3 yields:

  Performance counter stats for 'php time.php':

     2,525,791,532      instructions:u          

       0.501464325 seconds time elapsed

Now that is more like it !!

A wise person once said "Never trust a benchmark you didn't fake yourself".

What we are looking at here is not really the time elapsed, for that will vary so much depending on so many things. We are interested in the number of user instructions executed because regardless of how long it takes, this gives you a good idea of how complex some user code is for the CPU.

Not only does immutability reduce the complexity of access considerably, it also allows us to avoid synchronization after the first access to $this->member in the new context. We can do this safely because we already have a reference to the object, constructed (synchronized) on the first access and stored locally to the thread and object, and we know that the reference is immutable.

Immutability breaks backward compatibility, but as mentioned, I don't care about that if I'm breaking something stupid or bad.

It pains me to say it, the amount of effort I put into making pthreads work is completely unreasonable, but it was both bad and stupid.

It may not seem like it initially, but immutability will become your best friend, very quickly.

 Volatility

 

This might be a new word to some PHP programmers, or one of those words that you heard but don't really know what it means, so here is the dictionary definition:

Fig 2. The definition of volatile, for reference.
An object that might change while being manipulated in many contexts is the epitome of volatility.

pthreads v3 introduces volatile objects to replace objects whose Threaded members are currently mutated by many contexts.

Volatile extends Threaded, so a Volatile object set as a member of a Threaded object still creates an immutable reference to the Volatile object, which is desirable because of access complexity, but the Volatile object itself is ... volatile.

Volatile objects are slow, with high access complexity, and should only be used when there is no other option.

The cost of all this ...

 

Nothing is without cost, it's arguable that multi-threaded programming in PHP just got harder to understand, but it is without question that it got so much faster that the set of power PHP programmers that use pthreads are not going to care.