Reduce chunk memory usage by 20-60% by exploiting PHP copy-on-write behaviour (#2527)

This takes advantage of two key behaviours of PHP:
1. Assigning a string does not copy the string
2. Changing an offset in a string causes the string to be copied.

These two factors combined, along with the fact that blocklight and skylight arrays are usually all-zeros, allow us to produce a significant memory usage reduction of loaded chunks.
A freshly generated PM world with 3,332 chunks loaded drops from 310MB to 200MB memory usage with these changes applied.
This commit is contained in:
Dylan T 2018-11-17 14:46:05 +00:00 committed by GitHub
parent 554c029fbd
commit f8bfbc107d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 27 additions and 4 deletions

View File

@ -840,8 +840,12 @@ class Chunk{
*/
public function collectGarbage() : void{
foreach($this->subChunks as $y => $subChunk){
if(!($subChunk instanceof EmptySubChunk) and $subChunk->isEmpty()){ //normal subchunk full of air, remove it and replace it with an empty stub
$this->subChunks[$y] = $this->emptySubChunk;
if($subChunk instanceof SubChunk){
if($subChunk->isEmpty()){
$this->subChunks[$y] = $this->emptySubChunk;
}else{
$subChunk->collectGarbage();
}
}
}
}

View File

@ -23,8 +23,9 @@ declare(strict_types=1);
namespace pocketmine\level\format;
class SubChunk implements SubChunkInterface{
define(__NAMESPACE__ . '\ZERO_NIBBLE_ARRAY', str_repeat("\x00", 2048));
class SubChunk implements SubChunkInterface{
protected $ids;
protected $data;
protected $blockLight;
@ -44,6 +45,7 @@ class SubChunk implements SubChunkInterface{
self::assignData($this->data, $data, 2048);
self::assignData($this->skyLight, $skyLight, 2048, "\xff");
self::assignData($this->blockLight, $blockLight, 2048);
$this->collectGarbage();
}
public function isEmpty(bool $checkLight = true) : bool{
@ -51,7 +53,7 @@ class SubChunk implements SubChunkInterface{
substr_count($this->ids, "\x00") === 4096 and
(!$checkLight or (
substr_count($this->skyLight, "\xff") === 2048 and
substr_count($this->blockLight, "\x00") === 2048
$this->blockLight === ZERO_NIBBLE_ARRAY
))
);
}
@ -203,4 +205,21 @@ class SubChunk implements SubChunkInterface{
public function __debugInfo(){
return [];
}
public function collectGarbage() : void{
/*
* This strange looking code is designed to exploit PHP's copy-on-write behaviour. Assigning will copy a
* reference to the const instead of duplicating the whole string. The string will only be duplicated when
* modified, which is perfect for this purpose.
*/
if($this->data === ZERO_NIBBLE_ARRAY){
$this->data = ZERO_NIBBLE_ARRAY;
}
if($this->skyLight === ZERO_NIBBLE_ARRAY){
$this->skyLight = ZERO_NIBBLE_ARRAY;
}
if($this->blockLight === ZERO_NIBBLE_ARRAY){
$this->blockLight = ZERO_NIBBLE_ARRAY;
}
}
}