On 20/01/11 6:05 AM, la...@garfieldtech.com wrote:
So it sounds like the general answer is that if you pass a complex array to a
function by value and mess with it, data is duplicated for every item you modify
and its direct ancestors up to the root variable but not for the rest of the
tree.
For objects, because of their "pass by handle"-type behavior you are (usually)
modifying the same data directly so there's no duplication.
Does that sound correct?
Yes.
Related: What is the overhead of a ZVal? I'm assuming it's a fixed
number of bytes.
It seems not, though a zval has a fixed size. What that size is will
depend on the compiler and architecture of the system being used, or at
least on the ABI.
From zend.h:
typedef union _zvalue_value {
long lval; /* long value */
double dval; /* double value */
struct {
char *val;
int len;
} str;
HashTable *ht; /* hash table value */
zend_object_value obj;
} zvalue_value;
struct _zval_struct {
/* Variable information */
zvalue_value value; /* value */
zend_uint refcount__gc;
zend_uchar type; /* active type */
zend_uchar is_ref__gc;
};
The zvalue_value union will probably be 8 or 12 bytes, depending on the
architecture. The whole struct will then probably be between 14 and 24
bytes, depending on the architecture and structure alignment and so on.
For my system:
$ cd php-5.3.3
$ ./configure
$ cd Zend
$ gcc -I. -I../TSRM -x c - <<END
#include "zend.h"
int main(void) {
printf("%lu\n",sizeof(zval));
return 0;
}
END
$ file ./a.out
./a.out: Mach-O 64-bit executable
$ ./a.out
24
$ gcc -I. -I../TSRM -arch i386 -x c - <<END
#include "zend.h"
int main(void) {
printf("%lu\n",sizeof(zval));
return 0;
}
END
$ file ./a.out
./a.out: Mach-O executable i386
$ ./a.out
16
You can figure out what you think the overhead is from that. For a
string, arguably the whole structure is overhead, since the string is
stored elsewhere via pointer. Likewise for objects. For a double, the
payload is 8 bytes, and stored in the zval, so there's less overhead. An
integer, with a payload of 4 bytes, is somewhere in between.
Ben.
--Larry Garfield
On 1/19/11 11:27 AM, Gustavo Lopes wrote:
On Wed, 19 Jan 2011 14:23:49 -0000, Martin Scotta
<martinsco...@gmail.com> wrote:
What about objects?
With objects less copying occurs because the object value (zval) data is
actually just a pointer and an id that for most purposes works as a
pointer.
However, it should be said that while a copy of an array forces more
memory to be copied, the inner zvals are not actually copied. In this
snippet:
$a = array(1, 2, array(3));
$b = $a;
function separate(&$dummy) { }
separate($a);
the copy that occurs when you force the separation of the zval that is
shared by $a and $b ($b = $a doesn't copy the array in $a to $b, it
merely copies the zval pointer of $a to $b and increments its reference
count) is just a shallow copy of hash table and a increment of the first
level zvals' refcounts. This means the zvals that have their pointers
stored in the array $a's HashTable are not themselves copied.
Interestingly (or should I say, unfortunately), this happens even if the
inner zvals are references. See
http://php.net/manual/en/language.references.whatdo.php the part on arrays.
class Foo {
public $foo;
}
function test($o) {
$o->foo->foo->foo = 2;
}
$bar = new Foo;
$bar->foo = new Foo;
$bar->foo->foo = new Foo;
test( $bar );
This example shows no copying (in the sense of "new zval allocation on
passing or assignment") at all.
---
Also... is it better to pass an object as a parameter rather than many
values?
function withValues($anInteger, $aBool, $aString) {
var_dump($anInteger, $aBool, $aString);
}
function withObject(ParamOject $o) {
var_dump( $o->theInteger(), $o->theBool(), $o->theString() );
}
It should be indifferent. In normal circumstances, there is no zval
copying at all (only the pointers of arguments' symbols are copied).
Only when you start throwing references into the mix will you start
forcing copied.
--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php