Skip to content
This repository has been archived by the owner on Jan 29, 2020. It is now read-only.

Huge memory usage, despite using SapiStreamEmitter #232

Open
sasezaki opened this issue Feb 17, 2017 · 3 comments
Open

Huge memory usage, despite using SapiStreamEmitter #232

sasezaki opened this issue Feb 17, 2017 · 3 comments

Comments

@sasezaki
Copy link
Contributor

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)

@fcabralpacheco
Copy link
Contributor

fcabralpacheco commented Feb 19, 2017

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).

@cocochepeau
Copy link

cocochepeau commented Sep 5, 2017

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);

@weierophinney
Copy link
Member

This repository has been closed and moved to laminas/laminas-diactoros; a new issue has been opened at laminas/laminas-diactoros#14.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants