# Typical performance for FastCGI + PHP?

## dice

I'm pretty sure that my web server is a whole lot slower than it's supposed to be.  I'm running dual 1.5GHz AMD AthlonMP's w/ a gig of RAM and an IDE disk.  System software is Apache 2.0.58-r2, mod_log_sql 1.100, mod_fastcgi 2.4.2-r1, PHP 4.4.2-r6 and 5.1.4-r4 with cgi and force_cgi_redirect USE variables.

For static HTML pages I get about 400 requests per second using ab2 w/ a concurrancy level of 1000, most of the CPU usage on the server is in the system category during this benchmark.  For PHP pages I get about 22.5 requests per second using ab2 w/ concurrancy of 100, most of the CPU usage on the server is in the user category during this benchmark.  I'm running the benchmarks from a dual P3-450 machine which is connected via 100mbit ethernet.

This seems extremely slow to me, especially the PHP numbers.  Maybe my expectations were far too high, but I was hoping for something more like a few hundred pages per second w/ PHP and a few thousand pages per second w/ static HTML.  Are these results typical or did I screw something up in configuration?

----------

## dice

*bump*

Any ideas?

----------

## Janne Pikkarainen

Please first try to disable mod_log_sql and see if it helps. If it does and you still want to log to some SQL database, you might want to change your implementation a bit. 

- Change Apache to log via syslog instead of its own logging routine. That can be done by putting CustomLog "|logger -p local7.info" combined to your httpd.conf.

- Use some syslog-daemon which is able to log to whatever destination you want it to log: for example syslog-ng can easily be made log to MySQL. Just Google for "syslog-ng mysql" or so. Should you have any questions after those how-to's, just ask them here.  :Smile: 

Anyway, I suspect mod_log_sql is the reason for your slowish static html benchmark results. When it comes to PHP benchmarks, those numbers can be either good or bad: it completely depends what the page is supposed to do. If just plain <?php echo "Hello world"; ?> leads to 22 reqs/s, then it is very bad. On the other hand, some heavier PHP applications are just dog slow. Ok, not usually THAT slow, but at least one genealogy software I know of actually stalls almost whatever hardware.

So, what is your PHP test? Does it involve database connections, session/cookie initialization and handling, dynamic graphics, or some other fancy stuff...?

----------

## dice

Thanks for the reply.  The PHP I am testing with is dead simple:

```
<?php print "Hello"; ?>
```

I disabled mod_log_sql and I'm getting the same results with both PHP and static HTML.  The HTML page is also very simple:

```
<html>

<head>

<title>test</title>

</head>

<body>

<p>test</p>

</body>

</html>
```

Any ideas or insights would be greatly appreciated.

----------

## Janne Pikkarainen

What is the transfer rate Apache Benchmark reports to you after the test has finished? If it's low and not even near the capacity of your 100 Mbit/s network, then you might have a network connection duplex error. If the transfer rate is low, check both your server and torturing-client with ifconfig and mii-tool/ethtool. Probably those network cards should be set in 100 Mbit full duplex mode. Half duplex would lead to collisions and transfer errors -> slow rate.

If this is not the case, what are the MaxClients/MinSpareServers/MaxSpareServers settings in httpd.conf? And what modules does your Apache load during startup? Sometimes mod_negotiation can lead to slower than expected performance and can be safely disabled unless you rely on it providing a multi-lingual web site.

I just spotted something else from your earlier post, too. Concurrency level of 1000 is very much. Are you sure you're gonna need that kind of situations in the real world? Benchmarking with some insane values doesn't help you to fine-tune your server to meet your actual needs. If you really need concurrency of 1000 (meaning 1000 http requests coming in at the completely same time), you'll most likely need to take a look at ulimit values for Apache, especially how many file handles it can open at the same time. Also changing some kernel values might be beneficial.

----------

## dice

ab2 is giving me a transfer rate of ~130 kB/s for the static test and 4.33 kB/s for the PHP.  ifconfig isn't reporting any collisions for the interfaces on either the client or the server and mii-tool reports that both interfaces are running 100baseTx-FD.

The reason I was running the 1000 concurrancy level for the static test was just because I was messing around and seeing how increasing the concurrancy affected how many requests/second the server would give.  I also had some vague ideas of simulating a Slashdot effect by pummeling the server with a ton of requests in a short time.  Bringing the concurrancy level down to 100 doesn't really change anything.

