$30 off During Our Annual Pro Sale. View Details »

Asynchronous Programming Fundamentals

Asynchronous Programming Fundamentals

Samantha Quiñones

October 08, 2016
Tweet

More Decks by Samantha Quiñones

Other Decks in Technology

Transcript

  1. Asynchronous Programing
    Fundamentals
    Samantha Quiñones

    View Slide

  2. Samantha Quiñones
    Principal Software Engineer
    Code and Community Champion
    AOL Inc.
    @ieatkillerbees
    @squinones
    ɂ http://squinones.github.io
    ɔ https://joind.in/talk/3a1de

    View Slide

  3. Synchronous Operations
    declare(strict_types = 1);
    //process command line input
    $filename = $argv[1] ?? "data.txt";
    $fd = fopen($filename, 'r+');
    $data = fread($fd, filesize($filename));
    fclose($fd);
    echo $data;
    assign $filename
    open $filename
    read file
    write to stdout

    View Slide

  4. What is Asynchrony?
    • Events that occur outside the
    main program flow
    • Concurrency
    • Non-blocking I/O
    • Parallelism
    Procedure
    Procedure
    Procedure
    Async Event
    Async Event

    View Slide

  5. Asynchronous Events
    declare(ticks = 1);
    pcntl_signal(SIGUSR1, function (int $signal) {
    echo "Caught signal " . $signal . PHP_EOL;
    });
    echo "Running as PID " . posix_getpid() . PHP_EOL;
    while (true) {
    echo "Waiting for signals..." . PHP_EOL;
    sleep(3);
    }

    View Slide

  6. Asynchronous Events
    $command = "du -sk /var/log/* | sort -n | tail -1 2>&1";
    echo shell_exec($command); Primary
    Process
    Secondary
    Process

    View Slide

  7. Asynchronous programming describes a set of techniques designed
    to parallelize work and minimize idleness.
    Operation 1 Operation 2 Operation 3
    Operation 1
    Operation 2
    Operation 3
    Op1 Op 2 Op 3 Op 2 Op1 Op 3 Op1

    View Slide

  8. Async in PHP
    • Promises/Futures
    • Asynchronous I/O
    • Hack Async
    • Threads (haha, only joking!)

    View Slide

  9. A Promise for the Future

    View Slide

  10. A Rose By Another Name
    • Promises
    • Futures
    • Delays
    • Deferred

    View Slide

  11. Promise
    Fulfilled
    Rejected

    View Slide

  12. Multi-RPC

    View Slide

  13. Client sends a payload
    {
    requests: [
    posts.query({“status”: “published”}),
    slideshows.query({“status”: “published”})
    ]
    }
    API executes procedures asynchronously
    When procedures are all done, API returns
    {
    responses: [
    posts.query: { … },
    slideshows.query: { … })
    ]
    }

    View Slide

  14. What about HTTP Requests?
    {% set request1 = http('http://example.com/service-foo') %}
    {% set request2 = http('http://example.com/service-bar') %}
    {% set request3 = http('http://example.com/service-baz') %}

    Foo{{ request1.body.field }}
    Bar{{ request2.body.field }}
    Baz{{ request3.body.field }}

    233ms
    318ms
    188ms
    739ms

    View Slide

  15. $pool = new Pool($client, $requests, [
    'concurrency' => 10,
    'fulfilled' => $complete,
    'rejected' => $complete
    ]);
    $pool->promise()->wait();
    For each request, we create a Promise.
    A Promise represents an eventual result.
    We wait for all Promises to be Fulfilled or Rejected
    Gather requests
    Execute request 1
    Execute request 2
    Execute request 3
    Execute request 4
    Return responses

    View Slide

  16. require __DIR__ . '/vendor/autoload.php';
    use GuzzleHttp\Promise\Promise;
    $promise = new Promise();
    $promise->then(
    // onFulfilled
    function ($value) {
    echo "Promise fulfilled successfully! - " . $value;
    },
    // onRejected
    function ($reason) {
    echo "Promise rejected! :( - " . $reason;
    }
    );
    $promise->resolve("OpenWest");

    View Slide

  17. The Point of Promises
    function doTheThing(Thing $thing): string {
    $done = $this->do();
    if (!$done) {
    throw new NotDoneException("Didn't do the thing! :(");
    }
    return $done;
    }
    try {
    $doneThing = doTheThing(new Thing());
    echo $doneThing;
    } catch (NotDoneException $exc) {
    echo $exc->getMessage();
    }

    View Slide

  18. The Point of Promises
    $delayed = new Delayed();
    $delayed
    ->then(
    function (Thing $thing): string {
    $done = $this->do();
    if (!$done) {
    throw new NotDoneException("Didn't do the thing! :(");
    }
    return $done;
    }
    )
    ->capture(
    function (NotDoneException $e) {
    echo $e->getMessage();
    }
    )
    ->done(
    function (string $value) {
    echo $value;
    }
    )
    ;

    View Slide

  19. Asynchronous Functions
    • Can’t return values because there’s nothing to receive them
    • Can’t throw exceptions because there’s nothing to catch them
    • Promises allow us to compose the results of async functions and
    handle errors from async functions.
    • Promises are a paradigmatic translation layer!

    View Slide

  20. Blocking I/O
    • When we read from or write to disk
    • When we read from or write to a network connection
    • Communicating with an external service (DB, API, etc)

    View Slide

  21. Format Time (s) Equivalent Distance Equivalent Time
    1 CPU Cycle 0.3 ns
    1 m
    (1 step)
    1 second
    L1 Cache 0.9 ns
    3 m
    (3 steps)
    3 seconds
    Main Memory 120 ns
    360 m
    (US Capitol Grounds)
    6 minutes
    SSD 50 µs
    170 km
    (Prishtina)
    2 days
    HDD 5 ms
    13,000 km
    (Falkland Islands)
    5 months

    View Slide

  22. Event Loops

    View Slide

  23. declare(strict_types = 1);
    $filename = $argv[1] ?? "data/data.txt";
    $fd = fsockopen('localhost', 4000);
    $data = fread($fd, 512);
    fclose($fd);
    echo $data;

    View Slide

  24. declare(strict_types = 1);
    //process command line input
    $filename = $argv[1] ?? "data/data.txt";
    $fd = fsockopen('localhost', 4000);
    stream_set_blocking($fd, false); // enable non-blocking calls
    $data = fread($fd, 512);
    fclose($fd);
    echo $data;

    View Slide

  25. declare(strict_types = 1);
    //process command line input
    $filename = $argv[1] ?? "data/data.txt";
    $fd = fsockopen('localhost', 4000);
    stream_set_blocking($fd, false); // enable non-blocking calls
    $data = "";
    while (strlen($data) < 512) {
    $read = [$fd];
    $write = [];
    $except = [];
    if (stream_select($read, $write, $except, 0) > 0) {
    $chunk = "";
    do {
    $chunk = fread($fd, 512 - strlen($data));
    $data .= $chunk;
    } while ($chunk !== "" && strlen($data) < 512);
    }
    }
    fclose($fd);
    echo $data;

    View Slide

  26. Event Loops in PHP
    • ReactPHP (mature, supports stream_select & libevent)
    • Icicle (newer, supports stream_select, libevent, & libuv)

    View Slide

  27. Non-Blocking Server
    require __DIR__.'/vendor/autoload.php';
    $loop = React\EventLoop\Factory::create();
    $socket = new React\Socket\Server($loop);
    $socket->on('connection', function ($conn) {
    usleep(500000);
    $conn->write(str_repeat('a', 1024));
    $conn->end();
    });
    echo "Socket server listening on port 4000.\n";
    echo "You can connect to it by running: telnet localhost 4000\n";
    $socket->listen(4000);
    $loop->run();

    View Slide

  28. Events
    • File descriptor (socket) polling. Incoming connections, data, etc.
    • Callbacks, aka ticks
    • One-off timers
    • Periodic timers

    View Slide

  29. Callbacks
    $socket->on('connection', function ($conn) {
    usleep(500000);
    $conn->write(str_repeat('a', 1024));
    $conn->end();
    });
    on the event ‘connection’ execute the given function

    View Slide

  30. App tells kernel “read
    from socket 42”
    Kernel enqueues
    request
    App continues to run
    Kernel reads data
    and throws event
    App handles event
    (executes callback)

    View Slide

  31. require __DIR__ . '/vendor/autoload.php';
    $loop = React\EventLoop\Factory::create();
    $socket = new React\Socket\Server($loop);
    $http = new \React\Http\Server($socket);
    $counter = 0;
    $http->on(
    'request',
    function (\React\Http\Request $request, \React\Http\Response $response) use (&$counter, &$loop) {
    $counter++;
    echo "Handling request number $counter" . PHP_EOL;
    $headers = ['Content-Type' => 'text/html'];
    $response->writeHead(200, $headers);
    $process = new \React\ChildProcess\Process('fortune');
    $process->start($loop);
    $process->stdout->on(
    'data',
    function ($output) use (&$response) {
    $response->write($output);
    }
    );
    $process->on(
    'exit',
    function ($rc, $signal) use (&$response) {
    $response->end();
    }
    );
    }
    );
    echo "Socket server listening on port 4000.\n";
    echo "You can connect to it by running: telnet localhost 4000\n";
    $socket->listen(4000);
    $loop->run();

    View Slide

  32. What about Hack?

    View Slide

  33. Hack’s Approach
    • Async functions are marked with an ‘async’ keyword
    • Functions return an `Awaitable`
    • Awaitables are similar to promises - they represent a future value

    View Slide

  34. function fibonacci(int $n): float
    {
    $n1 = 0;
    $n2 = 1;
    $n3 = 1;
    for ($i = 2; $i < $n; ++$i) {
    $n3 = $n1 + $n2;
    $n1 = $n2;
    $n2 = $n3;
    }
    return (float)$n3;
    }
    function getData(): string
    {
    return file_get_contents('data/data.txt');
    }
    $fib = fibonacci(1024);
    $data = getData();
    echo getData() . PHP_EOL . $fib . PHP_EOL;

    View Slide

  35. async function fibonacci(int $n): Awaitable
    {
    $n1 = 0;
    $n2 = 1;
    $n3 = 1;
    for ($i = 2; $i < $n; ++$i) {
    $n3 = $n1 + $n2;
    $n1 = $n2;
    $n2 = $n3;
    }
    return (float)$n3;
    }
    async function getData(): Awaitable
    {
    return file_get_contents('data/data.txt');
    }
    async function printData(): Awaitable
    {
    $fib = fibonacci(1024);
    $data = getData();
    $results = await \HH\Asio\v([$fib, $data]);
    echo join(PHP_EOL, $results);
    }
    printData();

    View Slide

  36. async function fibonacci(int $n): Awaitable
    {
    $n1 = 0;
    $n2 = 1;
    $n3 = 1;
    for ($i = 2; $i < $n; ++$i) {
    $n3 = $n1 + $n2;
    $n1 = $n2;
    $n2 = $n3;
    if (!($n%100)) {
    await RescheduleWaitHandle::create(RescheduleWaitHandle::QUEUE_DEFAULT, 0);
    }
    }
    return (float)$n3;
    }
    async function getData(): Awaitable
    {
    return file_get_contents('data/data.txt');
    }
    async function printData(): Awaitable
    {
    $fib = fibonacci(1024);
    $data = getData();
    $results = await \HH\Asio\v([$fib, $data]);
    echo join(PHP_EOL, $results);
    }
    printData();

    View Slide

  37. Why Async?
    • Tackling the C10K (or C10M!) problem
    • Minimize latency and idleness - these are cost killers!
    • (Possibly) cleaner separations of concerns

    View Slide

  38. Async & PHP
    • async-interop
    • Core support?

    View Slide

  39. Things to Remember
    • Async is not always faster than sync. Sometimes it’s slower.
    • It can be hard to conceptualize the benefits of async in the mind
    • It can be hard to recognize the benefits of async on a laptop

    View Slide

  40. References
    • https://icicle.io/docs/manual/introduction/
    • http://php.net/manual/en/function.stream-select.php
    • https://blog.domenic.me/youre-missing-the-point-of-promises/
    • https://www.sitepoint.com/an-introduction-into-event-loops-in-php/
    • http://blog.carbonfive.com/2013/10/27/the-javascript-event-loop-explained/
    • http://blog.kgriffs.com/2012/09/18/demystifying-async-io.html
    • https://docs.hhvm.com/hack/async/introduction

    View Slide

  41. New Tweeps!
    Also, thanks guys!

    View Slide