Profiling a PHP script

Note: if you’re a web developer you might be interested in registering to become a ProgClub member. ProgClub is a free international club for computer programmers and we run some mailing lists you might like to hang out on to chat about software development, life, etc.

Warning: this article is long and detailed and contains a fair number of notes about experimental modifications. If you’re going to try applying the ideas in this file I strong encourage you to read the whole article first, and especially the comments which include important notes about how to patch the software properly (rather than just commenting stuff out which I do initially). You have been warned!

Update: this article is over three years old. It’s helped a number of people, which is nice, but it’s time to move on. The APD component is ancient and unmaintained. If you want a PHP profiler I strongly recommend Xdebug. Seriously: use Xdebug. Oh, and be careful!

Let me take you on a fabulous adventure.

I was working on a new web app I’m doing and the app felt a little sluggish. I wanted to know what was causing the sluggishness. Was it database access? Encryption? Compression? Something else?

So I did a search for profiling a php script and turned up this article (Simplest way to profile a PHP script) at StackOverflow. I read this answer which suggested the PECL APD extension so I figured I’d check it out.

I found the documentation for APD (APD stands for Advanced PHP Debugger) and I read the installation instructions, but there didn’t seem to be anything there. Admittedly I was just looking for the Ubuntu package to install with apt-get. Now that I’m writing this up I realise I should have read Installation of PECL extensions first, but I missed that at the time.

I started throwing commands at my terminal:

 # apt-cache search apd
 # apt-cache search apd php
 # apt-cache search apd | grep php
 # apt-cache search apd | grep PHP
 # cd /usr
 # find -name "apd.so"
 # apt-cache search pecl
 # apt-get install dh-make-php

The dh-make-php package was the closest thing I could find, so I thought maybe I would try that. It said “dh-make-php – Creates Debian source packages for PHP PEAR and PECL extensions”, sounded pretty close. Maybe. Anyway, before I confirmed the installation of of dh-make-php I thought I’d do some more research.

I searched for installing pecl apd ubuntu but that didn’t seem to turn up much. The first result was in French, and it only seemed to be an unanswered question about an error during compilation.

I searched for apd.so ubuntu (because the installation instructions that I read said I needed apd.so) and I turned up How to install apd in debian php5 at ServerFault. The answer said to install php-pear and then run “pecl install apd”. So I did “apt-get install php-pear” and that worked well enough. Then I ran pecl:

root@sixsigma:~# pecl install apd
downloading apd-1.0.1.tgz ...
Starting to download apd-1.0.1.tgz (36,643 bytes)
..........done: 36,643 bytes
15 source files, building
running: phpize
sh: phpize: not found
ERROR: `phpize' failed

I searched for sh: phpize: not found ERROR: `phpize’ failed and found phpize: command not found which said I needed to apt-get install php5-dev, which I did. Then I tried again:

root@sixsigma:~# pecl install apd
downloading apd-1.0.1.tgz ...
Starting to download apd-1.0.1.tgz (36,643 bytes)
..........done: 36,643 bytes
15 source files, building
running: phpize
Cannot find config.m4.
Make sure that you run '/usr/bin/phpize' in the top level source directory of the module

ERROR: `phpize' failed

I searched for the error message running: phpize Cannot find config.m4. and found Cannot find config.m4 + phpize +Resolved which just said like the error message said that I needed to run from the top level source directory. Whatever that was supposed to mean.

I searched for the downloaded file apd-1.0.1.tgz and found it in /build/buildd/php5-5.3.2/pear-build-download/. So I tried the following:

root@sixsigma:~# cd /build/buildd/php5-5.3.2/pear-build-download/
root@sixsigma:/build/buildd/php5-5.3.2/pear-build-download# tar xvf apd-1.0.1.tgz
package.xml
apd-1.0.1/LICENSE
apd-1.0.1/Makefile.in
apd-1.0.1/README
apd-1.0.1/apd.dsp
apd-1.0.1/apd_array.c
apd-1.0.1/apd_array.h
apd-1.0.1/apd_lib.c
apd-1.0.1/apd_lib.h
apd-1.0.1/apd_summary.c
apd-1.0.1/build
apd-1.0.1/build/CVS
apd-1.0.1/build/CVS/Entries
apd-1.0.1/build/CVS/Repository
apd-1.0.1/build/CVS/Root
apd-1.0.1/build/mkdep.awk
apd-1.0.1/build/scan_makefile_in.awk
apd-1.0.1/build/shtool
apd-1.0.1/config.m4
apd-1.0.1/php_apd.c
apd-1.0.1/php_apd.h
apd-1.0.1/php_sockets.h
apd-1.0.1/php_sockets_win.h
apd-1.0.1/pprofp
apd-1.0.1/pprof2calltree
apd-1.0.1/win32compat.c
apd-1.0.1/win32compat.h
root@sixsigma:/build/buildd/php5-5.3.2/pear-build-download# cd apd-1.0.1
root@sixsigma:/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1# ls
apd_array.c  apd_lib.h      LICENSE      php_sockets.h      README
apd_array.h  apd_summary.c  Makefile.in  php_sockets_win.h  win32compat.c
apd.dsp      build          php_apd.c    pprof2calltree     win32compat.h
apd_lib.c    config.m4      php_apd.h    pprofp

It wasn’t clear what to do next. There was no configure script or Makefile in the directory.

I searched for the full error message Starting to download apd-1.0.1.tgz (36,643 bytes) ……….done: 36,643 bytes 15 source files, building running: phpize Cannot find config.m4. and found this which seemed to suggest I needed to run “pear channel-update pear.php.net” which I did, but nothing exciting happened:

root@sixsigma:/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1# pear channel-update pear.php.net
Updating channel "pear.php.net"
Channel "pear.php.net" is up to date
root@sixsigma:/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1# pecl install apd
downloading apd-1.0.1.tgz ...
Starting to download apd-1.0.1.tgz (36,643 bytes)
..........done: 36,643 bytes
15 source files, building
running: phpize
Cannot find config.m4.
Make sure that you run '/usr/bin/phpize' in the top level source directory of the module

ERROR: `phpize' failed

