php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Request #61168 fwrite() should allow for buffering
Submitted: 2012-02-23 06:16 UTC Modified: 2016-07-01 21:14 UTC
Votes:4
Avg. Score:4.8 ± 0.4
Reproduced:4 of 4 (100.0%)
Same Version:1 (25.0%)
Same OS:4 (100.0%)
From: tstarling@php.net Assigned:
Status: Open Package: Streams related
PHP Version: 5.4.0RC8 OS: Linux
Private report: No CVE-ID: None
View Add Comment Developer Edit
Welcome! If you don't have a Git account, you can't do anything here.
You can add a comment by following this link or if you reported this bug, you can edit this bug over here.
(description)
Block user comment
Status: Assign to:
Package:
Bug Type:
Summary:
From: tstarling@php.net
New email:
PHP Version: OS:

 

 [2012-02-23 06:16 UTC] tstarling@php.net
Description:
------------
The stream_set_write_buffer() manual entry indicates that fwrite() buffers its output. This is incorrect. For regular files, no buffering is ever done, each fwrite() call leads to a syscall. 

This is a performance issue, especially for extensions like the CDB handler in DBA. I have a real-world script that uses almost as much system CPU as user CPU, because each dba_insert() call leads to multiple syscalls.

Test script:
---------------
$f = fopen( '/tmp/blah', 'w' );
stream_set_write_buffer( $f, 8192 );
for ( $i = 0; $i < 10; $i++ ) {
	fwrite( $f, 'x' );
}
fclose( $f );


Expected result:
----------------
strace php write-tight-loop.php
...
write(3, "xxxxxxxxxx", 1)                        = 10
...



Actual result:
--------------
write(3, "x", 1)                        = 1
write(3, "x", 1)                        = 1
write(3, "x", 1)                        = 1
write(3, "x", 1)                        = 1
write(3, "x", 1)                        = 1
write(3, "x", 1)                        = 1
write(3, "x", 1)                        = 1
write(3, "x", 1)                        = 1
write(3, "x", 1)                        = 1
write(3, "x", 1)                        = 1


Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2012-02-23 10:32 UTC] cataphract@php.net
You're not checking the return value of stream_set_write_buffer. If you were, you'd have noticed it returns -1 indicating failure.

PHP does not have buffering for writes as it does for reads. In fact, it has the opposite: it breaks the data to write in chunk_size bits (see the undocumented stream_set_chunk_size in PHP 5.4).

Therefore, streams that want to provide write buffering must do so themselves; PHP merely provides a way to configure the write buffering. In the case of plain files, write buffering is supported, but only for stdio files, not plain file descriptors. The stream by default doesn't use stdio (to avoid duplication of read buffers for instance).

Extensions can open a FILE* and pass it to php_stream_fopen_from_file(), but it seems that this won't suffice because PHP will still use low-level I/O on the file descriptor returned by fileno(), so you'd have to violate some abstractions and zero the fd field in the php_stdio_stream_data structure (stored in stream->abstract) to force stdio to be used. This should be revisited.

In any case, the documentation is wrong. I'm changing this to a documentation problem for the time being.
 [2012-02-23 10:32 UTC] cataphract@php.net
-Type: Bug +Type: Documentation Problem
 [2012-02-23 14:49 UTC] cataphract@php.net
Perhaps this change in plain_wrapper.c would be a good idea:

@@ -583,7 +583,13 @@
                case PHP_STREAM_OPTION_WRITE_BUFFER:

                        if (data->file == NULL) {
-                               return -1;
+                               char fixed_mode[5];
+                               php_stream_mode_sanitize_fdopen_fopencookie(stream, fixed_mode);
+                               data->file = fdopen(data->fd, fixed_mode);
+                               if (data->file == NULL) {
+                                       return -1;
+                               }
+                               data->fd = -1;
                        }

                        if (ptrparam)


I'll have to think a bit more about the implications.
 [2013-04-22 01:02 UTC] tstarling@php.net
Still not even a documentation change? It's been more than a year, and I can't imagine it being particularly complex to edit a single sentence in the manual.
 [2013-04-22 18:41 UTC] rasmus@php.net
You are right, it isn't. In fact you can easily do it. Go to http://edit.php.net 
and make the change and submit it.
 [2016-07-01 21:13 UTC] cmb@php.net
Automatic comment from SVN on behalf of cmb
Revision: http://svn.php.net/viewvc/?view=revision&amp;revision=339538
Log: Address #61168: fwrite() has no buffering

The info that fwrite() is normally buffered is obviously wrong, but the
rest of the paragraph also looks fishy, so we remove it altogether.
 [2016-07-01 21:14 UTC] cmb@php.net
-Summary: fwrite() has no buffering +Summary: fwrite() should allow for buffering -Type: Documentation Problem +Type: Feature/Change Request
 [2016-07-01 21:14 UTC] cmb@php.net
I've fixed the docs; changing to feature request as indicated by
Gustavo.
 
PHP Copyright © 2001-2019 The PHP Group
All rights reserved.
Last updated: Sat Dec 07 12:01:23 2019 UTC