5

Here are two programs that naively calculate the number of prime numbers <= n.
One is in Python and the other is in Java.

public class prime{
    public static void main(String args[]){
        int n = Integer.parseInt(args[0]);
        int nps = 0;
 boolean isp;

        for(int i = 1; i <= n; i++){
            isp = true;

            for(int k = 2; k < i; k++){
               if( (i*1.0 / k) == (i/k) ) isp = false;
            }
            if(isp){nps++;}
 }
        System.out.println(nps);
    }
}


`#!/usr/bin/python`                                                                                                                                        
import sys
n = int(sys.argv[1])
nps = 0

for i in range(1,n+1):
    isp = True
    for k in range(2,i):
        if( (i*1.0 / k) == (i/k) ): isp = False
    if isp == True: nps = nps + 1
print nps

Running them on n=10000 I get the following timings.
shell:~$ time python prime.py 10000 && time java prime 10000
1230

real 0m49.833s
user 0m49.815s
sys 0m0.012s
1230

real 0m1.491s
user 0m1.468s
sys 0m0.016s

Am I using for loops in python in an incorrect manner here or is python actually just this much slower?

I'm not looking for an answer that is specifically crafted for calculating primes but rather I am wondering if python code is typically utilized in a smarter fashion.

The Java code was compiled with javac 1.6.0_20
Run with java version "1.6.0_18"
OpenJDK Runtime Environment (IcedTea6 1.8.1) (6b18-1.8.1-0ubuntu1~9.10.1) OpenJDK Client VM (build 16.0-b13, mixed mode, sharing)

Python is:
Python 2.6.4 (r264:75706, Dec 7 2009, 18:45:15)

fthinker
  • 616
  • 2
  • 9
  • 17
  • 1
    One obvious thing you might want to do is change `range` to `xrange` - should make a bit of a difference for the memory usage, possibly also speed. – Kos Nov 12 '10 at 23:06
  • In general, it is enough to divide `i` by the numbers up to square root of `i`. And probably even better would be the [Sieve of Eratosthenes](http://en.wikipedia.org/wiki/Sieve_of_Eratosthenes) but that is not the scope of the question ;) – Felix Kling Nov 12 '10 at 23:09
  • Instead of "for i in range(a, b) doStuff();" try something like "i = a ; while (i <= b) { doStuff(); i = i + 1 }" – Laurențiu Dascălu Nov 12 '10 at 23:11
  • 1
    @Laurențiu: Wat? Even if that's slightly slower (I highly doubt it, range is written in C), it's absolutely non-idimatic. –  Nov 12 '10 at 23:27
  • 1
    @Laurențiu, delnan: more than that, the code is wrong. `for i in range(a, b)` is not the same as `while i <= b`; it's the same as `while i < b`. This tells you all you need to know about the original advice. – hughdbrown Nov 12 '10 at 23:46

9 Answers9

10

As has been pointed out, straight Python really isn't made for this sort of thing. That the prime checking algorithm is naive is also not the point. However, with two simple things I was able to greatly reduce the time in Python while using the original algorithm.

First, put everything inside of a function, call it main() or something. This decreased the time on my machine in Python from 20.6 seconds to 14.54 seconds. Doing things globally is slower than doing them in a function.

Second, use Psyco, a JIT compiler. This requires adding two lines to the top of the file (and of course having psyco installed):

import psyco
psyco.full()

This brought the final time to 2.77 seconds.

One last note. I decided for kicks to use Cython on this and got the time down to 0.8533. However, knowing how to make the few changes to make it fast Cython code isn't something that I recommend for the casual user.

Justin Peel
  • 45,189
  • 5
  • 55
  • 78
  • +1 for showing how much performance one can get out of Python extremely easily (86%!). –  Nov 12 '10 at 23:51
  • 1
    Adding the break statement, doing integer modulo test, using xrange instead of range, moving it into a function, and iterating to square root brought runtime from 17.4 seconds to 0.037 seconds on my machine - a 470x speedup without Psyco. Without changing the original algorithm (no break or sqrt), but using modulo and xrange in a function, it comes down to 4.6 seconds, a 3.7x speedup. – Russell Borogove Nov 13 '10 at 01:46
  • @Russell, you missed the point of the question I think. This was about comparing a Java implementation vs. a Python implementation of the same algorithm. – Justin Peel Nov 13 '10 at 03:10
  • Thanks Justin I guess the bottom line to be drawn out of this is... use psycho :) – fthinker Nov 13 '10 at 07:24
  • No, I got the point of the q. Just thought it was interesting side data. – Russell Borogove Nov 13 '10 at 19:27
  • @Russell, ok, just making sure since a lot of people have been mentioning the algorithmic speed-ups. – Justin Peel Nov 14 '10 at 05:02
  • @Justin, there's an implication in the question that Python isn't fast enough. I think it's worth pointing out that algorithmic improvements can trump language improvements in many cases. My own results of a 4x improvement *without* algorithmic changes echos Russell's 3.7x. – Mark Ransom Nov 15 '10 at 05:26