The error message had said something about “phpize” so I figured I’d give that a go.

root@sixsigma:/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1# phpize
Configuring for:
PHP Api Version:         20090626
Zend Module Api No:      20090626
Zend Extension Api No:   220090626
root@sixsigma:/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1# ls
acinclude.m4  apd_summary.c   configure        missing            pprofp
aclocal.m4    autom4te.cache  configure.in     mkinstalldirs      README
apd_array.c   build           install-sh       php_apd.c          run-tests.php
apd_array.h   config.guess    LICENSE          php_apd.h          win32compat.c
apd.dsp       config.h.in     ltmain.sh        php_sockets.h      win32compat.h
apd_lib.c     config.m4       Makefile.global  php_sockets_win.h
apd_lib.h     config.sub      Makefile.in      pprof2calltree

And there you go! The “phpize” command built me a configure script. So…

root@sixsigma:/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1# ./configure
checking for grep that handles long lines and -e... /bin/grep
checking for egrep... /bin/grep -E
checking for a sed that does not truncate output... /bin/sed
checking for cc... cc
checking whether the C compiler works... yes
checking for C compiler default output file name... a.out
checking for suffix of executables...
checking whether we are cross compiling... no
checking for suffix of object files... o
checking whether we are using the GNU C compiler... yes
checking whether cc accepts -g... yes
checking for cc option to accept ISO C89... none needed
checking how to run the C preprocessor... cc -E
checking for icc... no
checking for suncc... no
checking whether cc understands -c and -o together... yes
checking for system library directory... lib
checking if compiler supports -R... no
checking if compiler supports -Wl,-rpath,... yes
checking build system type... x86_64-unknown-linux-gnu
checking host system type... x86_64-unknown-linux-gnu
checking target system type... x86_64-unknown-linux-gnu
checking for PHP prefix... /usr
checking for PHP includes... -I/usr/include/php5 -I/usr/include/php5/main -I/usr/include/php5/TSRM -I/usr/include/php5/Zend -I/usr/include/php5/ext -I/usr/include/php5/ext/date/lib
checking for PHP extension directory... /usr/lib/php5/20090626
checking for PHP installed headers prefix... /usr/include/php5
checking if debug is enabled... no
checking if zts is enabled... no
checking for re2c... re2c
checking for re2c version... 0.13.5 (ok)
checking for gawk... no
checking for nawk... nawk
checking if nawk is broken... no
checking whether to enable apd support... yes, shared
checking for a sed that does not truncate output... (cached) /bin/sed
checking for fgrep... /bin/grep -F
checking for ld used by cc... /usr/bin/ld
checking if the linker (/usr/bin/ld) is GNU ld... yes
checking for BSD- or MS-compatible name lister (nm)... /usr/bin/nm -B
checking the name lister (/usr/bin/nm -B) interface... BSD nm
checking whether ln -s works... yes
checking the maximum length of command line arguments... 1572864
checking whether the shell understands some XSI constructs... yes
checking whether the shell understands "+="... yes
checking for /usr/bin/ld option to reload object files... -r
checking for objdump... objdump
checking how to recognize dependent libraries... pass_all
checking for ar... ar
checking for strip... strip
checking for ranlib... ranlib
checking command to parse /usr/bin/nm -B output from cc object... ok
checking for ANSI C header files... yes
checking for sys/types.h... yes
checking for sys/stat.h... yes
checking for stdlib.h... yes
checking for string.h... yes
checking for memory.h... yes
checking for strings.h... yes
checking for inttypes.h... yes
checking for stdint.h... yes
checking for unistd.h... yes
checking for dlfcn.h... yes
checking for objdir... .libs
checking if cc supports -fno-rtti -fno-exceptions... no
checking for cc option to produce PIC... -fPIC -DPIC
checking if cc PIC flag -fPIC -DPIC works... yes
checking if cc static flag -static works... yes
checking if cc supports -c -o file.o... yes
checking if cc supports -c -o file.o... (cached) yes
checking whether the cc linker (/usr/bin/ld -m elf_x86_64) supports shared libraries... yes
checking whether -lc should be explicitly linked in... no
checking dynamic linker characteristics... GNU/Linux ld.so
checking how to hardcode library paths into programs... immediate
checking whether stripping libraries is possible... yes
checking if libtool supports shared libraries... yes
checking whether to build shared libraries... yes
checking whether to build static libraries... no
configure: creating ./config.status
config.status: creating config.h
config.status: executing libtool commands

