views:

166

answers:

5

Hi,

Is there a table of how much "work" it takes to execute a given function in PHP? I'm not a compsci major, so I don't have maybe the formal background to know that "oh yeah, strings take longer to work with than integers" or anything like that. Are all steps/lines in a program created equal? I just don't even know where to start researching this.

I'm currently doing some Project Euler questions where I'm very sure my answer will work, but I'm timing out my local Apache server at a minute with my requests (and PE has said that all problems can be solved < 1 minute). I don't know how/where to start optimizing, so knowing more about PHP and how it uses memory would be useful. For what it's worth, here's my code for question 206:

<?php
$start = time();
for ($i=1010374999; $i < 1421374999; $i++) { 
$a = number_format(pow($i,2),0,".","");
$c = preg_split('//', $a, -1, PREG_SPLIT_NO_EMPTY);
if ($c[0]==1) {
 if ($c[2]==2) {
  if ($c[4]==3) {
   if ($c[6]==4) {
    if ($c[8]==5) {
     if ($c[10]==6) {
      if ($c[12]==7) {
       if ($c[14]==8) {
        if ($c[16]==9) {
         if ($c[18]==0) {
          echo $i;
         }
        }
       }
      }
     }
    }
   }
  }
 }
}
}
$end = time();
$elapsed = ($end-$start);
echo "<br />The time to calculate was $elapsed seconds";
?>

If this is a wiki question about optimization, just let me know and I'll move it. Again, not looking for an answer, just help on where to learn about being efficient in my coding (although cursory hints wouldn't be flat out rejected, and I realize there are probably more elegant mathematical ways to set up the problem)

+2  A: 

A good tool for taking a look at execution times for your code is xDebug: http://xdebug.org/docs/profiler

It's an installable PHP extension which can be configured to output a complete breakdown of function calls and execution times for your script. Using this, you'll be able to see what in your code is taking longest to execute and try some different approaches.

EDIT: now that I'm actually looking at your code, you're running 400 million+ regex calls! I don't know anything about project Euler, but I have a hard time believing this code can be excuted in under a minute on commodity hardware.

Steve Goodman
His code can't but there a solutions to his problem which can. Which is what this euler project is about. Mathematical problems where it is possible to write solver which operate in under a minute.
jitter
I did a little research on it. Seems like a fun side project, as noted in the accepted answer.
Steve Goodman
+1  A: 

preg_split is likely to be slow because it's using a regex. Is there not a better way to do that line?

Hint: You can access chars in a string like this:

$str = 'This is a test.';
echo $str[0];
Rich Bradshaw
That's a great hint, thanks
Alex Mcp
A: 

Try switching preg_split() to explode() or str_split() which are faster

rojoca
Good suggestion, but how do you know that? Just experience? Are Regular Expressions known to be slow? This is the kind of higher level stuff I just don't know. Any help is great!
Alex Mcp
Yeah, regular expressions are known to be slow. They are very powerful and make it very easy to find/process complex strings but that comes with a price. Most of the time they are ok but doing it inside of a million-times loop is not that efficient.I wont try to explain how it works internally (you can search Google if you really want to know) but suffice to say it has a whole engine behind it to allow all the 'magic' to happen :)
Carlos Lima
The documentation for preg_split() says: "If you don't need the power of regular expressions, you can choose faster (albeit simpler) alternatives like explode() or str_split()." http://php.net/preg_split
rojoca
+5  A: 

There's no such table that's going to tell you how long each PHP function takes to execute, since the time of execution will vary wildly depending on the input.

Take a look at what your code is doing. You've created a loop that's going to run 411,000,000 times. Given the code needs to complete in less than 60 seconds (a minute), in order to solve the problem you're assuming each trip through the loop will take less than (approximately) .000000145 seconds. That's unreasonable, and no amount of using the "right" function will solve your call. Try your loop with nothing in there

for ($i=1010374999; $i < 1421374999; $i++) { 

}

Unless you have access to science fiction computers, this probably isn't going to complete execution in less than 60 seconds. So you know this approach will never work.

This is known a brute force solution to a problem. The point of Project Euler is to get you thinking creatively, both from a math and programming point of view, about problems. You want to reduce the number of trips you need to take through that loop. The obvious solution will never be the answer here.

I don't want to tell you the solution, because the point of these things is to think your way through it and become a better algorithm programmer. Examine the problem, think about it's restrictions, and think about ways you reduce the total number of numbers you'd need to check.

Alan Storm
+1, that's an excellent approach to the "debug/profile problem": Don't assume. Either test/profile it (diligently, even more so if you have a "synthetic" test) or pick one part where you can make a _definitive_ statement. I was just about firing up the profiler but ... .000000145 seconds per loop, case closed, I love it ;-)
VolkerK
Thanks: A) For not answering it, B) For giving reasonable hints, C) for not assuming I have a certain level of knowledge, which I don't. Good answer!
Alex Mcp
A: 

First, here's a slightly cleaner version of your function, with debug output

<?php
$start = time();
$min = (int)floor(sqrt(1020304050607080900));
$max = (int)ceil(sqrt(1929394959697989990));

for ($i=$min; $i < $max; $i++) {
$c = $i * $i;
echo $i, ' => ', $c, "\n";
if ($c[0]==1
    && $c[2]==2
        && $c[4]==3
        && $c[6]==4
        && $c[8]==5
        && $c[10]==6
        && $c[12]==7
        && $c[14]==8
        && $c[16]==9
        && $c[18]==0)
  {
    echo $i;
        break;
  }
}
$end = time();
$elapsed = ($end-$start);
echo "<br />The time to calculate was $elapsed seconds";

And here's the first 10 lines of output:

1010101010 => 1020304050403020100
1010101011 => 1020304052423222121
1010101012 => 1020304054443424144
1010101013 => 1020304056463626169
1010101014 => 1020304058483828196
1010101015 => 1020304060504030225
1010101016 => 1020304062524232256
1010101017 => 1020304064544434289
1010101018 => 1020304066564636324
1010101019 => 1020304068584838361

That, right there, seems like it oughta inspire a possible optimization of your algorithm. Note that we're not even close, as of the 6th entry (1020304060504030225) -- we've got a 6 in a position where we need a 5!

In fact, many of the next entries will be worthless, until we're back at a point where we have a 5 in that position. Why bother caluclating the intervening values? If we can figure out how, we should jump ahead to 1010101060, where that digit becomes a 5 again... If we can keep skipping dozens of iterations at a time like this, we'll save well over 90% of our run time!

Note that this may not be a practical approach at all (in fact, I'm fairly confident it's not), but this is the way you should be thinking. What mathematical tricks can you use to reduce the number of iterations you execute?

Frank Farmer