LoginSignup
4
2

More than 5 years have passed since last update.

hack の async / await を利用して非同期処理を行う

Last updated at Posted at 2017-04-29

PHPを高速で実行できるHHVMですが、プログラムをhackで記述する事で更なる恩恵を得る事ができます。
その中でも特に画期的なのが async / await を使った非同期処理の実現です。
公式のマニュアルから抜粋してその利点をまとめます。

出典
-Async / introduction

Asyncとは

Hackはasyncという協調的マルチタスキングをプログラムから利用できる機能を提供します。これによりコードからasync機構を使うことで入出力(I/O)レイテンシやデータの取得処理を隠蔽できます。コード中でネットワークアクセスやデータベースのクエリを待機する処理がある場合、asyncは入出力に関わらない他の処理を行う事で、待機時間を最小化します。
asyncはマルチスレッドではありません。HHVMは PHP/Hack のコードを一つのメインリクエストスレッドで実行します。しかしMySQLのクエリのようなその他の処理はコードの実行時間を使わずに実行できるのです。

制限

  • 全ての PHP/HAck コードはメインリクエストスレッドで実行される。
  • ブロッキングなAPI (例 mysql_query(), sleep()) は自動的に非同期な処理に変換されません。 これはそれぞれの実行順序が変わる事によって予期しない影響が起こることが安全ではない為です。

Asyncの例:cURL

asyncを使わないで2つのcURLリクエストを行うと次のようになります。

<?hh

namespace Hack\UserDocumentation\Async\Intro\Examples\NonAsyncCurl;

function curl_A(): mixed {
  $ch = curl_init();
  curl_setopt($ch, CURLOPT_URL, "http://example.com/");
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  return curl_exec($ch);
}

function curl_B(): mixed {
  $ch = curl_init();
  curl_setopt($ch, CURLOPT_URL, "http://example.net/");
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  return curl_exec($ch);
}

function main(): void {
  $start = microtime(true);
  $a = curl_A();
  $b = curl_B();
  $end = microtime(true);
  echo "Total time taken: " . strval($end - $start) . " seconds" . PHP_EOL;
}

main();

出力

Total time taken: 1.050155878067 seconds

-3v4l.orgでの実行例

上記の例ではcurl_A()の中のcurl_exec()は全ての処理をブロックしています。その結果、curl_B()curl_A()と独立した処理であってもcurl_A()の処理が終わるまで待ってから実行されます。

実行の流れ

幸い、HHVMは非同期版のcurl_exec()を提供しています。

<?hh

namespace Hack\UserDocumentation\Async\Intro\Examples\Curl;

async function curl_A(): Awaitable<string> {
  $x = await \HH\Asio\curl_exec("http://example.com/");
  return $x;
}

async function curl_B(): Awaitable<string> {
  $y = await \HH\Asio\curl_exec("http://example.net/");
  return $y;
}

async function async_curl(): Awaitable<void> {
  $start = microtime(true);
  list($a, $b) = await \HH\Asio\v(array(curl_A(), curl_B()));
  $end = microtime(true);
  echo "Total time taken: " . strval($end - $start) . " seconds" . PHP_EOL;
}

\HH\Asio\join(async_curl());

出力

Total time taken: 0.74790596961975 seconds

-3v4l.orgでの実行例

非同期版のcurl_exec()はcURLのレスポンスを待っている間にスケジューラーに他のコードの実行を許します。この場合は非同期的にcurl_B()をさらに実行します。スケジューラーが処理を実行するともう一つの非同期なcurl_exec()が実行されます。HTTPリクエストは演算処理よりは遅い為、メインスレッドはリクエストが終わるまでアイドルします。

実行順序は大体、上記のようになりますが、そうでない場合もあります。例えば、curl_B()のリクエストがcurl_A()のHTTPより大幅に速い場合はcurl_B()の処理がcurl_A()の処理が完了する前に完了します。
非同期処理による速度向上の恩恵はネットワークの状態はDNSのキャッシュなどにより変動的です。ですが劇的な効果になりえます。

まとめ

HHVMを使ってコードを実行する場合、入出力に関する部分だけでもhackを使って書き換える事で非同期処理を実現する事ができます。リモートのWEBAPIコールやデータベースの処理を順次実行するようなアプリケーションの場合は大幅な高速化を実現できる可能性があります。
非同期処理をPHPで実現する為に複雑なバックエンドを構築する必要がある場合は、HHVMの備える非同期処理の利用も検討してみるとよいのではないでしょうか。

また非同期処理を実現する際には\HH\Asio配下にあるコレクションの処理や関数を確認しておくと良いでしょう。
- \HH\Asio\m
- 非同期対応の拡張 mysql, memcache, curl, stream

4
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
2