From 10f743b0a8b3294a45681188b254be95e27d9bd5 Mon Sep 17 00:00:00 2001 From: Denis Smetannikov Date: Thu, 17 Aug 2023 00:09:25 +0300 Subject: [PATCH] Memory optimization for ProgressBar (#19) --- demo/Commands/DemoProgressBar.php | 37 +++++++++++++++++++------ src/ProgressBars/ProgressBarSymfony.php | 18 ++++++++++++ 2 files changed, 47 insertions(+), 8 deletions(-) diff --git a/demo/Commands/DemoProgressBar.php b/demo/Commands/DemoProgressBar.php index 95baa34..c22d0fb 100644 --- a/demo/Commands/DemoProgressBar.php +++ b/demo/Commands/DemoProgressBar.php @@ -20,17 +20,19 @@ use JBZoo\Cli\CliCommand; use JBZoo\Cli\ProgressBars\ExceptionBreak; use JBZoo\Utils\Slug; +use Symfony\Component\Console\Helper\ProgressBar; use Symfony\Component\Console\Input\InputOption; class DemoProgressBar extends CliCommand { - private const CASE_SIMPLE = 'simple'; - private const CASE_MESSAGES = 'messages'; - private const CASE_ARRAY = 'array'; - private const CASE_BREAK = 'break'; - private const CASE_EXCEPTION = 'exception'; - private const CASE_EXCEPTION_LIST = 'exception-list'; - private const CASE_MILLION = 'million'; + private const CASE_SIMPLE = 'simple'; + private const CASE_MESSAGES = 'messages'; + private const CASE_ARRAY = 'array'; + private const CASE_BREAK = 'break'; + private const CASE_EXCEPTION = 'exception'; + private const CASE_EXCEPTION_LIST = 'exception-list'; + private const CASE_MILLION = 'million'; + private const CASE_MILLION_SYMFONY = 'million-symfony'; protected function configure(): void { @@ -54,6 +56,7 @@ protected function executeAction(): int self::CASE_EXCEPTION, self::CASE_EXCEPTION_LIST, self::CASE_MILLION, + self::CASE_MILLION_SYMFONY, ]); // Just 5 simple steps ///////////////////////////////////////////////////////////////////////////////////////// @@ -117,7 +120,7 @@ protected function executeAction(): int }, 'Ignoring and collecting exceptions. Throw them only at the end.', true); } - // 1 000 000 Items Benchmark /////////////////////////////////////////////////////////////////////////////////// + // Benchmark: 1 000 000 Items (~23 sec) //////////////////////////////////////////////////////////////////////// if ($caseName === self::CASE_MILLION) { $this->progressBar( 1_000_000, @@ -127,6 +130,24 @@ protected function executeAction(): int ); } + // Benchmark: 1 000 000 Items with pure Symfony progress bar (~4 sec) ////////////////////////////////////////// + if ($caseName === self::CASE_MILLION_SYMFONY) { + $this->_('Creates a new progress bar (1_000_000 units)'); + $progressBar = new ProgressBar($this->outputMode->getOutput(), 1_000_000); + + $this->_('Starts and displays the progress bar'); + $progressBar->start(); + + $i = 0; + + while ($i++ < 1_000_000) { + $progressBar->advance(); + } + + $this->_('Ensures that the progress bar is at 100%'); + $progressBar->finish(); + } + return self::SUCCESS; } diff --git a/src/ProgressBars/ProgressBarSymfony.php b/src/ProgressBars/ProgressBarSymfony.php index 0286ad7..5914d92 100644 --- a/src/ProgressBars/ProgressBarSymfony.php +++ b/src/ProgressBars/ProgressBarSymfony.php @@ -27,6 +27,8 @@ class ProgressBarSymfony extends AbstractSymfonyProgressBar { + private const LIMIT_ITEMT_FOR_PROFILING = 10; + private OutputInterface $output; private ?SymfonyProgressBar $progressBar = null; @@ -78,6 +80,9 @@ public function execute(): bool if (!$isOptimizeMode) { $this->stepMemoryDiff[] = \memory_get_usage(false) - $startMemory; $this->stepTimers[] = \microtime(true) - $startTime; + + $this->stepMemoryDiff = self::sliceProfileStats($this->stepMemoryDiff); + $this->stepTimers = self::sliceProfileStats($this->stepTimers); } $exceptionMessages = \array_merge($exceptionMessages, (array)$exceptionMessage); @@ -304,6 +309,19 @@ private function isOptimizeMode(): bool return $this->outputMode->getOutput()->getVerbosity() <= OutputInterface::VERBOSITY_NORMAL; } + private static function sliceProfileStats(array $arrayOfItems): array + { + if (\count($arrayOfItems) > self::LIMIT_ITEMT_FOR_PROFILING) { + $arrayOfItems = \array_slice( + $arrayOfItems, + -self::LIMIT_ITEMT_FOR_PROFILING, + self::LIMIT_ITEMT_FOR_PROFILING, + ); + } + + return $arrayOfItems; + } + private static function showListOfExceptions(array $exceptionMessages): void { if (\count($exceptionMessages) > 0) {