PHP performance issue: pass referenced variable as non-ref parameter - php

function M1($x, $y){}
function M2(&$x, $y){}
function M3(&$x, &$y){}
$arr = ['a' => ['b' => range(1, 1000)]];
$ref_arr = &$arr['a'];
$var = $ref_arr['b'];
$ref = &$ref_arr['b'];
//N->N, N->N
//0.003000020980835 sec.
for($i = 0; $i < 10000; ++$i)
M1($var, $var);
//N->R, N->N (slow)
//0.59903407096863 sec.
for($i = 0; $i < 10000; ++$i)
M2($var, $var);
//N->R, N->R
//0.003000020980835 sec.
for($i = 0; $i < 10000; ++$i)
M3($var, $var);
//R->N, R->N (very slow)
//1.1980690956116 sec.
for($i = 0; $i < 10000; ++$i)
M1($ref, $ref);
//R->R, R->N (slow)
//0.58603405952454 sec.
for($i = 0; $i < 10000; ++$i)
M2($ref, $ref);
//R->R, R->R
//0.003000020980835 sec.
for($i = 0; $i < 10000; ++$i)
M3($ref, $ref);
As I know, when pass a reference variable as a non-reference parameter, PHP will copy the value. So this line takes the longest time.
M1($ref, $ref); //1.1980690956116 sec.
But how to explain the performance of these 2 lines?
M2($var, $var); //0.59903407096863 sec.
M3($var, $var); //0.003000020980835 sec.

After serveral years since no one give me an answer, so I decide to answer by myself.
The reason is still the COPY-ON-WRITE mechanism in PHP, but when pass a parameter with different reference type, the copy happens immediately before the function call starts.
Define:
N for non-ref variable
R for ref variable
-> for parameter passing
These 3 forms won't trigger parameter copy:
N -> N
R -> R
N (ref count = 1) -> R
And for these 2 forms a parameter copy is always happens.
R -> N
N (ref count > 1) -> R
So when you decide to call a function with reference parameter (usually array functions like sort, reset), pass an array with refcount more than 1 is not a good idea.
Bad example:
$arr1 = [];
$arr2 = $arr1;
sort($arr1);
Good example:
$arr1 = [];
$arr2 = $arr1;
unset($arr2);
sort($arr1);

Related

PHP. Assigning values to an array within a loop

OK, simple question. New to PHP. How do I accomplish something similar to the following in PHP? This is no specific language intentionally.
i = 0;
j = 0;
k = 50;
While i <= 5
While j <= 10
myarray(i,j) = k
i = i + 1
j = j + 1
k = k + 2
next
next
I have found much info regarding arrays and loops in PHP and none seem to accomplish this simple task. Please don't be snarky... just give me a hand here.
You start each inner while j<=10.
Note that you didn't set $j to 0.
So correct the code is follows:
$i = 0;
$k = 50;
While( $i <= 5 ) {
$j = 0;
While( $j <= 10) {
$myarray[$i][$j] = $k;
$j++;
$k = $k + 2;
}
$i++;
}
Your pseudo code leaves some ambiguities, but maybe you mean to do the following:
$k=50;
for ($i=0; $i<=5; $i++)
for ($j=0; $j<=10; $j++, $k+=2)
$myarray[$i][$j]=$k;
This is a nested for-loop. The actual assignment to $myarray will happen 6*11=66 times in total and $k will have values from 50 to 115.
In PHP arrays don't need to be declared. You can simply assign a value to an arbitrary index of a given variable.
Here is a little demo.

Optimisation of O(n4) complexity to O(n) in PHP nested for loop

