Bug #73262 [Opn]: 8192 CHUNK_SIZE and PHP_SOCK_CHUNK_SIZE makes copy() slow

From: Date: Sun, 09 Oct 2016 19:43:55 +0000
Subject: Bug #73262 [Opn]: 8192 CHUNK_SIZE and PHP_SOCK_CHUNK_SIZE makes copy() slow
References: 1  Groups: php.bugs 
Request: Send a blank email to php-bugs+get-204572@lists.php.net to get a copy of this message
Edit report at https://bugs.php.net/bug.php?id=73262&edit=1 ID: 73262 Updated by: ab@php.net Reported by: jeffreydwalter at gmail dot com Summary: 8192 CHUNK_SIZE and PHP_SOCK_CHUNK_SIZE makes copy() slow Status: Open Type: Bug Package: Performance problem Operating System: Irrelevant PHP Version: Irrelevant Block user comment: N Private report: N New Comment: Thanks for the report. You seem to be on a machine with quite a slow I/O. While it is true, that increasing the buffer will bring a speedup, in some cases it will be on cost of wasted memory. Also, there are likely situations where PHP is even faster. Have you checked various filesizes? OFC it is very platform and HW dependent. It could be indeed an idea, to set the buffer size through the context on runtime. Another option could be to extend the buffer automatically. Hardcoding the buffer size at compilation time is IMHO not that sensible. Different streams will possibly need different handling. Fe for a stream that only handles a few bytes of data, having a buffer of 192k as you do is for sure an overkill. So a balanced solution could be challenging. Thanks. Previous Comments: ------------------------------------------------------------------------ [2016-10-07 01:15:14] jeffreydwalter at gmail dot com I also forgot to mention that I tested this in PHP 5.6.20 on FreeBSD 10.3, but it appears to be an issue across all versions of PHP, including 7.x. ------------------------------------------------------------------------ [2016-10-07 01:13:59] jeffreydwalter at gmail dot com After further testing it seems like setting PHP_SOCK_CHUNK_SIZE = 8192*24 is enough to speed up copy(), which make sense I suppose since it's using a socket to do the writes. ------------------------------------------------------------------------ [2016-10-07 01:05:46] jeffreydwalter at gmail dot com Description: ------------ ***I have not tested this outside of FreeBSD 10.3, but it should be an issue on most if not all platforms*** copy() is several orders of magnitude slower than exec('cp ...') on the same partition. It is even worse copying across partitions. After digging through the source code, I discovered that it's because of the shear number of buffered write() calls that are made on the underlying socket in _php_stream_write_buffer(). The buffer's size is hardcoded to 8192 <a href="https://github.com/php/php-src/blob/PHP-5.6.20/main/streams/php_streams_int.h#L49">here</a> and <a href="https://github.com/php/php-src/blob/PHP-5.6.20/main/php_network.h#L210">here</a>. If I change CHUNK_SIZE and PHP_SOCK_CHUNK_SIZE to 8192*24, the php copy() function will perform at a comparable speed to exec('cp ...'). I don't fully understand the ramifications of changing that buffer outside of greater memory usage, and I haven't tested it on network-related stream operations. So if there's a better way I'd love to know. I do realize that I could create a stream context in php, set the buffer size, and pass that context to copy(), but that doesn't seem like a workable solution. Perhaps a better solution would be to make those CHUNK_SIZE defines tunable at compile time so they can be optimized for one's specific platform? Test script: --------------- <?php # Change this is you want to test with a different file size. define('FILE_SIZE_IN_MB', 250); printf('Generating %sMB dummy file.'.PHP_EOL, FILE_SIZE_IN_MB); echo shell_exec('dd if=/dev/urandom of=dummy.txt bs=1048576 count='.FILE_SIZE_IN_MB.' 2>&1 | grep -v records'); $copy_start = time(); copy('dummy.txt', 'copied_with_php_copy.txt'); $copy_done = time(); $cp_start = time(); exec('cp dummy.txt copied_with_cp.txt'); $cp_done = time(); echo 'copy() took: '.($copy_done-$copy_start).'s'.PHP_EOL; echo 'cp took: '.($cp_done-$cp_start).'s'.PHP_EOL; echo shell_exec('ls -al dummy.txt copied_with_php_copy.txt copied_with_cp.txt').PHP_EOL; echo 'Removing test files.'.PHP_EOL; exec('rm -rf dummy.txt copied_with_php_copy.txt copied_with_cp.txt'); Expected result: ---------------- php_stream_write took 0 seconds 796 milliseconds (I added this line in stream.c) php_stream_write() with mmap used (I added this line in stream.c) php output: Generating 250MB dummy file. copy() took: 1s cp took: 1s -rw-r--r-- 1 262144000 Oct 6 20:00 copied_with_cp.txt -rw-r--r-- 1 262144000 Oct 6 20:00 copied_with_php_copy.txt -rw-r--r-- 1 262144000 Oct 6 19:59 dummy.txt Removing test files. Actual result: -------------- php_stream_write took 5 seconds 562 milliseconds (I added this line in stream.c) php_stream_write() with mmap used (I added this line in stream.c) php output: php blah.php Generating 250MB dummy file. copy() took: 13s cp took: 0s -rw-r--r-- 1 262144000 Oct 6 20:00 copied_with_cp.txt -rw-r--r-- 1 262144000 Oct 6 20:00 copied_with_php_copy.txt -rw-r--r-- 1 262144000 Oct 6 19:59 dummy.txt Removing test files. ------------------------------------------------------------------------ -- Edit this bug report at https://bugs.php.net/bug.php?id=73262&edit=1

« previous php.bugs (#204572) next »