socket: Performance degradation of stream handling on Windows OS

Here’s simple test script

<?php
require 'vendor/autoload.php';

$t1 = time();

\Amp\Loop::run(function () use ($t1) {
    \Amp\ByteStream\pipe(
        new \Amp\ByteStream\ResourceInputStream(fopen("C:\Downloads\Rogue One A Star Wars Story 2016 x264 HDTS AAC(Deflickered)-DDR\sample-Rogue One A Star Wars Story 2016 x264 HDTS AAC(Deflickered)-DDR.mkv", 'r')),
        new \Amp\ByteStream\ResourceOutputStream(fopen('file', 'w'))
    )->onResolve(function() use ($t1) {
        echo 'Done in '.(time() - $t1);
    });

    $server = Amp\Socket\listen("127.0.0.1:0");

    while ($socket = yield $server->accept()) {
//         do nothing
    }
});

Input is 19MB file. Code as is takes 241s to finish. If I comment out the while loop, file is copied in ~1 second!

Windows 7, PHP 7.2.0 (cli) (built: Nov 29 2017 00:17:00) ( ZTS MSVC15 (Visual C++ 2017) x86 )

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Comments: 15 (8 by maintainers)

Commits related to this issue

Most upvoted comments

I just had an opportunity to test your change, works great, good job 😃

Sure, but we have enough people to verify that. 😃

@ostrolucky Some quick math based on what @kelunik found about stream_select() on Windows: By default, chunks of 8192 bytes are read from streams: 19 * 2^20 / 8192 = 2432 chunks Since calls to stream_select() providing both file handles and sockets waits for 100 ms in select() before calling WaitForMultipleObjects(), then 2432 chunks * 100 ms / 1000 ms/s = 243 s, almost exactly the time you’re observing. This will be the same regardless of wether you use Amp, ReactPHP, or any other library as this is a PHP behavior, not specific to this library.

Streaming simultaneously on a TCP socket helps immensely because then select() does not wait for the full 100ms, but only until data is received on a selected socket.

STDIN is blocking on Windows. You may need to do some magic with amphp/process to create another process that can block that communicates with a non-blocking process using sockets.

If you can describe your use-case a bit more I may be able to provide a more specific solution.