14

以下の内容のスクリプトを実行し、 5秒後に停止したときに、正しいスクリプト実行時間を取得するために経過時間を 2 で割る必要がある理由を誰か教えてもらえますか?

ignore_user_abort(true); set_time_limit(0); 

$begin_time = microtime(true);

$elapsed_time = 0;

while(!connection_aborted()) {
    echo ' ';
    flush();
    usleep(1000000);
}

$elapsed_time = microtime(true) - $begin_time;

$timer_seconds = $elapsed_time; //10 seconds

$timer_seconds = $elapsed_time / 2; //5 seconds


/*I am writing to a DB - but you can use this to test */
$fp = fopen('times.txt', 'w');
fwrite($fp, 'Time Elapsed: '.$timer_seconds);
fclose($fp);

$elapsed_timeなぜを 2 で割る必要があるのか​​分からなかったので、気軽にコードを試してみてください。多分私は何かを誤解しましたか?

助けてくれてありがとう

アップデート

誰でもこれを試すことができるようにコードを更新し、テキスト ファイルに書き込んで出力を表示できるようにしました。

4

6 に答える 6

13

実験:

元のコードからの大幅な変更:

1)implicit_flushを使用すると、すべてのバッファが何かを行う前にフラッシュされます。
2) スペースだけを出力する代わりに、コードは反復回数と 1023 バイトの他のデータを出力して、表示する出力が十分にあることをブラウザに伝えます。通常の既知のトリック。
3) 出力テキスト ファイルの時間を節約するだけでなく、コードが実行した反復の合計も節約されます。

使用したコード:

<?php
// Tricks to allow instant output
@ini_set('implicit_flush', 1);
for ($i = 0; $i < ob_get_level(); $i++)
    ob_end_flush();
ob_implicit_flush(1);

//Your Code starts here
ignore_user_abort(true);
set_time_limit(0); 

$begin_time = microtime(true);
$elapsed_time = 0;

while(!connection_aborted())
{
    //this I changed, so that a looooong string is outputted
    echo $i++.str_repeat(' ',1020).'<br/>';
    flush();
    usleep(1000000);
}

$elapsed_time = microtime(true) - $begin_time;
$timer_seconds = $elapsed_time; //10 seconds

//Writes to file the number of ITERATIONS too along with time
$fp = fopen('4765107.txt', 'w');
fwrite($fp, 'Time Elapsed: '.$timer_seconds);
fwrite($fp, "\nIterations: ".$i);
fclose($fp);
?>

ライブデモ:


私が得たもの:

1) コードが 10 回の反復で実行され、ブラウザの STOP ボタンがクリックされると、出力ファイルには 13 回の反復が表示され、約 13.01 秒かかります。

2) コードが 20 回の反復で実行され、ブラウザの STOP ボタンがクリックされると、出力ファイルには 23 回の反復が表示され、約 23.01 秒かかります。


推論と結論:

1) [STOP] ボタンをクリックしても、スクリプトは実際には停止しませんが、クリックしてから 2 ~ 4 秒後に停止します。したがって、ブラウザに表示されるものよりも多くの反復があります。

2) 反復回数は、出力ファイルに示されているように、実行にかかる秒数と同じです。

したがって、エラーはなく、明らかにバグもありません。STOP ボタンをクリックしてからスクリプトが実際に停止するまでの待ち時間です。


ノート:

1) サーバー: Linux VPS。
2) テスト済みのクライアント: Firefox および Chrome。
3) STOP をクリックしてから 2 ~ 4 秒後にスクリプトが終了するため、現在のテストの出力ファイルが更新されるまでに約 3 ~ 4 秒かかります。

于 2011-01-26T11:58:00.843 に答える
9

要約: (この投稿は、さまざまな手段をテストした結果、壮大なものになりました)

PHP は、切断を検出するか、出力を配信するために、通常 2 回の while ループの反復を行います。この遅延は、Web サーバー ソフトウェア、ホスト コンピューター、クライアント コンピューター、およびクライアント ブラウザーに起因する可能性がありますが、反復ごとのスリープによって異なるはずです。遅延は、PHP の内部実行または出力プロセス (おそらく小さな内部バッファーまたは割り込み処理プロセス) から発生している可能性があります。

エピック投稿:

[Refresh] またはURL 送信からの実行時間をカウントすることは、正確な開始点ではありません。最初に必要な手順がいくつでもあり、遅延が増える可能性があります。

  1. DNS ルックアップが必要 (TCP オーバーヘッドあり)
  2. サーバーとの TCP 接続が確立されました
  3. Web サーバーがスレッドまたは子を作成する
  4. Web サーバーがリクエストの処理方法を決定する
  5. PHP の起動が必要な場合があります
  6. PHP でソースをオペコードに変換する必要がある場合があります

そこで、[Refresh] -> [Stop] 時間を測定して PHP によって記録された数値と比較するのではなく、表示された出力記録された出力を測定しました。変更されたスクリプトは実行できず (一定回数の反復後に終了します)、既定のphp.iniバッファリングをクリアし、反復回数を画面とタイミング ファイルに報告します。$sleep効果を確認するために、さまざまな期間でスクリプトを実行しました。最終的なスクリプト:

<?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);
?>

結果: (複数の実行を連結し、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 は数値を表示しませんが、最終的にはほぼ一致する数値を表示します)
(Chrome はテスト中/テスト後に
何も表示しません) (Safari はテスト中/テスト後に何も
表示しません) (IE は何も表示しません)試験中/試験後)

各行の最初の数字は、[停止] が押されたときに画面に表示される数字を示します (手動で記録)。

