Why is this loop heavy code so slow in Python? Possible Project Euler spoilers

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • jwrweatherley@gmail.com

    Why is this loop heavy code so slow in Python? Possible Project Euler spoilers

    I'm pretty new to python, but am very happy with it. As well as using
    it at work I've been using it to solve various puzzles on the Project
    Euler site - http://projecteuler.net. So far it has not let me down,
    but it has proved surprisingly slow on one puzzle.

    The puzzle is: p is the perimeter of a right angle triangle with
    integral length sides, {a,b,c}. which value of p < 1000, is the
    number of solutions {a,b,c} maximised?

    Here's my python code:

    #!/usr/local/bin/python

    solutions = [0] * 1001
    p = 0

    for a in xrange(1, 1000):
    for b in xrange(1, 1000 - a):
    for c in xrange(1, 1000 - a - b):
    p = a + b + c
    if p < 1000:
    if a ** 2 + b ** 2 == c ** 2:
    solutions[p] += 1

    max = 0
    maxIndex = 0
    index = 0
    for solution in solutions:
    if solution max:
    max = solution
    maxIndex = index
    index += 1

    print maxIndex


    It takes 2 minutes and twelve seconds on a 2.4GHz Core2Duo MacBook
    Pro. Surprised at how slow it was I implemented the same algorithm in
    C:

    #include <stdio.h>
    #include <stdlib.h>

    int main() {
    int* solutions = calloc(1000, sizeof(int));

    int p;
    for(int a = 1; a < 1000; ++a) {
    for(int b = 1; b < 1000 - a; ++b) {
    for(int c = 1; c < 1000 - a - b; ++c) {
    p = a + b + c;
    if(p < 1000) {
    if(a * a + b * b == c * c) {
    solutions[p] += 1;
    }
    }
    }
    }
    }

    int max = 0;
    int maxIndex = 0;

    for(int i = 0; i < 1000; ++i) {
    if(solutions[i] max) {
    max = solutions[i];
    maxIndex = i;
    }
    }
    printf("%d\n", maxIndex);
    return 0;
    }


    gcc -o 39 -std=c99 -O3 39.c

    The resulting executable takes 0.24 seconds to run. I'm not expecting
    a scripting language to run faster than native code, but I was
    surprised at how much slower it was in this case. Any ideas as to what
    is causing python so much trouble in the above code?

  • Arnaud Delobelle

    #2
    Re: Why is this loop heavy code so slow in Python? Possible Project Euler spoilers

    On Sep 2, 12:51 pm, jwrweather...@g mail.com wrote:
    I'm pretty new to python, but am very happy with it. As well as using
    it at work I've been using it to solve various puzzles on the Project
    Euler site -http://projecteuler.ne t. So far it has not let me down,
    but it has proved surprisingly slow on one puzzle.
    >
    The puzzle is: p is the perimeter of a right angle triangle with
    integral length sides, {a,b,c}. which value of p < 1000, is the
    number of solutions {a,b,c} maximised?
    >
    Here's my python code:
    >
    #!/usr/local/bin/python
    >
    solutions = [0] * 1001
    p = 0
    >
    for a in xrange(1, 1000):
    for b in xrange(1, 1000 - a):
    for c in xrange(1, 1000 - a - b):
    p = a + b + c
    if p < 1000:
    if a ** 2 + b ** 2 == c ** 2:
    solutions[p] += 1
    >
    max = 0
    maxIndex = 0
    index = 0
    for solution in solutions:
    if solution max:
    max = solution
    maxIndex = index
    index += 1
    >
    print maxIndex
    >
    It takes 2 minutes and twelve seconds on a 2.4GHz Core2Duo MacBook
    Pro. Surprised at how slow it was I implemented the same algorithm in
    C:
    >
    #include <stdio.h>
    #include <stdlib.h>
    >
    int main() {
    int* solutions = calloc(1000, sizeof(int));
    >
    int p;
    for(int a = 1; a < 1000; ++a) {
    for(int b = 1; b < 1000 - a; ++b) {
    for(int c = 1; c < 1000 - a - b; ++c) {
    p = a + b + c;
    if(p < 1000) {
    if(a * a + b * b == c * c) {
    solutions[p] += 1;
    }
    }
    }
    }
    }
    >
    int max = 0;
    int maxIndex = 0;
    >
    for(int i = 0; i < 1000; ++i) {
    if(solutions[i] max) {
    max = solutions[i];
    maxIndex = i;
    }
    }
    printf("%d\n", maxIndex);
    return 0;
    >
    }
    >
    gcc -o 39 -std=c99 -O3 39.c
    >
    The resulting executable takes 0.24 seconds to run. I'm not expecting
    a scripting language to run faster than native code, but I was
    surprised at how much slower it was in this case. Any ideas as to what
    is causing python so much trouble in the above code?
    from math import sqrt

    solutions = [0] * 1001
    p = 0

    for a in xrange(1, 1000):
    a2 = a*a
    for b in xrange(1, 1000 - a):
    c = sqrt(a2 + b*b)
    if c == int(c) and a+b+c < 1000:
    solutions[a+b+int(c)] += 1

    max = 0
    maxIndex = 0
    index = 0
    for solution in solutions:
    if solution max:
    max = solution
    maxIndex = index
    index += 1

    print maxIndex

    --
    Arnaud


    Comment

    • Greg Copeland

      #3
      Re: Why is this loop heavy code so slow in Python? Possible Project Euler spoilers

      On Sep 2, 7:20 am, Arnaud Delobelle <arno...@google mail.comwrote:
      On Sep 2, 12:51 pm, jwrweather...@g mail.com wrote:
      >
      >
      >
      I'm pretty new to python, but am very happy with it. As well as using
      it at work I've been using it to solve various puzzles on the Project
      Euler site -http://projecteuler.ne t. So far it has not let me down,
      but it has proved surprisingly slow on one puzzle.
      >
      The puzzle is: p is the perimeter of a right angle triangle with
      integral length sides, {a,b,c}. which value of p < 1000, is the
      number of solutions {a,b,c} maximised?
      >
      Here's my python code:
      >
      #!/usr/local/bin/python
      >
      solutions = [0] * 1001
      p = 0
      >
      for a in xrange(1, 1000):
      for b in xrange(1, 1000 - a):
      for c in xrange(1, 1000 - a - b):
      p = a + b + c
      if p < 1000:
      if a ** 2 + b ** 2 == c ** 2:
      solutions[p] += 1
      >
      max = 0
      maxIndex = 0
      index = 0
      for solution in solutions:
      if solution max:
      max = solution
      maxIndex = index
      index += 1
      >
      print maxIndex
      >
      It takes 2 minutes and twelve seconds on a 2.4GHz Core2Duo MacBook
      Pro. Surprised at how slow it was I implemented the same algorithm in
      C:
      >
      #include <stdio.h>
      #include <stdlib.h>
      >
      int main() {
      int* solutions = calloc(1000, sizeof(int));
      >
      int p;
      for(int a = 1; a < 1000; ++a) {
      for(int b = 1; b < 1000 - a; ++b) {
      for(int c = 1; c < 1000 - a - b; ++c) {
      p = a + b + c;
      if(p < 1000) {
      if(a * a + b * b == c * c) {
      solutions[p] += 1;
      }
      }
      }
      }
      }
      >
      int max = 0;
      int maxIndex = 0;
      >
      for(int i = 0; i < 1000; ++i) {
      if(solutions[i] max) {
      max = solutions[i];
      maxIndex = i;
      }
      }
      printf("%d\n", maxIndex);
      return 0;
      >
      }
      >
      gcc -o 39 -std=c99 -O3 39.c
      >
      The resulting executable takes 0.24 seconds to run. I'm not expecting
      a scripting language to run faster than native code, but I was
      surprised at how much slower it was in this case. Any ideas as to what
      is causing python so much trouble in the above code?
      >
      from math import sqrt
      >
      solutions = [0] * 1001
      p = 0
      >
      for a in xrange(1, 1000):
      a2 = a*a
      for b in xrange(1, 1000 - a):
      c = sqrt(a2 + b*b)
      if c == int(c) and a+b+c < 1000:
      solutions[a+b+int(c)] += 1
      >
      max = 0
      maxIndex = 0
      index = 0
      for solution in solutions:
      if solution max:
      max = solution
      maxIndex = index
      index += 1
      >
      print maxIndex
      >
      --
      Arnaud
      For the curious:

      O O + P A A + P
      ======= ======= ======= =======
      2:22.56 0:25.65 0:00.75 0:00.20

      O = Original Implementation
      P = Psyco (psyco.full())
      A = Arnaud's Revised Implementation

      Comment

      • rzed

        #4
        Re: Why is this loop heavy code so slow in Python? Possible Project Euler spoilers

        jwrweatherley@g mail.com wrote in
        news:1188733902 .513512.87510@r 34g2000hsd.goog legroups.com:
        The puzzle is: p is the perimeter of a right angle triangle with
        integral length sides, {a,b,c}. which value of p < 1000, is the
        number of solutions {a,b,c} maximised?
        >
        Here's my python code:
        >
        #!/usr/local/bin/python
        >
        solutions = [0] * 1001
        p = 0
        >
        for a in xrange(1, 1000):
        for b in xrange(1, 1000 - a):
        for c in xrange(1, 1000 - a - b):
        p = a + b + c
        if p < 1000:
        if a ** 2 + b ** 2 == c ** 2:
        solutions[p] += 1
        >
        Once p >= 1000, it ain't goin' back. If you break out of the
        innermost loop here after that happens, you'll save a bunch of
        time.
        max = 0
        maxIndex = 0
        index = 0
        for solution in solutions:
        if solution max:
        max = solution
        maxIndex = index
        index += 1
        >
        print maxIndex
        >
        >
        It takes 2 minutes and twelve seconds on a 2.4GHz Core2Duo
        MacBook Pro.
        >
        [...]
        The resulting executable takes 0.24 seconds to run. I'm not
        expecting a scripting language to run faster than native code,
        but I was surprised at how much slower it was in this case. Any
        ideas as to what is causing python so much trouble in the above
        code?
        >

        Comment

        • jwrweatherley@gmail.com

          #5
          Re: Why is this loop heavy code so slow in Python? Possible Project Euler spoilers

          [snip code]

          Thanks for that. I realise that improving the algorithm will speed
          things up. I wanted to know why my less than perfect algorithm was so
          much slower in python than exactly the same algorithm in C. Even when
          turning off gcc's optimiser with the -O0 flag, the C version is still
          100 times quicker.

          Comment

          • Mark Dickinson

            #6
            Re: Why is this loop heavy code so slow in Python? Possible Project Euler spoilers

            On Sep 2, 9:45 am, jwrweather...@g mail.com wrote:
            [snip code]
            >
            Thanks for that. I realise that improving the algorithm will speed
            things up. I wanted to know why my less than perfect algorithm was so
            much slower in python than exactly the same algorithm in C. Even when
            turning off gcc's optimiser with the -O0 flag, the C version is still
            >
            100 times quicker.
            Well, for one thing, you're creating half a million xrange objects in
            the course of the search. All the C code has
            to do is increment a few integers.

            Mark

            Comment

            • Ivan Wang

              #7
              Re: Why is this loop heavy code so slow in Python? Possible Project Euler spoilers

              On Sep 2, 9:45 pm, jwrweather...@g mail.com wrote:
              [snip code]
              >
              Thanks for that. I realise that improving the algorithm will speed
              things up. I wanted to know why my less than perfect algorithm was so
              much slower in python than exactly the same algorithm in C. Even when
              turning off gcc's optimiser with the -O0 flag, the C version is still
              >
              >
              >
              100 times quicker.- Hide quoted text -
              >
              - Show quoted text -
              Maybe Python is the slowest programming language in the world.
              So there is a joke: some python hater said that "python" can only
              crawl rather than run. :)

              Python is slow because:
              (1) dynamic binding
              (2) it is a interpretation language
              For example, in C code, an interger expression "a+b" will directly
              generate an assembly code "add" for x86 processors.
              A python interpreter, on the other side, need detect the type of a and
              b first, then choose the right "+" operation for them and use a
              evaluation stack to get the result.

              Psyco is a JIT compiler with just in time specialization which can
              somehow solve these two problems


              Comment

              • Arnaud Delobelle

                #8
                Re: Why is this loop heavy code so slow in Python? Possible Project Euler spoilers

                On Sep 2, 12:51 pm, jwrweather...@g mail.com wrote:
                [...]
                The resulting executable takes 0.24 seconds to run. I'm not expecting
                a scripting language to run faster than native code, but I was
                surprised at how much slower it was in this case. Any ideas as to what
                is causing python so much trouble in the above code?
                Sorry I didn't answer your question at all in my previous post
                (although my modified method gets the answer in about 6s on a measly
                PB G4 1GHz :).
                Your little code snippet is just about the worst bit of code for
                python to be compared to C. Three loops, only integer arithmetic:
                that's not going to make Python shine!

                Nevertheless as you optimise your C snippet (-O3), there are probably
                a few things that the compiler does for you:

                * as in the inner loop, a*a + b*b always remain the same, it is
                probably stored in a register once and for all
                * in the second loop, a*a remains the same so it might be calculated
                once and for all as well.

                It gives this:

                M = 1000
                solutions = [0] * M

                def f1():
                "Your original implementation"
                for a in xrange(1, M):
                for b in xrange(1, M - a):
                for c in xrange(1, M - a - b):
                if a**2 + b**2 == c**2:
                solutions[a+b+c] += 1

                def f2():
                "a*a + b*b precalculated"
                for a in xrange(1, M):
                a2 = a*a
                for b in xrange(1, M - a):
                s = a2 + b*b
                for c in xrange(1, M - a - b):
                if s == c*c:
                solutions[a+b+c] += 1

                It doesn't make it as quick as C of course, but more than halves the
                execution time.

                --
                Arnaud


                Comment

                • Cameron Laird

                  #9
                  Re: Why is this loop heavy code so slow in Python? Possible Project Euler spoilers

                  In article <1188742231.470 668.29910@g4g20 00hsf.googlegro ups.com>,
                  Mark Dickinson <dickinsm@gmail .comwrote:
                  >On Sep 2, 9:45 am, jwrweather...@g mail.com wrote:
                  >[snip code]
                  >>
                  >Thanks for that. I realise that improving the algorithm will speed
                  >things up. I wanted to know why my less than perfect algorithm was so
                  >much slower in python than exactly the same algorithm in C. Even when
                  >turning off gcc's optimiser with the -O0 flag, the C version is still
                  >>
                  100 times quicker.
                  >
                  >Well, for one thing, you're creating half a million xrange objects in
                  >the course of the search. All the C code has
                  >to do is increment a few integers.
                  >
                  >Mark
                  >
                  Right: Mr. Dickinson's original question is entirely
                  legitimate, and it's not adequate to respond, as some
                  follow-ups did, with ways to improve the Python-coded
                  algorithm.

                  The correct answer, which I want to reinforce, is that
                  the exhibited Python and C versions are NOT "exactly
                  the same algorithm", at least not without more quali-
                  fication. Part of Python expertise is to recognize
                  that creation of xrange objects, mentioned above, is
                  far from free. Also, -O3 gives C the opportunity,
                  also remarked in a follow-up, to factor calculations
                  outside their loops.

                  Comment

                  • Bruno Desthuilliers

                    #10
                    Re: Why is this loop heavy code so slow in Python? Possible ProjectEuler spoilers

                    Ivan Wang a écrit :
                    On Sep 2, 9:45 pm, jwrweather...@g mail.com wrote:
                    >
                    >>[snip code]
                    >>
                    >>Thanks for that. I realise that improving the algorithm will speed
                    >>things up. I wanted to know why my less than perfect algorithm was so
                    >>much slower in python than exactly the same algorithm in C. Even when
                    >>turning off gcc's optimiser with the -O0 flag, the C version is still
                    >>
                    >>
                    >>
                    >>
                    >>>100 times quicker.- Hide quoted text -
                    >>
                    >>- Show quoted text -
                    >
                    Maybe Python is the slowest programming language in the world.
                    So there is a joke: some python hater said that "python" can only
                    crawl rather than run. :)
                    >
                    Python is slow because:
                    (1) dynamic binding
                    Yes.
                    (2) it is a interpretation language
                    Not quite. It's compiled to byte-code - just like Java (would you call
                    Java an 'interpreted language' ?)

                    Comment

                    • Paul Rubin

                      #11
                      Re: Why is this loop heavy code so slow in Python? Possible Project Euler spoilers

                      aleax@mac.com (Alex Martelli) writes:
                      ...which suggests that creating an xrange object is _cheaper_ than
                      indexing a list...
                      Why not re-use the xrange instead of keeping a list around?

                      Python 2.4.4 (#1, Oct 23 2006, 13:58:00)
                      >>a = xrange(3)
                      >>print list(a)
                      [0, 1, 2]
                      >>print list(a)
                      [0, 1, 2]

                      Comment

                      • Alex Martelli

                        #12
                        Re: Why is this loop heavy code so slow in Python? Possible Project Euler spoilers

                        Paul Rubin <http://phr.cx@NOSPAM.i nvalidwrote:
                        aleax@mac.com (Alex Martelli) writes:
                        ...which suggests that creating an xrange object is _cheaper_ than
                        indexing a list...
                        >
                        Why not re-use the xrange instead of keeping a list around?
                        >
                        Python 2.4.4 (#1, Oct 23 2006, 13:58:00)
                        >>a = xrange(3)
                        >>print list(a)
                        [0, 1, 2]
                        >>print list(a)
                        [0, 1, 2]
                        Reusing xranges is exactly what my code was doing -- at each for loop
                        you need an xrange(1, k) for a different value of k, which is why you
                        need some container to keep them around (and a list of xrange objects is
                        the simplest applicable container).

                        Your suggestion doesn't appear to make any sense in the context of the
                        optimization problem at hand -- what list(...) calls are you thinking
                        of?! Please indicate how your suggestion would apply in the context of:

                        def f3(M=M, solutions=solut ions):
                        "pull out all the stops"
                        xrs = [xrange(1, k) for k in xrange(0, M+1)]
                        for a in xrs[M]:
                        a2 = a*a
                        for b in xrs[M-a]:
                        s = a2 + b*b
                        for c in xrs[M-a-b]:
                        if s == c*c:
                        solutions[a+b+c] += 1


                        Alex

                        Comment

                        • Paul Rubin

                          #13
                          Re: Why is this loop heavy code so slow in Python? Possible Project Euler spoilers

                          aleax@mac.com (Alex Martelli) writes:
                          Reusing xranges is exactly what my code was doing
                          Oh cool, I missed that, I was just going by the text description.
                          Looking at the code, yes, it's re-using the xranges. Thanks.

                          Comment

                          • Mark Dickinson

                            #14
                            Re: Why is this loop heavy code so slow in Python? Possible Project Euler spoilers

                            On Sep 2, 12:55 pm, al...@mac.com (Alex Martelli) wrote:
                            Mark Dickinson <dicki...@gmail .comwrote:
                            Well, for one thing, you're creating half a million xrange objects in
                            the course of the search. All the C code has
                            to do is increment a few integers.
                            >
                            I don't think the creation of xrange objects is a meaningful part of
                            Python's execution time here. Consider:
                            [...]
                            Agreed---I just came to the same conclusion after doing some tests.
                            So maybe it's the billion or so integer objects being created that
                            dominate the running time? (Not sure how many integer objects
                            actually are created here: doesn't Python cache *some* small
                            integers?)

                            Mark


                            Comment

                            • =?ISO-8859-1?Q?=22Martin_v=2E_L=F6wis=22?=

                              #15
                              Re: Why is this loop heavy code so slow in Python? Possible ProjectEuler spoilers

                              >(2) it is a interpretation language
                              Not quite. It's compiled to byte-code - just like Java (would you call
                              Java an 'interpreted language' ?)
                              Python is not implemented like Java. In Java (at least in HotSpot),
                              the byte code is further compiled to machine code before execution;
                              in Python, the byte code is interpreted.

                              Whether this makes Python an interpreter or a compiler,
                              I don't know.

                              Regards,
                              Martin

                              Comment

                              Working...