In the PHP documentation it says:
Do not use return-by-reference to increase performance. The engine
will automatically optimize this on its own.
I wish to return a reference to an array (which is a property of my class). How does PHP optimize this, because the array is not an object?
If the array has 1 billion entries, won't I get two arrays with 1 billion entries stored in memory if I don't pass it by reference?
PHP uses copy on write. That means that if you pass the huge array as a function parameter and only read from it (such as a foreach), you won't be writing to it so it doesn't need to make a copy.
$count = count($huge_array); // read the reference at the bottom
for($i = 0; $i < $count $i++) {
$value = 2*$huge_array[$i]/15;
if($value > 3)
$sub_array []= $value;
}
The $subarray which should be smaller (it is a subset of the huge array), will contain only the needed (changed) data.
If you do not intend on changing the values of the original $huge_array it will never get copied so no extra memory is used.
If you intend on changing the original values of the array, you need to pass it by reference.
If you intend on making and returning an altered version of the original array then you do need the extra memory PHP is allocating for you.
If you intend on making and returning an altered smaller subset of the original array then you will create a new empty array into which you will copy some of data from the huge array and should be careful not to overwrite values in the $huge_array, so you'll avoid writing from $huge_array and emphasise on reading from it.
This link explains that PHP was optimised for pass by value use cases.
Copy on write only works if the variable isn't a reference being passed to a function expecting a value, if it is, passing it around triggers a copy.
That makes PHP native functions that expected an argument to be passed by value and received a referenced variable copy the value of the reference.
function foo(&$data) {
for ($i = 0; $i < strlen($data); $i++) {
do_something($data{$i});
}
}
$string = "... looooong string with lots of data .....";
foo(string);
Executing strlen in C would imply iterating over the entire string to count it. In PHP strings have an attached length value. So strlen returns the value read from there (fast).
But if you give it a referenced variable it will have to copy it before reading it's length so it will iterate over the value to copy it into it's argument list, read and return the length (and subsequently release the memory for the freshly copied string).
Related
For a setup script I write in PHP (for CLI, not web) I use a settings file as well as other other "content" files.
The settings file is built up like a regular ini-file
[header.subheader.moreheaders...]
keyA = value
keyB = value1|value2|...
"header.subheader.moreheaders..." and "keyX" will form a nested associative array with "value" as a string or "value1|value2|..." as a simple array (0-...).
Thanks to this accepted answer, I got so far that I can split the headers into a recursive array recursively. So far, so good.
However, as the content files shall contain references to these variables, I would like to be able to read out single values from that multi-dimensional array with string placeholders like $#R[header.subheader.moreheaders.key] or $#R[header.subheader.moreheaders.key.0] depending on them being a string or an array.
In the script, $#R[header.subheader.moreheaders.key.0] should convert into $SettingsVar[header][subheader][moreheaders][key][0] to return the appropriate value.
Neither the script nor the content files will know what is inside the settings file. The script just knows the general structure and placeholder $#R[...].
This answer appears to know what value will be in order to search for it.
Since I do not fully understand this answer, I am not sure if that would be the right way.
Is there a similar easy way to get the reverse from building that array?
After some contemplation, I found a decent enough solution, which works for me (and hopefully others):
function GetNestedValue($aNestedKeys, $aNestedArray)
{
$vValue = $aNestedArray;
for($i = 0; $i < count($aNestedKeys); $i++)
{
if(array_key_exists($aNestedKeys[$i], $vValue))
{
$vValue = $vValue[$aNestedKeys[$i]];
}
else
{
$vValue = null;
break;
}
}
return $vValue;
}
Depending on what $aNestedKeys contain, it will either return a sub-array from $aNestedArray, a single value from it or null if any of the specified keys were not found.
given I have an array, say:
$myArray=['12','AB','3C']
I want to return the value 2 (which is the length of each of the array elements indivudually.)
But in case I have something like
$myArray=['12','AB2','3C']
I want to stop the calculation/loop right after the second element of the array 'AB2' and let my function return null.
What is the most effective way to reach this in the matter of being performance and speed effective? Since such an array can get long.
Casual way
I think you are trying to stop the array loop the moment you get two different lengths in an element?
In that case, at worst, you'd need an O(n) runtime (since you need to verify every element, unless you have an abstract data type in mind in which case it could be O(1), if it is stored in the object property or you calculate the difference detected on the fly while pushing items into arrays)
Since the moment we discover an element is not the same length, we can simply quickly store the length of the first element in the array since we know if we detect any other length other than what we stored, we can immediately return null
function linear_loop($array) {
$len_of_first = strlen($array[0]);
foreach ($array as $val) {
if (strlen($val) != $len_of_first) {
return null;
}
}
//Function still running, entire array was same, return the length of first element
return $len_of_first;
}
This function is O(n) with each operation is constant. strlen is O(1)
Algorithmic complexity of PHP function strlen()
Most "performance-fastest"
Since you said that the array can get quite long, if you are not immediately generating the array, but rather you need to push items into it, then in your push operation, you can check before pushing it the item_to_be_pushed is the same strlen or whatever property you are trying to compare as the one you've stored (which can be picked arbitrarily, since the array must be of uniform some property)
In this case, you could have some object with property: uniform_length and store that. Then whenever you push into your array, you can check against it with the uniform_length. If it isn't the same length, then you can store in the object property called uniform as false. (By default uniform is true since if there is only one element in the array, it must be uniform).
This would be an O(1) calculation since it is stored as an attribute. But you probably don't need an object for something as simple as this, and you can just store it as some variable.
O(1) vs O(n) Runtime and why it is more performance effective
Since not everyone knows Big O, a quick explanation on what I said. O(1) runtime is "infinitely" better than O(n) runtime since the runtime of the function will not grow with input (as in processing 1 million items require the same amount of steps as processing 1 item)
Just loop through and return early when you find something that isn't correct. Don't worry about micro-optimizations until you have profiled and found that this function is really your bottleneck
ex.
function isCorrect($arr) {
$len = strlen($arr[0]);
for($arr as $val) {
if(strlen($val) != $len) {
return false;
}
}
return true;
}
Just my two cents. You could also use array_map for this:
$myArray = ['12','AB','3CC'];
$lengths = array_map('strlen', $myArray);
// output
Array
(
[0] => 2
[1] => 2
[2] => 3
)
you can just write an if statement and check the max($lengths) and return true or false
-Cheers
I have a multidiminsional array that I have created in php that is passed back to a jQuery script. I need to iterate through this array and process the data.
In Firebug I can see that the data is located at data.data.items. I've tried finding the length of the array using data.data.items.length, but it comes back as undefined. Interestingly, this worked prior to my php portion working correctly when it passed back an array of 8 empty items. Now that it's populated (and the indexes are strings), length doesn't work. There is also an object in each of the items. What's breaking this?
An Array in JavaScript is an object nonetheless. When setting values using strings (or anything that isn't an integer), you are actually setting a property of the object (you are actually doing this when setting it with integer keys as well, but it's handled slightly differently).
To the issue of its sudden breakage after using strings as keys, I would expect that PHP realizes when you have an honest-to-goodness array versus an associative array, thus it sends arrays (surrounded by []) when all keys are integers, and objects (surrounded by {}) otherwise. I believe in the string-keyed case, PHP is generating objects, and thus .length becomes undefined (rather than 0 as in an empty array).
To answer your question, there is a simple way to count the "length" of this data:
var i = 0;
for (var item in data.data.items) {
i++;
}
Which will iterate through each property of data.data.items and count them. Note that if you (or any library you include) adds a property to the Object prototype, this will not produce expected results. This is fairly uncommon, but you must be aware of it when using for..in.
You can address this by using the method Nagh suggested, which ignores properties not defined on that particular object:
var i = 0;
for (var item in data.data.items) {
if(data.data.items.hasOwnProperty(item)) {
i++;
}
}
You can always use "foreach" kind of loop. In this case, you don't need to know what array length is there or even is it array or not, since you can iterate over object properties aswell.
As Joe already has pointed, javascript doesn't have associative arrays and when you trying to use one - you end up with object with properties. However, if u sure, that only properties this object got - is your array you can use code like that:
for (i in arr) {
if (arr.hasOwnProperty(i)) {
//do something with arr[i]
}
}
However if you really need an array, consider using integer as an array index.
I have a question that I cannot find an answer.
I'm constructing a very big array that contain hex values of a file (like $array[0]=B5 $array[1]=82 and so on until $array[1230009])
When I create a function to manipulate some offsets in that array and pass the $array as reference ( function parse(&$array) { ... }) it takes WAY longer than if I pass the array normaly ( function parse($array) { ... }) ..
How is that possible ?
PS: Is there any faster way not to use array at all ? Just to use a $string = "B5 82 00 1E ..etc", but I need to track the Offset as i advance in reading hex values as some of this values contains lengths"
There are some informations that might help in the following article : Do not use PHP references
Close to the end of that post, Johannes posts the following portion of code (quoting) :
function foo(&$data) {
for ($i = 0; $i < strlen($data); $i++) {
do_something($data{$i});
}
}
$string = "... looooong string with lots of data .....";
foo(string);
And a part of the comment that goes with it is (still quoting) :
But now in this case the developer tried to be smart and save time by
passing a reference. But well, strlen() expects a copy.
copy-on-write can't be done on references so $data will be
copied for calling strlen(), strlen() will do an absolutely simple
operation - in fact strlen() is one of the most trivial functions
in PHP - and the copy will be destroyed immediately.
You might well be in a situation like this one, considering the question you are asking...
Does count() really count the all the elements of a PHP array, or is this value cached somewhere and just gets retrieved?
Well, we can look at the source:
/ext/standard/array.c
PHP_FUNCTION(count) calls php_count_recursive(), which in turn calls zend_hash_num_elements() for non-recursive array, which is implemented this way:
ZEND_API int zend_hash_num_elements(const HashTable *ht)
{
IS_CONSISTENT(ht);
return ht->nNumOfElements;
}
So you can see, it's O(1) for $mode = COUNT_NORMAL.
In PHP 5+ the length is stored in the array so the counting is not done each time.
EDIT: You also might find this analysis interesting: PHP Count Performance. Although the length of the array is maintained by the array, it still seems as though it is faster to hold on to it if you are going to call count() many times.
PHP stores the size of an array internally, but you're still making a function call when which is slower than not making one, so you'll want to store the result in a variable if you're doing something like using it in a loop:
For example,
$cnt = count($array);
for ($i =0; $i < $cnt; $i++) {
foo($array[$i]);
}
Additionally, you can't always be sure count is being called on an array. If it's called on an object that implements Countable for example, the count method of that object will be called.