Multi-processing using PHP

There are situations when we need to make our one-process task into a multi-process task so that we can take advantage of the operating system’s multitasking capabilities.

The basic conceptual difference between multiprocessing and multithreading– A process, is a unique instance of a program with its own memory space, own process ID number etc. Whereas, a thread can be thought of as a virtual process, it does not have its own process ID, does not have its own memory space, but is still able to take advantage of multitasking.

I do agree that PHP is not that good for parallel computing, and PHP is not designed to do that actually. We can do better job using some other languages or technologies.

But the basic point is- there is a simple way to use PHP to handle multiprocessing at the basic level; In Unix, forking a process is quite fast and easier to handle, and PHP is able to fork processes at the run time, hence it can be used at the basic level at least.

A very simple use case of the same-

<?php

class Lib {
    const UPLOADED_IMAGE_DIR = '/var/tmp/image/';
    const RESIZED_IMAGE_DIR = '/var/tmp/resized/';

    public static function resizeImage($height, $width) {
        $image = self::UPLOADED_IMAGE_DIR . 'test.jpg';
        $newImage = self::RESIZED_IMAGE_DIR . 'test-' . $width . '-' . $height . '.jpg';
        list($originalwidth, $originalheight) = getimagesize($image);
        $tmpImage = imagecreatetruecolor($width, $height);
        $copiedImage = imagecreatefromjpeg($image);
        imagecopyresampled($tmpImage, $copiedImage, 0, 0, 0, 0, $width, $height, $originalwidth, $originalheight);

        return imagejpeg($tmpImage, $newImage, 100);
    }
}

class SingleProcess {
    private $sizes = array();

    public function setSizes($sizes) {
        $this->sizes = $sizes;

        return $this;
    }

    public function getSizes() {
        return $this->sizes;
    }

    public function execute() {                 //sequential process
        $sizes = $this->getSizes();
        foreach ($sizes as $size) {
            $status = Lib::resizeImage($size[0], $size[1]);
        }
    }
}

$requiredSizes = array(
    array(50, 50),
    array(100, 100),
    array(150, 150),
    array(200, 200),
    array(250, 250),
    array(300, 300),
    array(350, 350),
    array(400, 400),
    array(450, 450),
    array(500, 500),
    array(1000, 1000)
);
$obj = new SingleProcess();
$obj->setSizes($requiredSizes);
$obj->execute();


time php SingleProcess.php

real 0m9.019s
user 0m8.609s
sys 0m0.388s

Now executing the same using multiple processes

class MultiProcess {
    private $sizes = array();

    public function setSizes($sizes) {
        $this->sizes = $sizes;

        return $this;
    }

    public function getSizes() {
        return $this->sizes;
    }

    public function executeMultiProcess() {     //parallel process
        $childPids = array();
        $sizes = $this->getSizes();
        foreach ($sizes as $size) {
            $pid = pcntl_fork();
            if ($pid == -1) {   //fork failed. May be extreme OOM condition
                die('pcntl_fork failed');
            } elseif ($pid) {   //parent process                
                $childPids[] = $pid;
            } else {            //child process                
                $status = Lib::resizeImage($size[0], $size[1]);
                exit();
            }
        }

        while (!empty($childPids)) {    //wait for all children to complete
            foreach ($childPids as $key => $pid) {
                $status = null;
                $res = pcntl_waitpid($pid, $status, WNOHANG);
                if ($res == -1 || $res > 0) {   //if the process has already exited
                    unset($childPids[$key]);
                }
            }
            //here sleep() should be used, if the script is in production and doing some heavy process
        }
    }
}

$obj = new MultiProcess();
$obj->setSizes($requiredSizes);
$obj->executeMultiProcess();


time php MultiProcess.php

real 0m3.752s
user 0m13.289s
sys 0m1.104s

Interesting, see the difference.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s