zephir icon indicating copy to clipboard operation
zephir copied to clipboard

Slow performance during create multidimension arrays

Open omatrycy opened this issue 5 years ago • 3 comments

Hello Code generated by zephir for setting multi-dimension array is very slow.

Example zep file:

namespace Utils;
class Example
{
    public function generate(int size) -> array
    {
        var output = [];
        var i = 0;
        var j = 0;
        
        for i in range (0, size) {
            let output[i] = [];
            
            for j  in range(0, size) {
                let output[i][j] = 1;
            }
        }
  
        return output;
    }
}

Example php code used this module:

<?php 

$a = new Utils\Example();
$res = $a->generate(1000);

Execution time: real 0m4,693s user 0m4,658s sys 0m0,020s

Generated c code for this function

PHP_METHOD(Utils_Example, generate) {

	zend_bool _0, _4$$3;
	zval *size_param = NULL, output, i, j, _3$$3, _7$$4;
	zend_long size, _1, _2, _5$$3, _6$$3;
	zval *this_ptr = getThis();

	ZVAL_UNDEF(&output);
	ZVAL_UNDEF(&i);
	ZVAL_UNDEF(&j);
	ZVAL_UNDEF(&_3$$3);
	ZVAL_UNDEF(&_7$$4);

	ZEPHIR_MM_GROW();
	zephir_fetch_params(1, 1, 0, &size_param);

	size = zephir_get_intval(size_param);


	ZEPHIR_INIT_VAR(&output);
	array_init(&output);
	ZEPHIR_INIT_VAR(&i);
	ZVAL_LONG(&i, 0);
	ZEPHIR_INIT_VAR(&j);
	ZVAL_LONG(&j, 0);
	_2 = size;
	_1 = 0;
	_0 = 0;
	if (_1 <= _2) {
		while (1) {
			if (_0) {
				_1++;
				if (!(_1 <= _2)) {
					break;
				}
			} else {
				_0 = 1;
			}
			ZEPHIR_INIT_NVAR(&i);
			ZVAL_LONG(&i, _1);
			ZEPHIR_INIT_NVAR(&_3$$3);
			array_init(&_3$$3);
			zephir_array_update_zval(&output, &i, &_3$$3, PH_COPY | PH_SEPARATE);
			_6$$3 = size;
			_5$$3 = 0;
			_4$$3 = 0;
			if (_5$$3 <= _6$$3) {
				while (1) {
					if (_4$$3) {
						_5$$3++;
						if (!(_5$$3 <= _6$$3)) {
							break;
						}
					} else {
						_4$$3 = 1;
					}
					ZEPHIR_INIT_NVAR(&j);
					ZVAL_LONG(&j, _5$$3);
					ZEPHIR_INIT_NVAR(&_7$$4);
					ZVAL_LONG(&_7$$4, 1);
					zephir_array_update_multi(&output, &_7$$4 TSRMLS_CC, SL("zz"), 2, &i, &j);
				}
			}
		}
	}
	RETURN_CCTOR(&output);

}

Similar code created in php

<?php

class Example {
	public function generate(int $size): array 
	{
		$output = [];
		for ($i = 0; $i <= $size; $i++) {
			 $output[$i] = [];
			for ($j = 0; $j <= $size; $j++) {
				$output[$i][$j] = 1;
			}
		}
		return $output;
	}
}

$a = new Example();
$res = $a->generate(1000);

Execution time: real 0m0,178s user 0m0,156s sys 0m0,021s

For calculating time I used linux time command. PHP version : PHP 7.2.19-0ubuntu0.19.04.1 (cli) (built: Jun 4 2019 14:44:42) ( NTS ) Zephir version : 0.12.0 (with default configuration for new projects)

omatrycy avatar Jul 10 '19 18:07 omatrycy

I can try to use hash directly. Second, zephir_array_update_multi is retrieved only once according to the context, not every update.

dreamsxin avatar Sep 29 '19 14:09 dreamsxin

My test result: PHP

$ time php ../../test2.php 

real	0m0.115s
user	0m0.095s
sys	0m0.020s

zephir

$ time php ../../test.php 

real	0m0.164s
user	0m0.140s
sys	0m0.024s

After optimization

time php ../../test.php 

real	0m0.089s
user	0m0.049s
sys	0m0.040s

@sergeyklay Please take look if you can add reference type, and convert as follows:

	public function generate(int size) -> array
	{
		var output = [];
		var i = 0;
		var j = 0;

		for i in range (0, size) {
			var arr;
			let output[i] = [];
			let arr = &output[i]; // <-- reference

			for j  in range(0, size) {
				let arr[j] = 1;
			}
		}

		return output;
	}

to c code like this

...
zephir_array_update_zval(&output, &i, &empty_arr, PH_COPY | PH_SEPARATE);
zephir_array_fetch(&empty_arr, &output, &i, PH_NOISY|PH_READONLY, ...); // key point, use PH_READONLY, if reference type
...
zephir_array_update_zval(&empty_arr, &j, &value, PH_COPY); // key point, don't use PH_SEPARATE, if reference type
...

dreamsxin avatar Sep 29 '19 23:09 dreamsxin

Great improvement!

ruudboon avatar Sep 30 '19 09:09 ruudboon