I am writing one logic to iterate numbers first and then additional logic to putting them into particular subset of array.
What does this code do :
Code accept first $n
its create array of $n number from 1 to $n
Then started converting to subset of $main_array to possible one like
['1'] [1,2] [1,2,3] [2] [2,3] [3] etc. same like this
After creating subset i am counting those some subset which satisfy condition
Condition is xyz[0] should not come in subset with abc[0] vice versa xyz[i] should not come in subset abc[i]. Example 2 and 3 is coming subset then dont count that subset, same 1 and 4 is coming then dont count
here is my nested for loop :
$n = 1299;
$main_array = range(1,$n);
$counter = 0;
$count = sizeof($abc); // $abc and $xyz size will same always.
$abc = [2,1];
$xyz = [3,4];
for ($i=0; $i <$n; $i++) {
for($j = $i;$j < $n; $j++){
$interval_array = array();
for ($k = $i; $k <= $j; $k++){
array_push($interval_array,$main_array[$k]);
}
$counter++;
for ($l=0; $l < $count ; $l++) {
//if block here to additional condition using in_array() php function. which do $counter--
if(in_array($abc[$l], $interval_array) &&
in_array($xyz[$l], $interval_array)){
$counter--;
break;
}
}
}
}
$main_array i have to create on the spot after receiving $n values.
Following is cases :
when running $n = 4 its run in 4s
when running $n = 1200 or 1299 or more than 1000 its run in 60s-123s
Expected execution timing is 9s. I reduce from 124s to 65s by removing function calling inside for loop but its not coming to point.
Expectation of code is if i have array like
$array = [1,2,3];
then
subset need to generate :
[1],[1,2],[1,2,3],[2],[2,3],[3]
Any help in this ?
It's difficult to test performance against your experience, but this solution removes one of the loops.
The way you repeatedly build $interval_array is not needed, what this code does is to just add the new value from the main array on each $j loop. This array is then reset only in the outer loop and so it just keeps the last values and adds 1 extra value each time...
for ($i=0; $i <$n; $i++) {
$interval_array = array();
for($j = $i;$j < $n; $j++){
array_push($interval_array,$main_array[$j]);
// Check output
echo implode(",", $interval_array)."\n";
$counter++;
for ($l=0; $l < $count ; $l++) {
if(in_array($abc[$l], $interval_array) &&
in_array($xyz[$l], $interval_array)){
$counter--;
break 2;
}
}
}
}
adding "\n" to better understanding for subset flow.
import datetime
N = list(range(1, int(input("N:")) + 1))
affected_list = list(map(int, input("affected_list").split()))
poisoned_list = list(map(int, input("poisoned_list").split()))
start_time = datetime.datetime.now()
exclude_list = list(map(list, list(zip(affected_list, poisoned_list))))
final_list = []
for i in range(0, len(N)):
for j in range(i + 1, len(N) + 1):
if N[i:j] not in exclude_list:
final_list.append(N[i:j])
print(final_list)
end_time = datetime.datetime.now()
print("Total Time: ", (end_time - start_time).seconds)

How to generate different random number for storage in array

I am not sure but when i print_r the array, both random generated string are the same instead of different.
$amount_of_files = 2;
$generated_file_names = array();
for($i = 0; $i < $amount_of_files; $i++){
$generated_file_names[] = substr(md5(time()), 0, 10);
}
time() returns it's value to the nearest second - your code is executing in much less time than that so the value is the same. If you want random values for each item in the array use rand() or mt_rand() instead.
You can use like this
<?php
$amount_of_files = 2;
$generated_file_names = array();
for($i = 0; $i < $amount_of_files; $i++){
$generated_file_names[] = substr(md5(rand()),0,10);
}
print_r($generated_file_names);
?>
you need microtime() php is looping soo fast 0 to 2 and time() is not changing so md5 is same and sub_str is same for all.

Pre-incrementation vs. post-incrementation