4

Yes, Python is slow, about a hundred times slower than C. You can use xrange instead of range for a small speedup, but other than that it's fine.

Ultimately what you're doing wrong is that you do this in plain Python, instead of using optimized libraries such as Numpy or Psyco.

Java comes with a jit compiler that makes a big difference where you're just crunching numbers.

Jochen Ritzel
  • 94,379
  • 28
  • 188
  • 182
  • "about a hundred times slower than C" ... in somewhat contrived number crunching contests. C becomes a lot less faster when your task is I/O heavy ;) Apart from that: If you have a problem that can be solves relatively easily and efficiently in C, you can use Cython (http://cython.org/). It will generate C code from a Python dialect that adds optional static typing (essential for the performance gain, but truly optional in the part of the module that talks to Python). –  Nov 12 '10 at 23:35
  • 2
    [numpy and psycho in action](http://stackoverflow.com/questions/2068372/fastest-way-to-list-all-primes-below-n-in-python) - they are *really* fast ... – Jochen Ritzel Nov 12 '10 at 23:37
2

You can make your Python about twice as fast by replacing that complicated test with

if i % k == 0: isp = False

You can also make it about eight times faster (for n=10000) than that by adding a break after that isp = False.

Also, do yourself a favor and skip the even numbers (adding one to nps to start to include 2).

Finally, you only need k to go up to sqrt(i).

Of course, if you make the same changes in the Java, it's still about 10x faster than the optimized Python.

novalis
  • 1,092
  • 6
  • 12
2

Boy, when you said it was a naive implementation, you sure weren't joking!

But yes, a one to two order of magnitude difference in performance is not unexpected when comparing JIT-compiled, optimized machine code with an interpreted language. An alternative Python implementation such as Jython, which runs on the Java VM, may well be faster for this task; you could give it a whirl. Cython, which allows you to add static typing to Python and get C-like performance in some cases, may be worth investigating as well.

Even when considering the standard Python interpreter, CPython, though, the question is: is Python fast enough for the task at hand? Will the time you save writing the code in a dynamic language like Python make up for the extra time spent running it? If you had to write a given program in Java, would it seem like too much work to be worth the trouble?

Consider, for example, that a Python program running on a modern computer will be about as fast as a Java program running on a 10-year-old computer. The computer you had ten years ago was fast enough for many things, wasn't it?

Python does have a number of features that make it great for numerical work. These include an integer type that supports an unlimited number of digits, a decimal type with unlimited precision, and an optional library called NumPy specifically for calculations. Speed of execution, however, is not generally one of its major claims to fame. Where it excels is in getting the computer to do what you want with minimal cognitive friction.

kindall
  • 158,047
  • 31
  • 244
  • 289
  • I doubt Jython (or IronPython) will reach the speed of Java (or C#). Yes, they have the same JIT, but they still have to deal with all the dynamicness etc. which is the main reason why dynamic languages are so slow in the first place - "traditional" (static typing) optimizations only work on them to a certain degree. –  Nov 12 '10 at 23:25
  • Yeah, upon reflection, I suppose Jython wouldn't be as fast as Java, but it still might be faster than the CPython, which is a straight-up interpreter. Cython (which adds optional staticness) might do better. – kindall Nov 12 '10 at 23:27
  • I thought of Cython, too. I even think Cython can *beat* Java here. And yes, at least IronPython performs better than CPython in many cases (CPython is a bytecode VM though, not a plain interpreter). –  Nov 12 '10 at 23:40
  • Right, by "straight-up interpreter" I meant to say that the CPython VM doesn't compile to machine code, not that CPython interprets the source directly. The bytecode is interpreted. – kindall Nov 12 '10 at 23:44
0

If you're looking to do it fast, Python probably isn't the way forward, but you could speed it up a bit. First, you're using quite a slow way to test for divisibility. Modulo is quicker. You can also stop the inner loop (with k) as soon as it detects a match. I'd do something like this:

nps = 0
for i in range(1, n+1):
    if all(i % k for k in range(2, i)): # i.e. if divisible by none of them
       nps += 1

That brings it down from 25 s to 1.5 s for me. Using xrange brings it down to 0.9 s.

You could speed it up further by keeping a list of primes you've already found, and only testing those, rather than every number up to i (if i isn't divisible by 2, it won't be divisible by 4, 6, 8...).

Thomas K
  • 35,785
  • 7
  • 76
  • 82
0

Why don't you post something about the memory usage - and not just the speed? Trying to get a simple servlet on tomcat is wasting 3GB on my server.

What you did with the examples up there is not very good. You need to use numpy. Replace for/range with while loops, thus avoiding the list creation.

At last, python is quite suitable for number crunching, at least by people that do it the right way, and know what Sieve of Eratosthenes is, or mod operation is.

vonPetrushev
  • 4,761
  • 5
  • 35
  • 50
  • Yes, proper algorithms really help - they are language-independent though. But yeah, if you want to crunch numbers in Python, you use NumPy and/or psyco and/or Cython. –  Nov 12 '10 at 23:45
0

There are lots of things you can do to this algorithm to speed it up, but most of them would also speed up the Java version as well. Some of those will speed up the Python more than the Java, so they're worth testing.

Here's just a couple of changes that speed it up from 11.4 to 2.8 seconds on my system:

nps = 0 
for i in range(1,n+1): 
    isp = True 
    for k in range(2,i):
        isp = isp and (i % k != 0)
    if isp: nps = nps + 1 
print nps 
Mark Ransom
  • 271,357
  • 39
  • 345
  • 578
0

Python is a language which, ironically, is well-suited for developing algorithms. Even a modified algorithm like this:

# See Thomas K for use of all(), many posters for sqrt optimization
nps = 0
for i in xrange(1, n+1):
    if all(i % k for k in xrange(2, 1 + int(i ** 0.5))):
       nps += 1

runs in significantly under one second. Code like this:

def eras(n):
    last = n + 1
    sieve = [0,0] + range(2, last)
    sqn = int(round(n ** 0.5))
    it = (i for i in xrange(2, sqn + 1) if sieve[i])
    for i in it:
        sieve[i*i:last:i] = [0] * (n//i - i + 1)
    return filter(None, sieve)

is faster still. Or try out these.

The thing is, python is usually fast enough for designing your solution. If it is not fast enough for production, use numpy or Jython to goose more performance out of it. Or move it to a compiled language, taking your algorithm observations learned in python with you.

hughdbrown
  • 42,826
  • 20
  • 80
  • 102
-5

Yes, Python is one of the slowest practical languages you'll encounter. While loops are marginally faster than for i in xrange(), but ultimately Python will always be much, much slower than anything else.

Python has its place: Prototyping theory and ideas, or in any situation where the ability to produce code fast is more important than the code's performance.

Python is a scripting language. Not a programming language.

Squirrelsama
  • 5,000
  • 4
  • 25
  • 38
  • 3
    **Python is a programming language.** But it's not focused on number crunching speed, yes. Also, Ruby and many other dynamic languages won't perform much different from Python. Lua wil propably will be faster though. –  Nov 12 '10 at 23:18
  • 1
    -1 for "Python is a scripting languge. Not a programming language". It's not a number crunching language unless you use numpy. It's a far more powerful and expressive _programming_ language than java. – aaronasterling Nov 12 '10 at 23:19
  • I predict at least one Python groupie will hate on me, and probably -1 me for saying Python isn't a programming language. Generally, you CAN do anything you want in Python, but everything can always be done better in a real language. – Squirrelsama Nov 12 '10 at 23:20
  • 2
    Thats funny, I predicted that at least one java clone would claim that python isn't a real programming language in response to this thread. The thing about java is that everything can always be done better in real language. Sometimes, you just need the speed and, in that case, work in java. For example, java __doesn't even have closures__. How do you call that a "real" language when it doesn't even have proper lexical scoping? – aaronasterling Nov 12 '10 at 23:22
  • 1
    Well, if we were going for raw performance, we wouldn't use Java, either. We'd use a fully compiled language, like C. Horses for courses. For many programs, calculating prime numbers is irrelevant. That said, with a few tweaks, my answer above did the test case in under a second. – Thomas K Nov 12 '10 at 23:30
  • Sorry, I meant "...doesn't even have proper first class functions." You can have lexical scoping without closure but not lexical closure __and__ first class functions which every real language has had for the last ~30 years. – aaronasterling Nov 12 '10 at 23:32
  • 2
    Guys, guys. Cut the language pissing match. I commented because the notion that "X is not a programming language" because it is dynamic/interpreted/whatever is absurd (I dislike many "design" aspects of PHP, but it's still a programming language). Also, with Python being a general purpose language, a skilled programmer can solve most problems quite well in it, save system programming and number crunching. Period. Also - as much as I dislike Java for being limited to a subset of OOP - you sure can get quite a lot of stuff done in it. –  Nov 12 '10 at 23:33
  • of course Java has closures. They're called "inner classes" though. – Allen Nov 12 '10 at 23:37
  • @Allen. An "inner class" is a very limited form of closure in that it only applies to classes and so I have to stick my function on a class if I want the effect. Oh. Wait a minute. Java doesn't have functions. I have to define everything on a class. How is this more of a real programming language than python? – aaronasterling Nov 12 '10 at 23:46
  • @aaronasterling - It sucks that I couldn't get back to this thread until now. Here's a piece: I love Python and I hate Java with a passion (and would never defend it). It should not exist in today's world. Like C++, it is a limping dinosaur surrounded by languages that do everything better or more expressively. Python is very powerful, but it is so regularly misused in spite of its major flaws because of its major strengths. – Squirrelsama Nov 14 '10 at 07:36