Speed up image rendering script

PHP programming forum. Ask questions or help people concerning PHP code. Don't understand a function? Need help implementing a class? Don't understand a class? Here is where to ask. Remember to do your homework!

Moderator: General Moderators

Post Reply
User avatar
Luke
The Ninja Space Mod
Posts: 6424
Joined: Fri Aug 05, 2005 1:53 pm
Location: Paradise, CA

Speed up image rendering script

Post by Luke »

I have a controller in my app that is used to render an image based on input from the user. Basically it accepts a unique key, looks in the database to see if the image exists, and if so, reads it to the browser. If it doesn't exist, it renders an image that says "No Image Available" and issues a 404. It also allows you to request a thumbnail. If you do, it will check to see if one already exists, and if not, it will create one from the large image and output it. If an thumb already exists, it simply outputs it.

Code: Select all

<?php
 
class ImagesController extends AppController {
 
    var $name = 'Images';
    var $uses = array('Image','Home','Rv');
    /**
     * Outputs an image to the browser if one can be found, otherwise it issues a 404
     */
    
    function view($id = null) {
    
        if ($image = $this->Image->read(null, $id)) {
            $this->__display($image['Image']['path'], $image['Image']['mime_type'], $image['Image']['size']);
        } else {
            header("HTTP/1.0 404 Not Found");
        $this->__display(WWW_ROOT . 'img' . DS . 'not-available.png', 'image/png');
        }
        exit;
             
    }
    /**
     * Output a thumbnail of a big image
     * @todo Issue a 404, but output a No Image image
     */
    function thumb($id) {
    
        if ($image = $this->Image->read(null, $id)) {
    
            $parts = pathinfo($image['Image']['path']);
            $imagename = WWW_ROOT . 'img' . DS . 'uploads' . DS . 'homes' . DS . 'thumbs' . DS . $parts['basename'];
            // if there is not a thumbnail for this image, create one
            if(!file_exists($imagename)) {
                $img = imagecreatefromjpeg($image['Image']['path']);
                $height = 157;
                $width = 210;
                $rs_image = imagecreatetruecolor($width, $height);
                imagecopyresampled($rs_image, $img, 0, 0, 0, 0, $width, $height, 400, 300);
                imagejpeg($rs_image, $imagename, 100);
            }
            
            Configure::write('debug', 0);
            header("HTTP/1.0 200 OK");
            header(sprintf("Content-type: %s", $image['Image']['mime_type']));
            header(sprintf("Content-length: %d", $image['Image']['size']));
            readfile($imagename);
            exit;
        
        }
        
        header("HTTP/1.0 404 Not Found");
        $this->__display(WWW_ROOT . 'img' . DS . 'not-available-thumb.png', 'image/png');
        exit;
    
    }
    
    /**
     * Used for displaying an image by its path starting from uploads/
     */
    function tmp($path, $type = "text/jpeg", $size = null) {
    
        $this->__display(UPLOAD_DIR . $path, $type, $size);
    
    }
    
    function __display($path, $type = null, $size = null) {
    
        Configure::write('debug', 0);
        if (!$type) $type = "text/jpeg"; // best guess
        if (!$size) $size = filesize($path);
        //header("HTTP/1.0 200 OK"); // removed because I want to be able to issue a 404 and still show an image (a 404 one)
        header(sprintf("Content-type: %s", $type));
        header(sprintf("Content-length: %d", $size));
        readfile($path);
        exit;
    
    }
}
When I call a page that renders 15 or 20 images, it takes a while for all of them to render. Longer than if they were just images. My question is how do I speed this up? Any insight is helpful. Thanks, yo!
alex.barylski
DevNet Evangelist
Posts: 6267
Joined: Tue Dec 21, 2004 5:00 pm
Location: Winnipeg

Re: Speed up image rendering script

Post by alex.barylski »

My first thought would be caching.

Cache the thumbnails and the 404 error image and reuse the cache if available -- clean up the cache every X number of hours and update the cache when originals change.
User avatar
pickle
Briney Mod
Posts: 6445
Joined: Mon Jan 19, 2004 6:11 pm
Location: 53.01N x 112.48W
Contact:

Re: Speed up image rendering script

Post by pickle »

It's definitely going to take longer than if they're just images. You need to invoke the PHP interpreter & run this script every time, rather than Apache just sending the image. You're never going to have a PHP script acting as an image be as responsive as the plain image.

As for optimizing your code:
  1. It looks like thumb() could use __display() somehow rather than duplicating the image display code.
  2. Is sprintf() necessary? I never use it, but I imagine it would have more overhead than just using the raw string.
  3. I'm not sure if I'm in the wrong here or not, but I never output the content length. I've never needed to as I never have a problem with it in any browser I test with.
Real programmers don't comment their code. If it was hard to write, it should be hard to understand.
User avatar
WaldoMonster
Forum Contributor
Posts: 225
Joined: Mon Apr 19, 2004 6:19 pm
Contact:

Re: Speed up image rendering script

Post by WaldoMonster »

Send with every image an ETag header, based on the filemtime from the original image.
On every request check if the browser sends a HTTP_IF_NONE_MATCH header.
If the HTTP_IF_NONE_MATCH is equal to the filemtime then you can send a 304 Not Modified header without sending the actual image.
User avatar
Weirdan
Moderator
Posts: 5978
Joined: Mon Nov 03, 2003 6:13 pm
Location: Odessa, Ukraine

Re: Speed up image rendering script

Post by Weirdan »

another place for improvement is making sure image generating script does not use sessions unless absolutely necessary (and if it does, it should release exclusive lock on session file as soon as possible). Scripts that use sessions are effectively serialized for any single session, thus browser might be forced to download images one by one, instead of fetching them in parallel.
alex.barylski
DevNet Evangelist
Posts: 6267
Joined: Tue Dec 21, 2004 5:00 pm
Location: Winnipeg

Re: Speed up image rendering script

Post by alex.barylski »

Is sprintf() necessary? I never use it, but I imagine it would have more overhead than just using the raw string.
I recall running benchmarks and not seeing a big difference -- although technically it makes sense that sprintf would be faster than using a double quoted string, but in this case, NSG uses both. :)

Using double quoted strings requires the PHP tokenizer to perform the extra work whereas the sprintf() likely uses a more simplified search and replace on the placeholders, no really fancy tokenizing going on. For that reason I would personally vote sprintf to be faster than double quoted strings.
User avatar
Christopher
Site Administrator
Posts: 13596
Joined: Wed Aug 25, 2004 7:54 pm
Location: New York, NY, US

Re: Speed up image rendering script

Post by Christopher »

Seems like the problem is that PHP has to do them sequentially. Why not write a shell script to build all the thumbnails as parallel processes.
(#10850)
User avatar
Luke
The Ninja Space Mod
Posts: 6424
Joined: Fri Aug 05, 2005 1:53 pm
Location: Paradise, CA

Re: Speed up image rendering script

Post by Luke »

According to this, printf takes by far the longest... I dont know if that means sprintf would take longer too, but that would be my guess. http://i3x171um.com/output_benchmarks/ob.gif
Post Reply