Hello:
We have a very high-volume site that runs across a cluster of several
machines which needs to have access to our back end storage platform, which
needless to say is very busy. I have been given the task of reducing load to
the filer. Due to the fact that our application is rather large and includes
lots of files which reside on our filer, php would be a good place to cut down load.
We currently have a rather unique setup. We are running php with
fast-cgi support via sapi/cgi/cgi_main.c as well as APC for opcode/user
caching**. This allows us to run php as a daemon which reduces system calls
and retains persistence with APC. Our setup has been working quite well.
However, there is one behavior that is less than optimal.
Whenever one of our pages is accessed, it appears*** that the requested
file and all included files are opened, which results in several open calls to our filer. As I mentioned, we are running APC which seems to be doing a
nice job of op-code caching. It would be ideal if somehow I could modify php
to first check APC cache for the pre-compiled opcode before attempting to
open the file. This would be a huge win on our cluster, since each hit
currently generates 5-10 open calls.
Now please bear with me because I am new to the Zend / main tree of
the php source. If my understanding is correct, the basic flow of
cgi/php goes something like this:
request
some variable init.
module_init <-apc overide compile_file w/my_compile_file
php_fopen_primary_script(&file_handle TSRMLS_CC);
php_execute_script(&file_handle TSRMLS_CC);
VCWD_CHDIR_FILE(primary_file->filename);
# i think included_files gets opened here someplace
# i do not understand that magic yet.
zend_execute_scripts()
zend_compile_file()
compile_file || my_compile_file() <-- apc
# it looks like the std compile_file
# may do some zend_open cmds here via
# open_file_for_scaning()
zend_destroy_file_handle() <-- destroy before execute?
zend_execute()
php_request_shutdown()
FCGX_Finish_r;
I have a few questions:
- When exactly do the files get opened/read? And is this a necessary
step if you already have the op-code. - Is it possible to check apc_cache before opening files? If it
is possible, how/where do you think it could be best implemented?
Is there some similar interface to the my_compile_file trick used
to override zend_compile_file by apc? Perhaps something like
my_open_file, which I could then add support for into the apc module? - would changing utility_functions->fopen_function; to point to
my_fopen_function in place of zend_open work.
Any input would be greatly appreciated.
-James
** ( mod_php and the likes are not an option for us as we have our own
custom web server which we cannot do without :) )
*** when i truss one of the php daemon process i can clearly see that
it opens and reads the requested file. It also opens all the
included/required files but does not seem read them.
I think the main hint you need is to never use require_once/include_once
with an opcode cache. Those are the only ones that will cause an open()
call. You should only be seeing a single stat()
call per file under APC
(needed to get the inode+device hash key for the shared memory lookup).
-Rasmus
James M Luedke wrote:
Hello:
We have a very high-volume site that runs across a cluster of several
machines which needs to have access to our back end storage platform, which
needless to say is very busy. I have been given the task of reducing load to
the filer. Due to the fact that our application is rather large and includes
lots of files which reside on our filer, php would be a good place to cut down load.We currently have a rather unique setup. We are running php with
fast-cgi support via sapi/cgi/cgi_main.c as well as APC for opcode/user
caching**. This allows us to run php as a daemon which reduces system calls
and retains persistence with APC. Our setup has been working quite well.
However, there is one behavior that is less than optimal.Whenever one of our pages is accessed, it appears*** that the requested
file and all included files are opened, which results in several open calls to our filer. As I mentioned, we are running APC which seems to be doing a
nice job of op-code caching. It would be ideal if somehow I could modify php
to first check APC cache for the pre-compiled opcode before attempting to
open the file. This would be a huge win on our cluster, since each hit
currently generates 5-10 open calls.Now please bear with me because I am new to the Zend / main tree of
the php source. If my understanding is correct, the basic flow of
cgi/php goes something like this:request
some variable init.
module_init <-apc overide compile_file w/my_compile_file
php_fopen_primary_script(&file_handle TSRMLS_CC);
php_execute_script(&file_handle TSRMLS_CC);
VCWD_CHDIR_FILE(primary_file->filename);
# i think included_files gets opened here someplace
# i do not understand that magic yet.
zend_execute_scripts()
zend_compile_file()
compile_file || my_compile_file() <-- apc
# it looks like the std compile_file
# may do some zend_open cmds here via
# open_file_for_scaning()
zend_destroy_file_handle() <-- destroy before execute?
zend_execute()
php_request_shutdown()
FCGX_Finish_r;I have a few questions:
- When exactly do the files get opened/read? And is this a necessary
step if you already have the op-code.- Is it possible to check apc_cache before opening files? If it
is possible, how/where do you think it could be best implemented?
Is there some similar interface to the my_compile_file trick used
to override zend_compile_file by apc? Perhaps something like
my_open_file, which I could then add support for into the apc module?- would changing utility_functions->fopen_function; to point to
my_fopen_function in place of zend_open work.Any input would be greatly appreciated.
-James
** ( mod_php and the likes are not an option for us as we have our own
custom web server which we cannot do without :) )*** when i truss one of the php daemon process i can clearly see that
it opens and reads the requested file. It also opens all the
included/required files but does not seem read them.
Or simply use eAccelerator which does not cause those open
calls.
Rasmus, is that behaviour by design? I deinstalled APC
because of this 'characteristic' last week. It was killing a
high-traffic site.
- Sascha
I think the main hint you need is to never use require_once/include_once with
an opcode cache. Those are the only ones that will cause an open() call. You
should only be seeing a singlestat()
call per file under APC (needed to get
the inode+device hash key for the shared memory lookup).-Rasmus
James M Luedke wrote:
Hello:
We have a very high-volume site that runs across a cluster of several
machines which needs to have access to our back end storage platform, which
needless to say is very busy. I have been given the task of reducing load to
the filer. Due to the fact that our application is rather large and includes
lots of files which reside on our filer, php would be a good place to cut
down load.We currently have a rather unique setup. We are running php with
fast-cgi support via sapi/cgi/cgi_main.c as well as APC for opcode/user
caching**. This allows us to run php as a daemon which reduces system calls
and retains persistence with APC. Our setup has been working quite well.
However, there is one behavior that is less than optimal.
Whenever one of our pages is accessed, it appears*** that the requested
file and all included files are opened, which results in several open calls
to our filer. As I mentioned, we are running APC which seems to be doing a
nice job of op-code caching. It would be ideal if somehow I could modify php
to first check APC cache for the pre-compiled opcode before attempting to
open the file. This would be a huge win on our cluster, since each hit
currently generates 5-10 open calls.Now please bear with me because I am new to the Zend / main tree of the
php source. If my understanding is correct, the basic flow of cgi/php goes
something like this:
request
some variable init.
module_init <-apc overide compile_file w/my_compile_file
php_fopen_primary_script(&file_handle TSRMLS_CC);
php_execute_script(&file_handle TSRMLS_CC);
VCWD_CHDIR_FILE(primary_file->filename);
# i think included_files gets opened here someplace
# i do not understand that magic yet.
zend_execute_scripts()
zend_compile_file()
compile_file || my_compile_file() <-- apc
# it looks like the std compile_file
# may do some zend_open cmds here via
# open_file_for_scaning()
zend_destroy_file_handle() <-- destroy before execute?
zend_execute()
php_request_shutdown()
FCGX_Finish_r;I have a few questions:
- When exactly do the files get opened/read? And is this a necessary step
if you already have the op-code.- Is it possible to check apc_cache before opening files? If it is
possible, how/where do you think it could be best implemented? Is there some
similar interface to the my_compile_file trick used to override
zend_compile_file by apc? Perhaps something like my_open_file, which I could
then add support for into the apc module?- would changing utility_functions->fopen_function; to point to
my_fopen_function in place of zend_open work.Any input would be greatly appreciated.
-James
** ( mod_php and the likes are not an option for us as we have our own
custom web server which we cannot do without :) )*** when i truss one of the php daemon process i can clearly see that
it opens and reads the requested file. It also opens all the
included/required files but does not seem read them.
Sascha Schumann wrote:
Or simply use eAccelerator which does not cause those open calls. Rasmus, is that behaviour by design? I deinstalled APC because of this 'characteristic' last week. It was killing a high-traffic site. - Sascha
I think the main hint you need is to never use require_once/include_once with
an opcode cache. Those are the only ones that will cause an open() call. You
should only be seeing a singlestat()
call per file under APC (needed to get
the inode+device hash key for the shared memory lookup).-Rasmus
This explains why we did not get nearly the performance from APC that we
did from eAccelerator. I thought it was odd.
Brian.
Sascha Schumann wrote:
Or simply use eAccelerator which does not cause those open calls.
Which doesn't support PHP 5.1 very well.
Rasmus, is that behaviour by design? I deinstalled APC because of this 'characteristic' last week. It was killing a high-traffic site.
Look at the code for require_once/include_once. It has nothing to do
with APC. The code is:
zend_file_handle file_handle;
if(SUCCESS == zend_stream_open(inc_filename->value.str.val,
&file_handle)) {
if(!file_handle.opened_path) {
file_handle.opened_path = estrndup(inc_filename->value.str.val,
inc_filename->value.str.len);
}
if(zend_hash_add(&EG(included_files),
file_handle.opened_path,
strlen(file_handle.opened_path)+1,
(void *)&dummy, sizeof(int), NULL)==SUCCESS) {
op_array = zend_compile_file(&file_handle, ZEND_INCLUDE);
zend_destroy_file_handle(&file_handle);
} else {
zend_file_handle_dtor(&file_handle);
failure_retval=1;
}
} else {
if(opline->op2.u.constant.value.lval==ZEND_INCLUDE_ONCE) {
zend_message_dispatcher(ZMSG_FAILED_INCLUDE_FOPEN,
inc_filename->value.str.val);
} else {
zend_message_dispatcher(ZMSG_FAILED_REQUIRE_FOPEN,
inc_filename->value.str.val);
}
}
In contrast, a straight include/require just does:
op_array = compile_filename(opline->op2.u.constant.value.lval,
inc_filename);
Nothing else. An opcode cache doesn't kick in until the compiler hook
is invoked either by zend_compile_filename or zend_compile_file.
Anything that happens before is outside of its control, and since there
is no hook here to override this require_once behaviour I really don't
see how an opcode cache can be avoiding this zend_stream_open() call.
The way the _once code is written makes a lot of sense for non-opcode
cached requests where it would be silly to do an initial stat to check
if the file has been included and then go back and open it. Doing it
all in one call makes sense assuming multiple includes is not the common
case and that an open isn't significantly more expensive than a stat.
However, for an opcode cache this is of course far from ideal and it is
an area I have on my TODO list for trying to figure out a nice way to
work around it. If eaccel has somehow figured out a way around this I'd
like to see it. I have never looked at that code and I don't
particularly want to, so if someone could provide a rough summary of how
they manage to override this _once logic I would appreciate it.
-Rasmus
Sascha Schumann wrote:
Or simply use eAccelerator which does not cause those open calls.
Which doesn't support PHP 5.1 very well.
Which is pretty fine for us PHP 4.4 users.
The site where I reinstalled eAccelerator does not use
include_/require_once. Neither the invoked script nor a few
included files were cached at all, despite having lots of
free memory available. That looked like a bug to me.
- Sascha
Sascha Schumann wrote:
Sascha Schumann wrote:
Or simply use eAccelerator which does not cause those open calls.
Which doesn't support PHP 5.1 very well.
Which is pretty fine for us PHP 4.4 users. The site where I reinstalled eAccelerator does not use include_/require_once. Neither the invoked script nor a few included files were cached at all, despite having lots of free memory available. That looked like a bug to me.
Well, tracing through and submitting a useful bug report would be the
right approach then. Obviously if something is not being cached there
is a problem somewhere. Current APC is serving up billions of pages a
day on the world's most visited web site. It works pretty damn well.
There are still some edge cases that aren't handled well, but the core
is solid.
-Rasmus
I tested this and you are correct. This should make a big difference.
I learn something new every day :)
Thanks a million.
-James
---- Rasmus Lerdorf rasmus@lerdorf.com wrote:
I think the main hint you need is to never use require_once/include_once
with an opcode cache. Those are the only ones that will cause an open()
call. You should only be seeing a singlestat()
call per file under APC
(needed to get the inode+device hash key for the shared memory lookup).-Rasmus
James M Luedke wrote:
Hello:
We have a very high-volume site that runs across a cluster of several
machines which needs to have access to our back end storage platform, which
needless to say is very busy. I have been given the task of reducing load to
the filer. Due to the fact that our application is rather large and includes
lots of files which reside on our filer, php would be a good place to cut down load.We currently have a rather unique setup. We are running php with
fast-cgi support via sapi/cgi/cgi_main.c as well as APC for opcode/user
caching**. This allows us to run php as a daemon which reduces system calls
and retains persistence with APC. Our setup has been working quite well.
However, there is one behavior that is less than optimal.Whenever one of our pages is accessed, it appears*** that the requested
file and all included files are opened, which results in several open calls to our filer. As I mentioned, we are running APC which seems to be doing a
nice job of op-code caching. It would be ideal if somehow I could modify php
to first check APC cache for the pre-compiled opcode before attempting to
open the file. This would be a huge win on our cluster, since each hit
currently generates 5-10 open calls.Now please bear with me because I am new to the Zend / main tree of
the php source. If my understanding is correct, the basic flow of
cgi/php goes something like this:request
some variable init.
module_init <-apc overide compile_file w/my_compile_file
php_fopen_primary_script(&file_handle TSRMLS_CC);
php_execute_script(&file_handle TSRMLS_CC);
VCWD_CHDIR_FILE(primary_file->filename);
# i think included_files gets opened here someplace
# i do not understand that magic yet.
zend_execute_scripts()
zend_compile_file()
compile_file || my_compile_file() <-- apc
# it looks like the std compile_file
# may do some zend_open cmds here via
# open_file_for_scaning()
zend_destroy_file_handle() <-- destroy before execute?
zend_execute()
php_request_shutdown()
FCGX_Finish_r;I have a few questions:
- When exactly do the files get opened/read? And is this a necessary
step if you already have the op-code.- Is it possible to check apc_cache before opening files? If it
is possible, how/where do you think it could be best implemented?
Is there some similar interface to the my_compile_file trick used
to override zend_compile_file by apc? Perhaps something like
my_open_file, which I could then add support for into the apc module?- would changing utility_functions->fopen_function; to point to
my_fopen_function in place of zend_open work.Any input would be greatly appreciated.
-James
** ( mod_php and the likes are not an option for us as we have our own
custom web server which we cannot do without :) )*** when i truss one of the php daemon process i can clearly see that
it opens and reads the requested file. It also opens all the
included/required files but does not seem read them.