One thing I did notice is that during the PHP benchmark top is full of php-cgi commands which are labeled as <defunct> after the benchmark has been running for a few seconds.  These all go away within a couple seconds of the benchmark stopping, I've been assuming that they're just processes which were temporarily unresponsive due to the server being under a high load.

Disabling mod_negotiation didn't seem to do anything.  Here's a list of what Apache is running:

```
www2 apache2 # grep  ^[^\#]*LoadModule httpd.conf

LoadModule access_module                 modules/mod_access.so

LoadModule auth_module                   modules/mod_auth.so

LoadModule auth_anon_module              modules/mod_auth_anon.so

LoadModule auth_dbm_module               modules/mod_auth_dbm.so

LoadModule charset_lite_module           modules/mod_charset_lite.so

LoadModule env_module                    modules/mod_env.so

LoadModule expires_module                modules/mod_expires.so

LoadModule headers_module                modules/mod_headers.so

LoadModule mime_module                   modules/mod_mime.so

LoadModule setenvif_module               modules/mod_setenvif.so

LoadModule log_config_module             modules/mod_log_config.so

LoadModule logio_module                  modules/mod_logio.so

LoadModule cgi_module                    modules/mod_cgi.so

LoadModule cgid_module                   modules/mod_cgid.so

LoadModule suexec_module                 modules/mod_suexec.so

LoadModule alias_module                  modules/mod_alias.so

LoadModule rewrite_module                modules/mod_rewrite.so

    LoadModule userdir_module            modules/mod_userdir.so

LoadModule actions_module                modules/mod_actions.so

LoadModule autoindex_module              modules/mod_autoindex.so

LoadModule dir_module                    modules/mod_dir.so

LoadModule ext_filter_module             modules/mod_ext_filter.so

LoadModule deflate_module                modules/mod_deflate.so

LoadModule include_module                modules/mod_include.so

LoadModule vhost_alias_module            modules/mod_vhost_alias.so
```

I deleted the few modules that aren't commented out but also aren't being loaded because I didn't add the define to apache's config (INFO and PROXY stuff).  There's also mod_log_sql, mod_ssl, and mod_fastcgi from the modules.d directory.  Finally, here's the settings for the MPM:

```
<IfModule prefork.c>

    StartServers         5

    MinSpareServers      5

    MaxSpareServers     15

    MaxClients         256

    MaxRequestsPerChild  10000

</IfModule>
```

We're using prefork right now although I've also set it to run with worker and that didn't seem to change anything aside from giving me a couple segfaults in error_log while under heavy load.  The settings for prefork are partly based on others that I've seen while googling around and partly me just messing with things to see what happens.

----------

## Janne Pikkarainen

Something really makes your Apache a lot slower than it should be. Have you tried to benchmark your server from the server itself instead of that other computer? I mean giving this command at the server itself:

```
ab2 -k -n 100000 -c 100 http://localhost:80/
```

If this works fast, then something's rotten in your network configuration. If this is also very slow, then your Apache is behaving oddly. In the latter case I would first strip out ALL dynamic or otherwise exotic modules, then benchmark, if it works fast, add some module, benchmark again... and use that technique to find out what's the slow module.

You might also strace the Apache process to see where it's spending its time.

----------

## kashani

Numbers Dice reported are roughly what I've seen in the past.

mod_log_sql works fine though we made modifications to ours to support a BIGINT primary key which makes stats queries faster and to use Innodb tables since we have 12 servers dumping into our central log db.

The best way to get more speed out of PHP is to make the binary smaller. Assuming that any changes I did to the CLI version would be reflected in the Apache module I took the CLI binary from 7MB to 4.5MB. Additionally having more RAM helps. 

It is my understanding that fast-cgi is slower than loading PHP in directly under Apache and the only purpose of doing it is to decrease overall RAM usage. Or you are using a threaded webserver and need non thread safe libs in your PHP. Since you're testing the limits of the box and using pre-fork you're going to be loading PHP into RAM multiple times anyway.

And lastly Eaccelerator finally works with PHP 5.1 though the code is still an RC1 and you'll need to disable the session USE flag if you're using the hardened PHP patches. Eaccelerator will not work with threaded PHP/Apache.

I think you've got two choices. Eaccelerator/PHP/Apache-prefork or PHP-fastcgi/Apache-worker and the smallest PHP binary you can get away with.