root@sixsigma:/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1# make
/bin/bash /build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/libtool --mode=compile cc  -I. -I/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1 -DPHP_ATOM_INC -I/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/include -I/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/main -I/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1 -I/usr/include/php5 -I/usr/include/php5/main -I/usr/include/php5/TSRM -I/usr/include/php5/Zend -I/usr/include/php5/ext -I/usr/include/php5/ext/date/lib  -DHAVE_CONFIG_H  -g -O2   -c /build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/php_apd.c -o php_apd.lo
libtool: compile:  cc -I. -I/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1 -DPHP_ATOM_INC -I/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/include -I/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/main -I/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1 -I/usr/include/php5 -I/usr/include/php5/main -I/usr/include/php5/TSRM -I/usr/include/php5/Zend -I/usr/include/php5/ext -I/usr/include/php5/ext/date/lib -DHAVE_CONFIG_H -g -O2 -c /build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/php_apd.c  -fPIC -DPIC -o .libs/php_apd.o
/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/php_apd.c: In function âapd_dump_fprintfâ:
/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/php_apd.c:182: warning: ignoring return value of âwriteâ, declared with attribute warn_unused_result
/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/php_apd.c: In function âapd_pprof_fprintfâ:
/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/php_apd.c:205: warning: format not a string literal and no format arguments
/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/php_apd.c: In function âapd_interactiveâ:
/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/php_apd.c:241: warning: ignoring return value of âwriteâ, declared with attribute warn_unused_result
/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/php_apd.c: In function âzif_override_functionâ:
/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/php_apd.c:615: warning: âzend_get_parameters_exâ is deprecated (declared at /usr/include/php5/Zend/zend_API.h:222)
/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/php_apd.c: In function âzif_rename_functionâ:
/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/php_apd.c:669: warning: âzend_get_parameters_exâ is deprecated (declared at /usr/include/php5/Zend/zend_API.h:222)
/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/php_apd.c: In function âzif_apd_set_pprof_traceâ:
/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/php_apd.c:792: warning: âzend_get_parameters_exâ is deprecated (declared at /usr/include/php5/Zend/zend_API.h:222)
/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/php_apd.c: In function âzif_apd_echoâ:
/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/php_apd.c:934: warning: ignoring return value of âwriteâ, declared with attribute warn_unused_result
/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/php_apd.c:935: warning: ignoring return value of âwriteâ, declared with attribute warn_unused_result
/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/php_apd.c: In function âapd_zend_startupâ:
/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/php_apd.c:967: error: âstruct _zend_compiler_globalsâ has no member named âextended_infoâ
make: *** [php_apd.lo] Error 1

I searched for building pecl apd but didn’t turn up anything useful. I had a look at the code in php_apd.c on line 967:

int apd_zend_startup(zend_extension *extension)
{
  TSRMLS_FETCH();
  CG(extended_info) = 1;  /* XXX: this is ridiculous */
  return zend_startup_module(&apd_module_entry);
}

The problem line was the line saying “this is ridiculous”. I wonder what they meant?

I decided to figure out where CG was defined so I could figure out what was going on. I grepped for CG:

root@sixsigma:/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1# grep -R "define CG" *
root@sixsigma:/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1# 

No dice. I had a look in the Makefile and found phpincludedir = /usr/include/php5.

jj5@sixsigma:~$ cd /usr/include/php5
jj5@sixsigma:/usr/include/php5$ ls
ext  main  TSRM  Zend
jj5@sixsigma:/usr/include/php5$ grep -R "define CG" *
Zend/zend_globals_macros.h:# define CG(v) TSRMG(compiler_globals_id, zend_compiler_globals *, v)
Zend/zend_globals_macros.h:# define CG(v) (compiler_globals.v)

The code in question was:

/* Compiler */
#ifdef ZTS
# define CG(v) TSRMG(compiler_globals_id, zend_compiler_globals *, v)
int zendparse(void *compiler_globals);
#else
# define CG(v) (compiler_globals.v)
extern ZEND_API struct _zend_compiler_globals compiler_globals;
int zendparse(void);
#endif

I wasn’t sure if ZTS was defined or not, but in any event the problem seemed to be related to *zend_compiler_globals.