いくつかのポイント:

  • スクリプトは各 while 反復の開始時にのみチェックするため、終了点は最も近い$sleep周期 (上記のスクリプトでは 1/10 秒) に量子化されusleepます。メソッドは完全な遅延ではないため、多少の変動があります。
  • 使用しているブラウザとサーバーによって違いが生じます。フラッシュのマニュアル ページには、「Web サーバーのバッファリング スキームをオーバーライドできない可能性があり、ブラウザのクライアント側のバッファリングには影響しない」と記載されています。次に、サーバーとクライアントの両方の問題について詳しく説明します。[サーバー: WinXPsp3 / Apache 2.2.17 / PHP 5.3.3 クライアント: WinXPsp3 / FireFox 3.6.13]

分析:

0.001 秒の遅延を除いて、[停止] と PHP がそれをキャッチする (または Firefox または Apache のレポート) 間に 2 反復の遅延が見られます。遅延が 0.001 秒の場合、少し異なります。平均は 4 回の反復または 0.004 秒です。これはおそらく、検出速度のしきい値に近づいています。

遅延時間が 0.1 秒以上の場合、実行時間は$sleep* {recorded iterations} とほぼ一致しています。

遅延時間が 0.1 秒未満の場合、スリープ時間よりも大きな実行遅延が見られます。これは、クライアント接続のチェック、インクリメント$i、テキストの出力、および反復ごとのバッファーのフラッシュのコストによるものと思われます。実行時間との不一致$i*$sleepはかなり線形であり、これらのタスクを完了するのに約 0.001 秒/反復かかることを示唆しています (0.01 秒のスリープでは 0.0008 ですが、0.001 秒のスリープでは 0.0010 になります。おそらくMALLOC/output の増加の結果です)。

于 2011-01-28T23:03:06.880 に答える
6

ブラウザの「停止」ボタンを押した瞬間であることに依存していますが、これが事実であることを確認したという証拠は示されていませconnection_aborted()ん. true実際、そうではありません。

ネットワークで「接続が中止されました」チェックがどのように行われるかを忘れてしまいました。アプリケーション (この場合は php) は、パイプに書き込もうとするまで、何が起こったのかわかりません。

のドキュメントconnection_abort()の最初のコメントは、次のように述べています。 "

connection_abort()したがって、この方法で確実に使用できるとは思いません。

安心してください、microtime()正常に動作します。

于 2011-01-22T00:37:32.417 に答える
4

connection_aborted()バッファが送信されたときにのみ切断を検出できます。ただしflush()、必ずしもバッファを送信するとは限りません。したがって、バッファがいっぱいになり、実際にフラッシュされるまで、ループは繰り返されます。

詳細については、指定された関数のマニュアル ページを参照してください。

于 2011-01-22T00:41:15.480 に答える
0

そのままの状態でスクリプトを使用しても、Ubuntu では正しく機能せず、Chrome を使用してページにアクセスします。ループが続き、Apache を再起動する必要がありました。反対に、先頭に ob_end_flush() を追加すると、その問題が解決され、タイマーは実際には正しいものになります。

ob_end_flush();
ignore_user_abort(true); 
set_time_limit(0);

$begin_time = microtime(true);

$elapsed_time = 0;

while(!connection_aborted()) {
    echo ' ';
    flush();
    usleep(1000000);
    error_log("looping");
}

$elapsed_time = microtime(true) - $begin_time;

$timer_seconds = $elapsed_time; 
error_log(var_export($timer_seconds, true));


$timer_seconds = $elapsed_time / 2; 
error_log(var_export($timer_seconds, true));

これを実行してエラー ログを見ると、最初の実行で $elpased_time が正しいことがわかります。割る必要もありません。コードがそのように動作する理由については、私のマシンでも機能しないため、わかりません。

于 2011-01-26T20:45:27.060 に答える
0

これは「問題」ではなく、「設計による」ものです

これは、http の機能の関数です。

Web ページをクライアント (ブラウザ) に送信する場合、サーバーは content-length ヘッダーを送信する必要があります。これで、スクリプトからすべてを取得するまで、コンテンツの長さを知ることができません。

そのため、サーバーはスクリプトが終了するまでスクリプト出力をバッファリングします。

これが気まぐれなところです。サーバーや同じサーバーの異なるバージョンに応じて、クライアントが定期的に切断されたかどうかを確認する場合としない場合があります。これは、サーバーのビジー状態によっても変わる場合があります。

PHP はクライアントへの接続を制御できません。接続がまだ存在するかどうかをサーバーに問い合わせることしかできません。サーバーは、真実を伝える場合と伝えない場合があります。そのため、スクリプトは引き続き実行されます (サーバーが認識していない可能性があります)。

では、スクリプトの START に ob_end_flush() を追加した後、なぜミクシの作業が行われたのでしょうか?

それは、チャンク転送と呼ばれる http の別の機能をオンにしたためです。これにより、コンテンツ長ヘッダーの特別なバージョンを使用して、データをチャンクで送信できます (実際にはそうではなく、次のチャンク長を送信するだけです)。

Wireshark で Mikushi のスクリプトを試すと、エンコードが表示されます。例をここに示します。

HTTP/1.1 200 OK
Date: Tue, 01 Feb 2011 11:52:35 GMT
Server: Apache/2.2.3 (CentOS)
X-Powered-By: PHP/5.1.6
Connection: close
Transfer-Encoding: chunked
Content-Type: text/html; charset=UTF-8


7 <== this is the content-length 
<pre>0

2 <== this is the content-length 
1

2 ditto ...
2

2
3

2
4

2
5

したがって、これは、実際のサーバーをテストするまで、サーバーがいつ接続を終了するかを知ることは不可能であることを意味します (はい、Tomalak :))。一人一人違うから。Web ブラウザでさえ、実際の終了を遅らせるようなことをする場合があり、それが問題をさらに混乱させる可能性があります。

DC

于 2011-02-01T12:05:26.613 に答える