kashani

----------

## NightMonkey

 *kashani wrote:*   

> Numbers Dice reported are roughly what I've seen in the past.
> 
> mod_log_sql works fine though we made modifications to ours to support a BIGINT primary key which makes stats queries faster and to use Innodb tables since we have 12 servers dumping into our central log db.
> 
> The best way to get more speed out of PHP is to make the binary smaller. Assuming that any changes I did to the CLI version would be reflected in the Apache module I took the CLI binary from 7MB to 4.5MB. Additionally having more RAM helps. 
> ...

 

Hmm... I think that eaccelerator does support threaded PHP - see Step 3 of the Eaccelerator configuration guide:

 *Quote:*   

> If you use a thread safe build of PHP you must use "zend_extension_ts" instead of "zend_extension".

 

Works for Me(tm), with Apache/PHP both +threads.  :Smile: 

----------

## kashani

I think you might be my hero today. We'd still have some issues to sort out, but if Eaccelerator works with threaded PHP 5.1 I'd start solving them sooner rather than later. Thanks for the tip. 

For the record pecl-apc, Another PHP Cache, does not work at all with PHP 5.1 if you actually using any of the object oriented stuff.

kashani

----------

## Janne Pikkarainen

Kashani & others: with the hardware like the one which the original poster has in use, Apache should really be A LOT faster when it comes to static pages. This is my test server with P4 Xeon 2.0 GHz and 1 GB of RAM. Static page is just a simple Hello world html page.

```
Server Software:        Apache/2.2.2

Server Hostname:        localhost

Server Port:            80

Document Path:          /

Document Length:        177 bytes

Concurrency Level:      1000

Time taken for tests:   3.854626 seconds

Complete requests:      10000

Failed requests:        0

Write errors:           0

Keep-Alive requests:    10000

Total transferred:      4910054 bytes

HTML transferred:       1770000 bytes

Requests per second:    2594.29 [#/sec] (mean)

Time per request:       385.463 [ms] (mean)

Time per request:       0.385 [ms] (mean, across all concurrent requests)

Transfer rate:          1243.70 [Kbytes/sec] received

Connection Times (ms)

              min  mean[+/-sd] median   max

Connect:        0    0   1.4      0      24

Processing:     0   19  51.2      0    3409

Waiting:        0   19  51.2      0    3409

Total:          0   19  51.7      0    3433
```

Yes, the Apache version is 2.2.2, but the performance has been pretty much the same over the different Apache generations. Please note that this test was performed from the server itself; results may of course vary if I test it from another server.

And then here's the same server with PHP version of Hello world. PHP is version 5.1.4 and loaded as an Apache module.

```
Server Software:        Apache/2.2.2

Server Hostname:        localhost

Server Port:            80

Document Path:          /testi.php

Document Length:        5 bytes

Concurrency Level:      1000

Time taken for tests:   6.928643 seconds

Complete requests:      10000

Failed requests:        0

Write errors:           0

Keep-Alive requests:    10000

Total transferred:      2570072 bytes

HTML transferred:       50000 bytes

Requests per second:    1443.28 [#/sec] (mean)

Time per request:       692.864 [ms] (mean)

Time per request:       0.693 [ms] (mean, across all concurrent requests)

Transfer rate:          362.12 [Kbytes/sec] received
```

I also tried to use mod_log_sql in the past, but then discovered that it was better to disable it and instead make my Apache to log via logger and put my logging server to log Apache logs to MySQL. Well, that was in my case, anyway.  :Smile: 

----------

## sf_alpha

íIf you use PHP FastCGI only. Disable mod_php completely and use apache with mpm_worker (USE="threads") instead.

Its far much faster but DON'T ENABLE MOD PHP or you will get Segmantation Fault".