root@sixsigma:/usr/include/php5# grep -R compiler_global *
Zend/zend_globals_macros.h:typedef struct _zend_compiler_globals zend_compiler_globals;
Zend/zend_globals_macros.h:# define CG(v) TSRMG(compiler_globals_id, zend_compiler_globals *, v)
Zend/zend_globals_macros.h:int zendparse(void *compiler_globals);
Zend/zend_globals_macros.h:# define CG(v) (compiler_globals.v)
Zend/zend_globals_macros.h:extern ZEND_API struct _zend_compiler_globals compiler_globals;
Zend/zend_globals.h:ZEND_API extern int compiler_globals_id;
Zend/zend_globals.h:struct _zend_compiler_globals {

I found the struct definition in Zend/zend_globals.h. I had a look at the source. It was a huge file. I searched for “extend” (because the error message was about missing member “extended_info”) and all I found was:

        /* for extended information support */
        zend_bool no_extensions;

Maybe I had an older version of the header file and it had been refactored from “no_extensions” to “extended_info”? That might explain why the comment was complaining about something being ridiculous. Maybe they rename their interface all the time? I tried changing php_apd.c to use “no_extensions” instead of “extended_info” but that didn’t compile either. Not knowing what else to do I commented out the line wtih the syntax error:

int apd_zend_startup(zend_extension *extension)
{
  TSRMLS_FETCH();
  /* CG(extended_info) = 1;  /* XXX: this is ridiculous */
  return zend_startup_module(&apd_module_entry);
}

Then I ran make again:

root@sixsigma:/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1# make
/bin/bash /build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/libtool --mode=compile cc  -I. -I/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1 -DPHP_ATOM_INC -I/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/include -I/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/main -I/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1 -I/usr/include/php5 -I/usr/include/php5/main -I/usr/include/php5/TSRM -I/usr/include/php5/Zend -I/usr/include/php5/ext -I/usr/include/php5/ext/date/lib  -DHAVE_CONFIG_H  -g -O2   -c /build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/php_apd.c -o php_apd.lo
libtool: compile:  cc -I. -I/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1 -DPHP_ATOM_INC -I/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/include -I/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/main -I/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1 -I/usr/include/php5 -I/usr/include/php5/main -I/usr/include/php5/TSRM -I/usr/include/php5/Zend -I/usr/include/php5/ext -I/usr/include/php5/ext/date/lib -DHAVE_CONFIG_H -g -O2 -c /build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/php_apd.c  -fPIC -DPIC -o .libs/php_apd.o
/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/php_apd.c: In function âapd_dump_fprintfâ:
/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/php_apd.c:182: warning: ignoring return value of âwriteâ, declared with attribute warn_unused_result
/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/php_apd.c: In function âapd_pprof_fprintfâ:
/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/php_apd.c:205: warning: format not a string literal and no format arguments
/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/php_apd.c: In function âapd_interactiveâ:
/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/php_apd.c:241: warning: ignoring return value of âwriteâ, declared with attribute warn_unused_result
/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/php_apd.c: In function âzif_override_functionâ:
/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/php_apd.c:615: warning: âzend_get_parameters_exâ is deprecated (declared at /usr/include/php5/Zend/zend_API.h:222)
/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/php_apd.c: In function âzif_rename_functionâ:
/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/php_apd.c:669: warning: âzend_get_parameters_exâ is deprecated (declared at /usr/include/php5/Zend/zend_API.h:222)
/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/php_apd.c: In function âzif_apd_set_pprof_traceâ:
/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/php_apd.c:792: warning: âzend_get_parameters_exâ is deprecated (declared at /usr/include/php5/Zend/zend_API.h:222)
/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/php_apd.c: In function âzif_apd_echoâ:
/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/php_apd.c:934: warning: ignoring return value of âwriteâ, declared with attribute warn_unused_result
/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/php_apd.c:935: warning: ignoring return value of âwriteâ, declared with attribute warn_unused_result
/bin/bash /build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/libtool --mode=compile cc  -I. -I/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1 -DPHP_ATOM_INC -I/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/include -I/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/main -I/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1 -I/usr/include/php5 -I/usr/include/php5/main -I/usr/include/php5/TSRM -I/usr/include/php5/Zend -I/usr/include/php5/ext -I/usr/include/php5/ext/date/lib  -DHAVE_CONFIG_H  -g -O2   -c /build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/apd_lib.c -o apd_lib.lo
libtool: compile:  cc -I. -I/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1 -DPHP_ATOM_INC -I/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/include -I/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/main -I/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1 -I/usr/include/php5 -I/usr/include/php5/main -I/usr/include/php5/TSRM -I/usr/include/php5/Zend -I/usr/include/php5/ext -I/usr/include/php5/ext/date/lib -DHAVE_CONFIG_H -g -O2 -c /build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/apd_lib.c  -fPIC -DPIC -o .libs/apd_lib.o
/bin/bash /build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/libtool --mode=compile cc  -I. -I/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1 -DPHP_ATOM_INC -I/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/include -I/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/main -I/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1 -I/usr/include/php5 -I/usr/include/php5/main -I/usr/include/php5/TSRM -I/usr/include/php5/Zend -I/usr/include/php5/ext -I/usr/include/php5/ext/date/lib  -DHAVE_CONFIG_H  -g -O2   -c /build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/apd_array.c -o apd_array.lo
libtool: compile:  cc -I. -I/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1 -DPHP_ATOM_INC -I/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/include -I/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/main -I/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1 -I/usr/include/php5 -I/usr/include/php5/main -I/usr/include/php5/TSRM -I/usr/include/php5/Zend -I/usr/include/php5/ext -I/usr/include/php5/ext/date/lib -DHAVE_CONFIG_H -g -O2 -c /build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/apd_array.c  -fPIC -DPIC -o .libs/apd_array.o
/bin/bash /build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/libtool --mode=compile cc  -I. -I/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1 -DPHP_ATOM_INC -I/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/include -I/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/main -I/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1 -I/usr/include/php5 -I/usr/include/php5/main -I/usr/include/php5/TSRM -I/usr/include/php5/Zend -I/usr/include/php5/ext -I/usr/include/php5/ext/date/lib  -DHAVE_CONFIG_H  -g -O2   -c /build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/apd_summary.c -o apd_summary.lo
libtool: compile:  cc -I. -I/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1 -DPHP_ATOM_INC -I/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/include -I/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/main -I/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1 -I/usr/include/php5 -I/usr/include/php5/main -I/usr/include/php5/TSRM -I/usr/include/php5/Zend -I/usr/include/php5/ext -I/usr/include/php5/ext/date/lib -DHAVE_CONFIG_H -g -O2 -c /build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/apd_summary.c  -fPIC -DPIC -o .libs/apd_summary.o
/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/apd_summary.c: In function âapd_summary_output_footerâ:
/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/apd_summary.c:278: warning: unknown conversion type character â"â in format
/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/apd_summary.c:294: warning: passing argument 6 of âphp_basenameâ from incompatible pointer type
/usr/include/php5/ext/standard/php_string.h:128: note: expected âsize_t *â but argument is of type âint *â
/bin/bash /build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/libtool --mode=link cc -DPHP_ATOM_INC -I/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/include -I/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/main -I/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1 -I/usr/include/php5 -I/usr/include/php5/main -I/usr/include/php5/TSRM -I/usr/include/php5/Zend -I/usr/include/php5/ext -I/usr/include/php5/ext/date/lib  -DHAVE_CONFIG_H  -g -O2   -o apd.la -export-dynamic -avoid-version -prefer-pic -module -rpath /build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/modules  php_apd.lo apd_lib.lo apd_array.lo apd_summary.lo
libtool: link: cc -shared  .libs/php_apd.o .libs/apd_lib.o .libs/apd_array.o .libs/apd_summary.o      -Wl,-soname -Wl,apd.so -o .libs/apd.so
libtool: link: ( cd ".libs" && rm -f "apd.la" && ln -s "../apd.la" "apd.la" )
/bin/bash /build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/libtool --mode=install cp ./apd.la /build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/modules
libtool: install: cp ./.libs/apd.so /build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/modules/apd.so
libtool: install: cp ./.libs/apd.lai /build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/modules/apd.la
libtool: finish: PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/X11R6/bin:/usr/lib/surfraw:/sbin" ldconfig -n /build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/modules
----------------------------------------------------------------------
Libraries have been installed in:
   /build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/modules

If you ever happen to want to link against installed libraries
in a given directory, LIBDIR, you must either use libtool, and
specify the full pathname of the library, or use the `-LLIBDIR'
flag during linking and do at least one of the following:
   - add LIBDIR to the `LD_LIBRARY_PATH' environment variable
     during execution
   - add LIBDIR to the `LD_RUN_PATH' environment variable
     during linking
   - use the `-Wl,-rpath -Wl,LIBDIR' linker flag
   - have your system administrator add LIBDIR to `/etc/ld.so.conf'

See any operating system documentation about shared libraries for
more information, such as the ld(1) and ld.so(8) manual pages.
----------------------------------------------------------------------

Build complete.
Don't forget to run 'make test'.

Success!

The build script said to run make test, so:

root@sixsigma:/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1# make test

Build complete.
Don't forget to run 'make test'.

PHP Warning:  PHP Startup: Invalid library (maybe not a PHP library) 'apd.so' in Unknown on line 0

Warning: PHP Startup: Invalid library (maybe not a PHP library) 'apd.so' in Unknown on line 0
PHP Warning:  PHP Startup: Invalid library (maybe not a PHP library) 'apd.so' in Unknown on line 0

Warning: PHP Startup: Invalid library (maybe not a PHP library) 'apd.so' in Unknown on line 0

=====================================================================
PHP         : /usr/bin/php
PHP_SAPI    : cli
PHP_VERSION : 5.3.2-1ubuntu4.11
ZEND_VERSION: 2.3.0
PHP_OS      : Linux - Linux sixsigma.blackbrick.com 2.6.33.5-rscloud #2 SMP Thu Jun 10 15:26:23 UTC 2010 x86_64
INI actual  : /build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/tmp-php.ini
More .INIs  :
CWD         : /build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1
Extra dirs  :
VALGRIND    : Not used
=====================================================================
TIME START 2012-01-10 04:13:08
=====================================================================
No tests were run.

That didn’t look good. There was complaints about an invalid apd.so library and no tests were run. What next?

I had a look in the Makefile for the “test: all” target:

test: all
        -@if test ! -z "$(PHP_EXECUTABLE)" && test -x "$(PHP_EXECUTABLE)"; then \
                INI_FILE=`$(PHP_EXECUTABLE) -d 'display_errors=stderr' -r 'echo php_ini_loaded_file();' 2> /dev/null`; \
                if test "$$INI_FILE"; then \
                        $(EGREP) -h -v $(PHP_DEPRECATED_DIRECTIVES_REGEX) "$$INI_FILE" > $(top_builddir)/tmp-php.ini; \
                else \
                        echo > $(top_builddir)/tmp-php.ini; \
                fi; \
                INI_SCANNED_PATH=`$(PHP_EXECUTABLE) -d 'display_errors=stderr' -r '$$a = explode(",\n", trim(php_ini_scanned_files())); echo $$a[0];' 2> /dev/null`; \
                if test "$$INI_SCANNED_PATH"; then \
                        INI_SCANNED_PATH=`$(top_srcdir)/build/shtool path -d $$INI_SCANNED_PATH`; \
                        $(EGREP) -h -v $(PHP_DEPRECATED_DIRECTIVES_REGEX) "$$INI_SCANNED_PATH"/*.ini >> $(top_builddir)/tmp-php.ini; \
                fi; \
                TEST_PHP_EXECUTABLE=$(PHP_EXECUTABLE) \
                TEST_PHP_SRCDIR=$(top_srcdir) \
                CC="$(CC)" \
                        $(PHP_EXECUTABLE) -n -c $(top_builddir)/tmp-php.ini $(PHP_TEST_SETTINGS) $(top_srcdir)/run-tests.php -n -c $(top_builddir)/tmp-php.ini -d extension_dir=$(top_builddir)/modules/ $(PHP_TEST_SHARED_EXTENSIONS) $(TESTS); \
        else \
                echo "ERROR: Cannot run tests without CLI sapi."; \
        fi

A little bit of tinkering and I was able to determine that the test script was running:

/usr/bin/php -n -c /build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/tmp-php.ini -d open_basedir= -d output_buffering=0 -d memory_limit=-1 /build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/run-tests.php -n -c /build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/tmp-php.ini -d extension_dir=/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/modules/ -d extension=apd.so

I wasn’t quite sure where to begin with that, so I searched for the error message PHP Warning: PHP Startup: Invalid library (maybe not a PHP library) ‘apd.so’ in Unknown on line 0 which rendered Re: Problems Compiling (lots of) extensions with
php 5.1-beta3 – msg#00192
which suggested the problem was that APD was a Zend extension not a normal extension:

Aren’t tzhose Zend Extensions? So you would need to load them with
zend_extension = [full path] instead of extension = [filename].

I remembered way back in the original installation instructions something about configuring zend_extension… so I edited /build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/tmp-php.ini and added the following:

 zend_extension = /build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/modules/apd.so
 apd.dumpdir = /build/trace
 apd.statement_tracing = 0

Since I was using the zend_extension I edited the command line to remove all references to “extension” arguments:

/usr/bin/php -n -c /build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/tmp-php.ini -d open_basedir= -d output_buffering=0 -d memory_limit=-1 /build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/run-tests.php -n -c /build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/tmp-php.ini

root@sixsigma:/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1# /usr/bin/php -n -c /build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/tmp-php.ini -d open_basedir= -d output_buffering=0 -d memory_limit=-1 /build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/run-tests.php -n -c /build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/tmp-php.ini
ERROR: environment variable TEST_PHP_EXECUTABLE must be set to specify PHP executable!

So I set that variable:

root@sixsigma:/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1# TEST_PHP_EXECUTABLE=/usr/bin/php
root@sixsigma:/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1# /usr/bin/php -n -c /build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/tmp-php.ini -d open_basedir= -d output_buffering=0 -d memory_limit=-1 /build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/run-tests.php -n -c /build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/tmp-php.ini
ERROR: environment variable TEST_PHP_EXECUTABLE must be set to specify PHP executable!
root@sixsigma:/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1# export TEST_PHP_EXECUTABLE
root@sixsigma:/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1# /usr/bin/php -n -c /build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/tmp-php.ini -d open_basedir= -d output_buffering=0 -d memory_limit=-1 /build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/run-tests.php -n -c /build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/tmp-php.ini

=====================================================================
PHP         : /usr/bin/php
PHP_SAPI    : cli
PHP_VERSION : 5.3.2-1ubuntu4.11
ZEND_VERSION: 2.3.0
PHP_OS      : Linux - Linux sixsigma.blackbrick.com 2.6.33.5-rscloud #2 SMP Thu Jun 10 15:26:23 UTC 2010 x86_64
INI actual  : /build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/tmp-php.ini
More .INIs  :
CWD         : /build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1
Extra dirs  :
VALGRIND    : Not used
=====================================================================
TIME START 2012-01-10 04:43:34
=====================================================================
No tests were run.

Still no tests running. I had a read, a nice long read, of the /build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/run-tests.php file. It seems to load an run *.phpt files. So I searched for pecl apd phpt files and found Patching a bug in a pecl extension which describes the *.phpt file format. So I created a test.phpt file:

--TEST--
Just testing.
--FILE--
<?php

apd_set_pprof_trace();

function a() { echo "a"; }
function b() { a(); echo "b"; }

a();
b();
a();
--EXPECT--
aaba

I ran the run-tests.php script again and checked the /build/trace directory for messages from APD, but there was nothing there. The tests hadn’t run.

I couldn’t be bothered figuring out the correct syntax for run-tests.php so I edited the file and changed line 824 from:

$test_dirs = array();

To:

$test_dirs = array( '.' );

Then I ran the tests again:

root@sixsigma:/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1# /usr/bin/php -n -c /build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/tmp-php.ini -d open_basedir= -d output_buffering=0 -d memory_limit=-1 /build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/run-tests.php -n -c /build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/tmp-php.ini

=====================================================================
PHP         : /usr/bin/php
PHP_SAPI    : cli
PHP_VERSION : 5.3.2-1ubuntu4.11
ZEND_VERSION: 2.3.0
PHP_OS      : Linux - Linux sixsigma.blackbrick.com 2.6.33.5-rscloud #2 SMP Thu Jun 10 15:26:23 UTC 2010 x86_64
INI actual  : /build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/tmp-php.ini
More .INIs  :
CWD         : /build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1
Extra dirs  :
VALGRIND    : Not used
=====================================================================
TIME START 2012-01-10 05:05:06
=====================================================================
PASS Just testing. [test.phpt]
=====================================================================
TIME END 2012-01-10 05:05:06

=====================================================================
TEST RESULT SUMMARY
---------------------------------------------------------------------
Exts skipped    :    0
Exts tested     :   45
---------------------------------------------------------------------

Number of tests :    1                 1
Tests skipped   :    0 (  0.0%) --------
Tests warned    :    0 (  0.0%) (  0.0%)
Tests failed    :    0 (  0.0%) (  0.0%)
Expected fail   :    0 (  0.0%) (  0.0%)
Tests passed    :    1 (100.0%) (100.0%)
---------------------------------------------------------------------
Time taken      :    0 seconds
=====================================================================

We would like to send this report automatically to the
PHP QA team, to give us a better understanding of how
the test cases are doing. If you don't want to send it
immediately, you can choose "s" to save the report to
a file that you can send us later.
Do you want to send this report now? [Yns]: n

And they ran!

I checked the /build/trace directory, and there was a file there! The trace had worked!

root@sixsigma:/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1# cd /build/trace
root@sixsigma:/build/trace# ls
pprof.15345.0
root@sixsigma:/build/trace# head pprof.15345.0
#Pprof [APD] v1.0.1
caller=/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/test.php

END_HEADER
! 1 /build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/test.php
& 1 main 2
+ 1 1 2
& 2 apd_set_pprof_trace 2
+ 2 1 2
@ 0 2 0 0 489

It took me a little while to find it, but eventually I found the pprofp script in the file /build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/pprofp. So I ran it:

root@sixsigma:/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1# ./pprofp -r /build/trace/pprof.15345.0
bash: ./pprofp: /usr/local/bin/php: bad interpreter: No such file or directory

I needed to fix the registered interpreter. I changed it to /usr/bin/php.

root@sixsigma:/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1# ./pprofp -r /build/trace/pprof.15345.0
PHP:  syntax error, unexpected '=' in Unknown on line 7

Trace for /build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1/test.php
Total Elapsed Time = 0.00
Total System Time  = 0.00
Total User Time    = 0.00


         Real         User        System             secs/    cumm
%Time (excl/cumm)  (excl/cumm)  (excl/cumm) Calls    call    s/call  Memory Usage Name
--------------------------------------------------------------------------------------
84.9 0.00 0.00  0.00 0.00  0.00 0.00     1  0.0005   0.0005            0 apd_set_pprof_trace
12.8 0.00 0.00  0.00 0.00  0.00 0.00     3  0.0000   0.0000            0 a
1.4 0.00 0.00  0.00 0.00  0.00 0.00     1  0.0000   0.0000            0 b
0.9 0.00 0.00  0.00 0.00  0.00 0.00     1  0.0000   0.0006            0 main

It works!

So then all I had to do was configure my application under development to run apd, which I did. Then I ran the report on the trace file it generated:

root@sixsigma:/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1# ./pprofp -r /build/trace/pprof.15650.1
PHP:  syntax error, unexpected '=' in Unknown on line 7

Trace for /home/jj5/proj/aman/src/web/aman/config.php
Total Elapsed Time = 2.62
Total System Time  = 0.37
Total User Time    = 1.23


         Real         User        System             secs/    cumm
%Time (excl/cumm)  (excl/cumm)  (excl/cumm) Calls    call    s/call  Memory Usage Name
--------------------------------------------------------------------------------------
36.2 0.95 0.95  0.03 0.03  0.00 0.00     2  0.4737   0.4773            0 ob_end_flush
15.0 0.39 0.39  0.21 0.21  0.07 0.07  52388  0.0000   0.0000            0 sgen_word_xor
9.3 0.24 0.24  0.21 0.21  0.02 0.02  33504  0.0000   0.0000            0 sgen_word_add
6.0 0.16 0.16  0.18 0.18  0.06 0.06  16756  0.0000   0.0000            0 sgen_word_a
4.5 0.12 0.12  0.04 0.04  0.02 0.02  16756  0.0000   0.0000            0 sgen_word_b
4.3 0.11 0.11  0.10 0.10  0.01 0.01  16756  0.0000   0.0000            0 sgen_word_c
4.3 0.11 0.11  0.11 0.11  0.05 0.05  16756  0.0000   0.0000            0 sgen_word_d
3.4 0.09 0.09  0.07 0.07  0.04 0.04    34  0.0026   0.0026            0 mysqli->query
3.2 0.08 1.20  0.10 0.90  0.04 0.27  16752  0.0000   0.0001            0 SgenBlowfishCipher->do_round
2.2 0.06 0.06  0.07 0.07  0.00 0.00   175  0.0003   0.0003            0 dirname
1.3 0.04 0.04  0.03 0.03  0.00 0.00  4477  0.0000   0.0000            0 substr
1.1 0.03 0.03  0.03 0.03  0.01 0.01  4168  0.0000   0.0000            0 hexdec

And ob_end_flush had a massive 36% of the processing time! Not sure what to do, I tried changing my php.ini file setting output_buffering to 200,000:

#output_buffering = 4096
output_buffering = 200000

Then I ran my code again and examined the next trace:

root@sixsigma:/build/buildd/php5-5.3.2/pear-build-download/apd-1.0.1# ./pprofp -r /build/trace/pprof.15948.0
PHP:  syntax error, unexpected '=' in Unknown on line 7

Trace for /home/jj5/proj/aman/src/web/aman/config.php
Total Elapsed Time = 2.01
Total System Time  = 0.57
Total User Time    = 1.37


         Real         User        System             secs/    cumm
%Time (excl/cumm)  (excl/cumm)  (excl/cumm) Calls    call    s/call  Memory Usage Name
--------------------------------------------------------------------------------------
20.8 0.42 0.42  0.30 0.30  0.07 0.07  52388  0.0000   0.0000            0 sgen_word_xor
14.7 0.30 0.30  0.22 0.22  0.07 0.07  33504  0.0000   0.0000            0 sgen_word_add
9.2 0.18 0.18  0.15 0.15  0.05 0.05  16756  0.0000   0.0000            0 sgen_word_a
6.9 0.14 0.14  0.10 0.10  0.04 0.04  16756  0.0000   0.0000            0 sgen_word_c
6.5 0.13 0.13  0.07 0.07  0.03 0.03  16756  0.0000   0.0000            0 sgen_word_b
6.3 0.13 0.13  0.10 0.10  0.04 0.04  16756  0.0000   0.0000            0 sgen_word_d
4.6 0.09 0.09  0.04 0.04  0.05 0.05   175  0.0005   0.0005            0 dirname
4.4 0.09 1.35  0.07 0.98  0.07 0.36  16752  0.0000   0.0001            0 SgenBlowfishCipher->do_round
3.5 0.07 0.07  0.03 0.03  0.02 0.02     2  0.0348   0.0348            0 mysqli_connect
2.8 0.06 0.06  0.03 0.03  0.01 0.01   819  0.0001   0.0001            0 preg_replace
1.7 0.03 0.03  0.01 0.01  0.01 0.01  4477  0.0000   0.0000            0 substr
1.6 0.03 0.03  0.02 0.02  0.01 0.01   139  0.0002   0.0002            0 gettype
1.4 0.03 0.03  0.01 0.01  0.01 0.01  4168  0.0000   0.0000            0 hexdec
1.3 0.03 0.03  0.05 0.05  0.02 0.02    34  0.0008   0.0008            0 mysqli->query
1.1 0.02 0.03  0.01 0.01  0.01 0.01     2  0.0114   0.0153            0 ob_end_flush

So that configuration change seemed to have fixed that problem! Or maybe it was just an aberration?

In any event, I’ve found the reason for my sluggish site. It’s because I’ve been lazy and haven’t refactored pccipher yet. When I was doing the development work it was easiest to write the algorithm once and then define an interface for both the 32-bit and 64-bit versions of the code to implement. This meant that I had to do my math with functions rather than with operators, which is slooooow. Anyway, I knew that. I guess now that I can’t hide from it I’ll have to get around to fixing up that implementation and improving its performance.

I’m glad that was easy…

Web page HTML/CSS/JavaScript file size

I found this article (Some Guidelines for Determining Web Page and File Size) today which talks about the average size of HTML and other files on the web. According the article (and I’m not clear how they got their data) the average HTML file is 25k, JPEG 11.9k, GIF 2.9k, PNG 14.5k, SWF 32k, external scripts 11.2k and external CSS 17k with the average total size of a web page being 130k. Interesting stuff. Particularly that scripts are typically 11.2k given that jQuery is 90k.

I’m really struggling with a design decision at the moment, being that I’m not sure whether it’s better to embed CSS/JavaScript content or to link it. The thing is that if you link it then the client has to send extra HTTP requests (at least two) to get the content, which is overhead and takes time. The thing is, if your users are returning customers then they might already have the linked files in their cache, meaning they don’t need to send extra HTTP requests, or if they do maybe those requests won’t need to return content. But then maybe a browser will cache a file when it shouldn’t (this can be avoided with good design), or maybe the user’s connection will fail while loading the linked files and they’ll see an unstyled page in their browser.

So many pros and cons, and it’s all hypothetical… what I really need is data. Anyway, I don’t have data, nor do I really have the tools to get it. So given that I have to fly in the dark, here’s my plan:

When I’m processing a request for a user who doesn’t have a browser cookie set I will embed CSS and JavaScript in the HTML. This is because if their browser cookie isn’t set then this is their first request to my web-site, maybe ever, or maybe just in a while. Either way, it’s probably safe to assume they’re a first-time visitor so they won’t have any content in their cache and they’d need to send additional requests for linked files. So I can save those additional requests and hopefully make my web pages load faster for users who are probably one-off visitors.

But for regular users having to download the same content over and over in every request gets tired fast. The linked files can be about half the size of the page, so embedding doubles the size of each transfer. When I’m processing a request if the user’s browser cookie is already set then I’ll assume they’re a regular visitor and link my JavaScript files rather than embedding them. I’ll still embed CSS content though, because my CSS content is relatively small and I want to avoid errors where the page loads but the styles don’t.

Then I’ll make the system configurable so users can change their link/embed settings for CSS and JavaScript if they’re not happy with the defaults. Regular power users can use this feature to turn on linking for all content so pages load as fast as possible for them.