How are they different? Here's what I'm thinking, but I'm not sure....
If you use pre-incrementation, for example in a for loop with ++j, then you are basically saying: "Make a copy of the value of j for use in the loop, then increment j, then go through the statements in the loop with the copy of j." If you are using post-incrementation in the same loop j++, then you are basically saying: "Make a copy of the value of j for use in the loop, then go through the statements in the loop with the copy of j, then increment j."
The reason I'm unsure is because I've created a for loop that multiplies the value of j by 10 and then outputs the result for j=1 through j=12, using both post- and pre-incrementation. The human readable output is exactly the same with post- and pre-incrementation. I'm thinking, 'How are the outputs exactly the same if there isn't some kind of copy operation involved?'
So, I'm guessing the difference between pre- and post-incrementation truly becomes important, in php, when I use references (which act as pointers in php) rather than names for return values? This would be because copies of references aren't made, so pre-incrementation would be: "Increment j, then go through the statements in the loop with the changed value of j, then increment j again...," whereas post-incremetation would look like: "Use the value of j for the statements in the loop, then change the value of j, then go through the loop with the new value of j..."
Pre- or post-incrementing do not magically delay things until later. It's simply inline shorthand.
// pre-increment
$var = 5;
print(++$var); // increments first, then passes value (now 6) to print()
// post-increment
$var = 5;
print($var++); // passes value (still 5) to print(), then increments
Now let's look at a loop.
for ($i = 0; $i < 9; $i++) {
print($i);
}
The last part of the loop declaration (the $i++) is simply the statement to execute after each time through the loop. It "passes" the value to nowhere, then increments it. $i isn't used anywhere at that time. Later when the next statement is executed (print($i);), the value of $i has already increased.
// add 1, then do nothing with $i
for ($i = 0; $i < 9; ++$i) {}
// do nothing with $i, then add 1
for ($i = 0; $i < 9; $i++) {}
Whichever way you do it, $i will be the same within the loop.
If it helps, you can think of them as small routines that kind of do this:
// ++$i
{
$i = $i + 1;
return $i;
}
// $i++
{
return $i;
$i = $i + 1;
}
As I reread your question, I think the confusion is more with how the loop works than how increment operators work. Keeping in mind that the increment is a straightforward, all-at-once operation, here's how third expression in the loop works.
// here's a basic loop
for ($i = 0; $i < 9; $i++) {
// do loop stuff
print($i);
}
// this is exactly what happens
for ($i = 0; $i < 9; ) {
// do loop stuff
print($i);
$i++;
}
Just because that last line can be put in the loop declaration doesn't give it any special powers. There are no references or anything used behind the scenes. The same $i variable is seen both inside and outside the loop. Every statement inside or outside the loop directly looks up the value of $i when necessary. That's it. No funny business.
When doing $x++, you are post-incrementing... This means that the incrementation will only occur after the statement has been evaluated.
So, given the following code:
$x = 10; $y = 0; $z = 5;
$y = $z * $x++;
PHP does this:
$x = 10; $y = 0; $z = 5;
$y = $z * $x++;
// Ignore Post-Increment, Evalutate
$y = $z * $x;
$y = 5 * 10;
// Now Increment x - POST-INCREMENT
$x = $x + 1;
$x = 10 + 1;
$x = 11;
// Continue evaluating statement
$y = 5 * 10;
$y = 50;
When doing ++$x, you are pre-incrementing... This means that the incrementation will occur before the statement is evaluated:
$x = 10; $y = 0; $z = 5;
$y = $z * ++$x;
// Do Pre-Increment
$x = $x + 1;
$x = 10 + 1;
$x = 11;
// Evaluate
$y = $z * $x;
$y = 5 * 11;
$y = 55;
In the case of a for loop in PHP, PHP evaluates a for loop as follows:
for($i = 0; $i < 30; $i++) {
doSomething();
}
// Is evaluated EXACTLY as such by PHP
$i = 0;
while($i < 30) {
doSomething();
$i++;
}
The first expression ($i = 0) is evaluated (executed) once unconditionally at the beginning of the loop.
In the beginning of each iteration, $i < 30 is evaluated. If it evaluates to TRUE, the loop continues and the nested statement(s) are executed. If it evaluates to FALSE, the execution of the loop ends.
At the end of each iteration, $i++ is evaluated (executed) as an independent expression.
Therefore, post-incrementing or pre-incrementing a variable as the third expression in the loop doesn't have an effect on the behavior of it. In this simple case, both expressions will behave exactly the same.
However, in a complex loop such as the following:
for($i = $j = 0; $i < 30; $i += ++$j) {
$j = getResult($j);
}
Post-incrementing or pre-incrementing $j directly affects the value of $i according to the examples above. In this case, you need to choose exactly what you want to do.
$i = 0;
echo $i++;
echo $i;
$j=0;
echo ++$j;
echo $j;
Pre increment display incremented value. But Post increment display value then increment. About code will output 01 and 11

How to get the greatest difference among a list of ordered numbers in PHP?

For example,
1,3,6,8,11,45,99
The interval between numbers is:
2,3,2,3,34,54
So the greatest difference is 54.
How to implement this function?
function get_greatest_diff($arr_of_numbers)
{}
You got a lot of different options:
Sort the array, then compare the first and the last element
For each element, compare it to each subsequent element. Keep the highest difference in memory.
Implement some kind of merge-sort, but return the difference instead of the original sorted values.
You should handle the case where the array has less than 2 elements separately:
$maxDiff = -1;
for ($i = 0; $i + 1 < count($array); $i++) {
$diff = $array[$i + 1] - $array[$i];
if ($diff > $maxDiff)
$maxDiff = $diff;
}
}
You should do something like this :
$greatest_diff = 0;
for($i = 0; $i < sizeof($arr_of_numbers) - 1; $i++)
{
$current_diff = $arr_of_numbers[$i + 1] - $arr_of_numbers[$i];
if($current_diff > $greatest_diff){
$greatest_diff = $current_diff;
}
}
echo $greatest_diff;

Categories