dvdoug / BoxPacker

4D bin packing / knapsack problem solver
MIT License
612 stars 156 forks source link

Feature request: add max execution time #620

Open Gemorroj opened 2 weeks ago

Gemorroj commented 2 weeks ago

Sometimes "BoxPacker" works for a very long time and we need to force it to stop and continue working with other code. I would like to add some kind of countdown and throw an exception when a certain limit is reached. Something like this:

$limitSeconds = 2;

$startTime = microtime(true);
while(true) { // packing process
    sleep(1);

    $currentTime = microtime(true);
    if ($currentTime - $limitSeconds > $startTime) {
        throw new \Exception('Time Limit Exception');
    }
}
dvdoug commented 1 week ago

Hi @Gemorroj

I understand the desire, but would like to understand what it is that's taking too long for you in the first place. Is this a case where you're packing a very large number of items and/or have a very large number of box types?

Gemorroj commented 1 week ago

@dvdoug These are both cases. There are many packages, there are many goods, there are many of both. This is a service for our customers and customers have different scenarios.

dvdoug commented 1 week ago

If you could supply a couple of examples, I'd be happy to try and figure out if there are any obvious bottlenecks that could speed things up

Gemorroj commented 1 week ago
<?php

declare(strict_types=1);

require __DIR__.'/vendor/autoload.php';

use DVDoug\BoxPacker\Packer;
use DVDoug\BoxPacker\Test\TestBox;
use DVDoug\BoxPacker\Test\TestItem;
use DVDoug\BoxPacker\Rotation;

$packer = new Packer();
$packer->setMaxBoxesToBalanceWeight(0);

// boxes
for ($i = 0; $i < 100; $i++) {
    $box = new TestBox(
        reference: 'box '.$i,
        outerWidth: $i * 10,
        outerLength: $i * 10,
        outerDepth: $i * 10,
        emptyWeight: 1,
        innerWidth: $i * 10,
        innerLength: $i * 10,
        innerDepth: $i * 10,
        maxWeight: 10000,
    );
    $packer->addBox($box);
}

// items
$item = new TestItem(
    description: 'item 1',
    width: 100,
    length: 100,
    depth: 100,
    weight: 100,
    allowedRotation: Rotation::BestFit, // need exactly best fit
);
$packer->addItem($item, 500);

$startTime = \microtime(true);
$result = $packer->pack();
$endTime = \microtime(true);

echo \round($endTime - $startTime, 2).'s.'.\PHP_EOL;
// \print_r($result);

on my local computer, the code runs for about 50 seconds.

dvdoug commented 1 week ago

Hi @Gemorroj

Do you have Xdebug enabled? That slows things down significantly. The testcase I just added based on your example takes about 7.3seconds for me and that's on a fairly underpowered laptop-class CPU.

On the CI, the difference between https://github.com/dvdoug/BoxPacker/actions/runs/11879381968/job/33101223212 and https://github.com/dvdoug/BoxPacker/actions/runs/11880522441/job/33103770101 is only 3.1 seconds

Gemorroj commented 1 week ago

@dvdoug hm. yes. There was a xdebug.mode=develop. I didn't think this mode had such a big impact. In xdebug.mode=off mode, I also get 7 seconds. But even this time is too long for us. And there may be even more boxes or items.