Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Huge memory usage, despite using SapiStreamEmitter #14

Closed
weierophinney opened this issue Dec 31, 2019 · 3 comments
Closed

Huge memory usage, despite using SapiStreamEmitter #14

weierophinney opened this issue Dec 31, 2019 · 3 comments
Labels
Invalid This doesn't seem right

Comments

@weierophinney
Copy link
Member

Huge memory usage, despite using SapiStreamEmitter

When using Zend\Diactoros\Server with ->setEmitter(new \Zend\Diactoros\Response\SapiStreamEmitter());, it is still huge memory usage.

here is reproduce code.

<?php
use Zend\Diactoros\Server;
// zendframework/zend-diactoros version is 1.3.10
require_once __DIR__.'/vendor/autoload.php';
$server = Server::createServer(function () {
    // create empty 10MB file
    // $ dd if=/dev/zero of=tempfile bs=1M count=10
    return new \Zend\Diactoros\Response(fopen('tempfile', 'r'));
}, $_SERVER, $_GET, $_POST, $_COOKIE, $_FILES);
$server->setEmitter(new \Zend\Diactoros\Response\SapiStreamEmitter());
$server->listen();
// for builtin server
file_put_contents("php://stdout", "\nMemory Usage: " . formatBytes(memory_get_peak_usage(true)));

function formatBytes($bytes, $precision = 2) {
    if ( abs($bytes) < 1024 ) $precision = 0;
    $sign = '';
    if ( $bytes < 0 ) {
        $sign = '-';
        $bytes = abs($bytes);
    }
    $exp   = floor(log($bytes) / log(1024));
    $bytes = sprintf('%.'.$precision.'f', ($bytes / pow(1024, floor($exp))));
    return $sign . $bytes .' '. ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'][$exp];
}

expected Memory Usage is 2.00 MB,
but showned Memory Usage is 16.00 MB.

I think that reason is Server::listen() calls ob_start,
but did not clean output buffer (ob_end_clean)


Originally posted by @sasezaki at zendframework/zend-diactoros#232

@weierophinney
Copy link
Member Author

First, for anyone reading this, you need to apply PR #233 before running this code.

@sasezaki your suggestion will not work. However, may be interesting to add an "ob_end_flush" at the end of "listen" method (not ob_end_clean).

In order to reduce memory usage is necessary a call to "ob_flush" after each "read" call in SapiStreamEmitter.php.

However I believe this may cause performance issues in most production environments.

The flush calls has a very high performance cost.

And besides this, the PHP engine already regularly flushes output buffer, following "output_buffering" configuration directive.

Which I believe is more appropriate because it allows the application admin to choose the best balance between resource consumption and performance.

And depending of layers beyond PHP engine, the "ob_flush" may be pointless (e.g. Apache buffering when using gzip).


Originally posted by @fcabralpacheco at zendframework/zend-diactoros#232 (comment)

@weierophinney
Copy link
Member Author

It has been months (literally) since I started looking for a solution.

In my case, here is what did the trick - Note that I'm only using the SapiStreamEmitter:

ob_end_flush();
ob_implicit_flush();
$emitter->emit($response);

I'm not sure if this is really efficient, but at least it works (with large files ofc). I still need to try it in prod though..

Not taking any credit here. In fact, this is from an answer (that have some up votes) on ob_implicit_flush() function at php.net: https://secure.php.net/manual/fr/function.ob-implicit-flush.php#116748

EDIT - After some more investigation.. We can simply do this. Way more cleaner.

ob_implicit_flush();
$maxBufferLevel = 0; // Stands for the maximum (and last) output buffering level to unwrap
$emitter->emit($response, $maxBufferLevel);

Originally posted by @cocochepeau at zendframework/zend-diactoros#232 (comment)

@Xerkus
Copy link
Member

Xerkus commented May 2, 2023

SapiEmitter no longer part of laminas-diactoros

@Xerkus Xerkus closed this as not planned Won't fix, can't repro, duplicate, stale May 2, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Invalid This doesn't seem right
Projects
None yet
Development

No branches or pull requests

2 participants