PHP_FCGI_CHILDREN with Apache and mod_astcgi shuold be kept low (I don't know why), my Production server use PHP_FCGI_CHILDREN=5 for now.

I try to raise it to 25 but mod_fastcgi seems to have performance problems with threaded apache.

Number of FastCGI Server should more than MaxClients/ThreadsPerChild.

(i.e. Maxclients = 250, ThreadsPerChild = 25, number of PHP FastCGI Server should be at least 11 and Try tune PHP_FCGI_CHILDREN until you get best performance).

But with LigHTTPd PHP_FCGI_CHILDREN=25 is fine and works great.

If you use eaccelerator, beware the php code. Errornous php code may led memory leaks ... If you want to use eaccelerator make sure PHP script is not error when parsing or you may have to use some monitor script to monitor server memory usage every 30 seconds and try to restart server if swap are being use more than 25%.

My config

```

<IfDefine PHP_FASTCGI>

        # Enable FastCGI

        <IfModule mod_fastcgi.c>

                AddHandler php_fcgi .php .phtml

                Action php_fcgi  /php-fcgi/php-fcgi

                ScriptAlias /php-fcgi/php-fcgi /usr/local/fcgi-bin/php-fcgi

                FastCgiServer /usr/local/fcgi-bin/php-fcgi -processes 15 -pass-header Authorization -pass-header HTTP_AUTHORIZATION -listen-queue-depth 250 -idle-timeout 240

        </IfModule>

        # Set it to handle the files

        <IfModule mod_mime.c>

                AddType application/x-httpd-php .php

                AddType application/x-httpd-php .phtml

                AddType application/x-httpd-php .php3

                AddType application/x-httpd-php .php4

                AddType application/x-httpd-php-source .phps

        </IfModule>

        AddDirectoryIndex index.php index.phtml

</IfDefine>

```

/usr/local/fcgi-bin/php-fcgi

```

#!/bin/bash

if [ -n "${PHPRC}" ]; then

        export PHPRC

fi

if [ -z "${PHP_FCGI_CHILDREN}" ]; then

        PHP_FCGI_CHILDREN="5"

fi

if [ -z "${PHP_FCGI_MAX_REQUESTS}" ]; then

        PHP_FCGI_MAX_REQUESTS="100000"

fi

export PHP_FCGI_CHILDREN PHP_FCGI_MAX_REQUESTS

exec /usr/bin/php-cgi

```

----------

## kashani

 *Janne Pikkarainen wrote:*   

> Kashani & others: with the hardware like the one which the original poster has in use, Apache should really be A LOT faster when it comes to static pages. This is my test server with P4 Xeon 2.0 GHz and 1 GB of RAM. Static page is just a simple Hello world html page.
> 
> 

 

In order for the above to be useful we need to know threaded or unthreaded? I'm going to guess threaded which is why the original poster is slower because he is using prefork. 

grumble apples.... grumble grumble oranges...

kashani

----------

## Janne Pikkarainen

 *kashani wrote:*   

>  *Janne Pikkarainen wrote:*   Kashani & others: with the hardware like the one which the original poster has in use, Apache should really be A LOT faster when it comes to static pages. This is my test server with P4 Xeon 2.0 GHz and 1 GB of RAM. Static page is just a simple Hello world html page.
> 
>  
> 
> In order for the above to be useful we need to know threaded or unthreaded? I'm going to guess threaded which is why the original poster is slower because he is using prefork. 
> ...

 

Prefork. I still don't trust any threaded mpm when my Apache has to use PHP.  :Smile: 

----------

## dice

Hi everyone!

Thanks for all the great replies, I learned a lot going through everything in this thread.  I still haven't gotten FastCGI to give me the performance I wanted, but I've moved on to using different methods to achieve the same goals.  At this point I suspect that the problems could be related to process restrictions in GRsec, but that's just a hunch.

My plan was to use FastCGI + SuExec to host PHP4 and PHP5 pages which would execute with the uid/gid of the owner (virtualhosters) rather than of Apache in order to keep the vhosts from sniffing around in eachother's files.  I've now switched over to using the ITK MPM and the new concurrentmodphp USE flag for allowing PHP4 and PHP5 to be used as modules concurrantly.

The performance is quite a bit better, although I think it could perhaps use some more tweaking.  I'm getting 1050 pages/second w/ static HTML and 780 pages/second with PHP4 and PHP5 pages while using mod_log_sql with the MySQL database on the web server.  Turning off mod_log_sql brings those numbers up to 2660 static and 1300 PHP pages per second.  I think if I move the MySQL server to a different machine I should be able to get close to the latter numbers.

I still need to do a lot more testing with this config using real user pages and data so that I can ensure everything is playing well together.  Looks good so far though.

Thanks again for all the great help.

----------

## kashani

Moving your sql logging to another machine will actually let you surpass those numbers in a productions situation since you are eliminating expensive disk writes from your local system. Probably not so noticeable in an unloaded system.

kashani

----------

