Summary: (this post became epic as I tested various avenues)
PHP takes -typically two- while-loop iterations to detect a disconnect or to deliver output. This delay could come from the web server software, host computer, client computer, and client browser but then it should vary depending on the sleep per iteration. More likely the delay is coming from internal execution or output process of PHP (perhaps from a small internal buffer or the interrupt handling process).
Epic Post:
Counting your execution time from [Refresh] or URL-submit isn't exactly an accurate starting point - any number of steps may be required first and could add to the delay:
- DNS lookup required (with TCP overhead)
- TCP connection established with server
- Web Server creates a thread or child
- Web Server decides how to deal with the request
- PHP may need to start up
- PHP may need to convert your source into opcode
So rather than measuring [Refresh] -> [Stop] time and comparing it to the number recorded by PHP, I measured displayed output to recorded output - which reduces the delay measurement to mostly just within PHP (although Server/Browser will effect). The modified script can't run away (it terminates after a fixed number of iterations), clears the default php.ini
buffering, and reports an iteration count on screen and in the timing file. I ran the script with various $sleep
periods to see the effects. The final script:
<?php
date_default_timezone_set('America/Los_Angeles'); //Used by ob apparently
ignore_user_abort(true); //Don't terminate script because user leaves
set_time_limit(0); //Allow runaway script !danger !danger
while (@ob_end_flush()) {}; //By default set on/4K in php.ini
$start=microtime(true);
$n=1000;
$i=0;
$sleep=100000;// 0.1s
while(!connection_aborted() && $i<$n) {
echo "\n[".++$i."]";flush();
usleep($sleep);
}
$end=microtime(true);
file_put_contents("timing.txt",
"#\$sleep=".($sleep/1000000).
"s\n/ s=$start / e=$end ($i) / d=".($end-$start)."\n",
FILE_APPEND);
?>
Results: (multiple runs concatenated, run in Firefox)
# On-screen $i / start utime / end utime (current $i) / delta utime
#$sleep=1s
2 / s=1296251342.5729 / e=1296251346.5721 (4) / d=3.999242067337
3 / s=1296251352.9094 / e=1296251357.91 (5) / d=5.000559091568
#$sleep=0.1s
11 / s=1296251157.982 / e=1296251159.2896 (13) / d=1.3075668811798
8 / s=1296251167.5659 / e=1296251168.5709 (10) / d=1.0050280094147
16 / s=1296251190.0493 / e=1296251191.8599 (18) / d=1.810576915741
4 / s=1296251202.7471 / e=1296251203.3505 (6) / d=0.60339689254761
16 / s=1296251724.5782 / e=1296251726.3882 (18) / d=1.8099851608276
#$sleep=0.01s
42 / s=1296251233.0498 / e=1296251233.5217 (44) / d=0.47195816040039
62 / s=1296251260.4463 / e=1296251261.1336 (64) / d=0.68735003471375
150 / s=1296251279.2656 / e=1296251280.901 (152) / d=1.6353850364685
379 / s=1296252444.7587 / e=1296252449.0108 (394) / d=4.2521529197693
#$sleep=0.001s
337 / s=1296251293.4823 / e=1296251294.1515 (341) / d=0.66925406455994
207 / s=1296251313.7312 / e=1296251314.1445 (211) / d=0.41328597068787
792 / s=1296251324.5233 / e=1296251326.0915 (795) / d=1.5682451725006
(Opera doesn't display numbers during, but displays final numbers which roughly match)
(Chrome doesn't display anything during/after the test)
(Safari doesn't display anything during/after the test)
(IE doesn't display anything during/after the test)
The first number on each line indicates the number displayed on screen once [stop] was pressed (manually recorded).
A few points:
- Your stop point is quantized to the nearest
$sleep
period (1/10s in the above script) because the script only checks at the beginning of each while iteration, there's some variation because the usleep
method isn't a perfect delay.
- The browser and server you're using makes a difference. The flush manual page notes "may not be able to override the buffering scheme of your web server, and has no effect on any client-side buffering in the browser." Then goes into more detail about both server and client issues. [Server: WinXPsp3 / Apache 2.2.17 / PHP 5.3.3 Client: WinXPsp3 / FireFox 3.6.13]
Analysis:
In all but the 0.001s delay we're seeing a 2 iteration delay between [stop] and PHP catching it (or Firefox or Apache reporting). With a 0.001s delay it varies a bit, the average being ~4 iterations or 0.004s - this is probably getting close to detection speed threshold.
When the delay time is 0.1s or above we're seeing an execution time closely matching $sleep
* {recorded iterations}.
When the delay time is below 0.1s we're seeing execution delays greater than the sleep time. This is likely from the cost of checking client-connection, incrementing $i
, outputting text, and flushing the buffer per iteration. The discrepancy between the execution time and $i*$sleep
is pretty linear suggesting it takes ~0.001s/iteration to complete these tasks (with a 0.01s sleep it's 0.0008, while a 0.001s sleep works out to 0.0010 - probably a result of increased MALLOC
/output).
microtime_float
function. Use thetrue
paramter tomicrotime(true);
which was introduced in PHP 5.0.0... Also, how are you counting the5
seconds? – Fishmongermicrotime(true);
– Lifegivingecho
andflush
are not free. So don't expect 5 loop iterations to take exactly 5 seconds. I'm not saying it should be much more than that (I'd be very surprised at anything more than 5.25 seconds or so), but it won't be 5 flat... – Fishmongerecho ' '; flush();
. – Lifegivingusleep
time up. The cost offlush
andecho
should be pretty constant per iteration. So if you make the sleep time 5 seconds (for a total sleep time of 25 seconds), that would cause 5 iterations. So if your output is 30 seconds, you knowecho
andflush
combine for 1 second per iteration (ludicrous, I know)... – Fishmonger50.000932
!? What the hell. – Lifegivingob_implicit_flush();
but that made no difference either. Is that what you meant? – Lifegiving