Return Styles: Pseud0ch, Terminal, Valhalla, NES, Geocities, Blue Moon. Entire thread

FIOC is SAF (Slow as Fuck)

Name: Anonymous 2008-10-13 20:16

While reading my SICP, I found a neat fibonacci function that has log n operations. I looks something like this.


(define (fib n)
  (fib-iter 1 0 0 1 n))

(define (fib-iter a b p q count)
  (cond ((= count 0) b)
        ((even? count)
         (fib-iter a
                   b
                   (+ (* p p) (* q q))     ; compute p'
                   (+ (* q q) (* 2 p q))   ; compute q'
                   (/ count 2)))
        (else (fib-iter (+ (* b q) (* a q) (* a p))
                        (+ (* b p) (* a q))
                        p
                        q
                        (- count 1)))))


So I implemented it in Haskell, just for fun.


fib n = fib' 1 0 0 1 n
  where fib' a b p q 0 = b
        fib' a b p q n | even n =
          fib' a b (p * p + q * q) (q * q + 2 * p * q) (n `div` 2)
        fib' a b p q n =
          fib' (b * q + a * q + a * p) (b * p + a * q) p q (n - 1)


To calculate and print fib(1000000) and fib(10000000) (i.e. the millionth and ten-millionth fibonacci numbers) takes about 3.26 seconds with the Haskell version (when compiled with GHC), and 43.24 seconds with the Scheme version (when interpreted by plt-r5rs, cause I can't figure out how to get it to be r6rs compliant, nor can I figure out how to make the compiler accept r5rs code :( )

Just to have some more fun, I also implemented it iteratively in Python.


def fib(n):
    a = 1
    b = 0
    p = 0
    q = 1

    while n > 0:
        if n % 2 == 0:
            new_p = p * p + q * q
            new_q = q * q + 2 * p * q
            n = n / 2
            p = new_p
            q = new_q
        else:
            new_a = b * q + a * q + a * p
            new_b = b * p + a * q
            n = n - 1
            a = new_a
            b = new_b

    return b


This takes CPython a whopping 1926.49 seconds to calculate the same numbers. Holy fuck, /prog/, maybe you're right about FIOC after all.

Here are the other implementations I've written and their times.

Ruby (995.35 seconds)

def fib(n)
    a = 1
    b = 0
    p = 0
    q = 1

    while n > 0
        if n % 2 == 0:
            new_p = p * p + q * q
            new_q = q * q + 2 * p * q
            n = n / 2
            p = new_p
            q = new_q
        else
            new_a = b * q + a * q + a * p
            new_b = b * p + a * q
            n = n - 1
            a = new_a
            b = new_b
        end
    end
    b
end


C using GMP (3.24 seconds)

void fib(unsigned long n)
{
    mpz_t a, b, p, q, tmp1, tmp2, tmp3;

    mpz_init(a);
    mpz_init(b);
    mpz_init(p);
    mpz_init(q);
    mpz_init(tmp1);
    mpz_init(tmp2);
    mpz_init(tmp3);

    mpz_set_ui(a, 1);
    mpz_set_ui(b, 0);
    mpz_set_ui(p, 0);
    mpz_set_ui(q, 1);

    while(n > 0)
    {
        if(n % 2 == 0)
        {
            // p * p + q * q
            mpz_mul(tmp1, p, p);
            mpz_mul(tmp2, q, q);
            mpz_add(tmp3, tmp1, tmp2);

            // q * q + 2 * p * q;
            mpz_mul_ui(tmp1, p, 2);
            mpz_add(tmp1, tmp1, q);
            mpz_mul(tmp1, tmp1, q);

            mpz_set(p, tmp3);
            mpz_set(q, tmp1);
            n >>= 1;
        }
        else
        {
            // b * q + a * q + a * p;
            mpz_mul(tmp1, b, q);
            mpz_mul(tmp2, a, q);
            mpz_mul(tmp3, a, p);
            mpz_add(tmp3, tmp3, tmp2);
            mpz_add(tmp3, tmp3, tmp1);

            // b * p + a * q;
            mpz_mul(tmp1, b, p);
            mpz_mul(b, a, q);
            mpz_add(b, tmp1, b);

            mpz_set(a, tmp3);
            n--;
        }
    }

    gmp_printf("%Zd\n", b);

    mpz_clear(a);
    mpz_clear(b);
    mpz_clear(p);
    mpz_clear(q);
    mpz_clear(tmp1);
    mpz_clear(tmp2);
    mpz_clear(tmp3);
}

Name: Anonymous 2008-10-13 20:55

Those GHC writers are truly epic.

Name: Anonymous 2008-10-13 21:00

Fibonacci can be done in O(1)
:O

Name: Anonymous 2008-10-13 21:36

I don't want to call you out as a troll, but perhaps your benchmarks are slightly off?

root@suigintou> nice -n -5 time python fib.py
       60.43 real        60.20 user         0.01 sys
root@suigintou> nice -n -5 time ruby fib.rb
      176.65 real       175.92 user         0.03 sys
root@suigintou> python --version
Python 2.5.2
root@suigintou> ruby --version
ruby 1.8.6 (2008-08-11 patchlevel 287) [amd64-freebsd7]

Name: Anonymous 2008-10-13 21:40

Haskell has been gaining some support in here, at least from me, Taro, and you (I assume you are the Sussman?). I used to be a LISPfag but recently I've been quite enjoying the performance and beautiful tools that Haskell offers. I orgasmed the first time I wrote code with list comprehension.

The LISP performance still isn't bad at all if it's really interpreted. I'd be curious to see a compiled version (Stalin/etc), although I won't be assed to do it.

Name: Anonymous 2008-10-13 21:53

>>4
I didn't adjust nice levels at all. I'll try that later and see what happens.

Are you sure you're calculating the millionth (1000000) and ten-millionth (10000000) fibonacci numbers?

Name: Anonymous 2008-10-13 22:03

>>3
no, it can't. it can be calculated in O(log n) at best.

Name: Anonymous 2008-10-13 22:04

>>6
For both of them, I'm calculating the ten-millionth fibonacci numbers --

fib( 10000000 )

Realistically, I think the benchmark is more a test of the bigint implementation of the language (but then again, there really isn't such thing as a generalized benchmark anyway). I tested on an amd64 machine -- what platform are you on? It might be a platform-specific implementation issue with CPython or something.

The excessively long Ruby time is pretty strange too, but I'm not at all familiar with Ruby's internals.

Name: Anonymous 2008-10-13 22:11

>>7
I don't understand

Name: Anonymous 2008-10-13 22:25

>>7
Memoization

Name: Anonymous 2008-10-13 22:28

Here's my Python script verbatim.


def fib(n):
    a = 1
    b = 0
    p = 0
    q = 1

    while n > 0:
        if n % 2 == 0:
            new_p = p * p + q * q
            new_q = q * q + 2 * p * q
            n = n / 2
            p = new_p
            q = new_q
        else:
            new_a = b * q + a * q + a * p
            new_b = b * p + a * q
            n = n - 1
            a = new_a
            b = new_b

    return b

print fib(1000000)
print fib(10000000)

Name: Anonymous 2008-10-13 22:50

>>11
Ah, your problem there is that Python's (and Ruby's, for that matter) str(bigint) conversion is slow as fuck and is causing the bottleneck. Your fibonacci algorithm may be O(log(N)), but binary to decimal conversion is a O(n2)1 operation which takes up the majority of the computation time.

That said, the other languages do the same conversion much faster. I suspect it's a performance bug of some sort (or just an accepted limitation) in the Python and Ruby libraries.

                                 
References: [1] http://mail.python.org/pipermail/python-list/2004-July/270235.html

Name: Anonymous 2008-10-13 22:50

FIOC
Thread is over, you may go to sleep now everyone.

Name: Anonymous 2008-10-13 23:07

>>9,10
exponentiation requires at least O(log n). even with memoization it's still O(log n) the first time for each number.

Name: Anonymous 2008-10-13 23:24

>>12
Ha, you're right. Maybe CPython should change it's bigint to use GMP, since that seems to be fast.

Name: Anonymous 2008-10-14 0:14

>>15
or maybe people should let cpython die and use one of the better implementations of python

Name: Anonymous 2008-10-14 0:20

>>16
We'll all switch to PyPy when it becomes reasonable, but until it outperforms CPython we'll stick with CPython. Let's not even talk about any of the other implementations. They're all either incomplete, utter shit, or both.

Name: Anonymous 2008-10-14 2:09

Name: Anonymous 2008-10-14 6:08

>>18
Sorry, I like using stacks in my programs.  And I cannot see how that would work anyway -- how would you call library functions without the stack?

Name: Anonymous 2008-10-14 6:13

>>19
"Stackless Python, or Stackless, is a Python programming language interpreter, so named because it avoids depending on the C call stack for its own stack."

Name: Anonymous 2008-10-14 6:24

no, it can't. it can be calculated in O(log n) at best.
F(n) = φn−(1−φ)n
          √5

Name: Anonymous 2008-10-14 6:25

>>20
It's shit.

Name: Anonymous 2008-10-14 6:29

>>21
see >>14

Name: Anonymous 2008-10-14 6:31

>>23
See >>21 for Ο(1).

Name: Anonymous 2008-10-14 6:31

>>22
It's better than CPython.

Name: Anonymous 2008-10-14 6:34

>>25
Stackless is basically CPython, and IronPython is indeed shit (as in slower than CPython). I can find references for the later fact if you really want to troll me good.

Name: Anonymous 2008-10-14 6:40

>>22
CURRY BUTT xDDDDD~~~

Name: Anonymous 2008-10-14 7:01

>>24
[b]n[b]
http://en.wikipedia.org/wiki/Exponentiation#Efficiently_computing_a_power
In general, the number of multiplication operations required to compute an can be reduced to Θ(log n) by using exponentiation by squaring or (more generally) addition-chain exponentiation.

Name: Anonymous 2008-10-14 7:18

>>28
BBCODE FAILURE renders your argument invalid.

Name: Anonymous 2008-10-14 7:24

>>29
MATHS FAILURE renders your argument invalid.

Name: Anonymous 2008-10-14 8:14

>>28
pow is O(1), your point is moot.

Name: Anonymous 2008-10-14 9:14

>>31
pow is O(log n), your point is moot.

Name: Anonymous 2008-10-14 10:34

>>32
pow is O(log log n), your point is snacks.

Name: Anonymous 2008-10-14 10:40

Too bad Ruby Python is slow as fuck.

Name: Anonymous 2008-10-14 10:42

>>33
your point is squeeks and pow is O(log log log n).

Name: Anonymous 2008-10-14 10:46

>>35
Your point is reeses and I had it for breakfast.

Name: Anonymous 2008-10-14 11:33

>>36
pow is O(log log log log n) and you're back to /b/, please

Name: Anonymous 2008-10-14 15:58

pow is O(logk n) where k=0,1,2,...

Name: Anonymous 2008-10-14 16:01

What the devil does FIOC mean anyway.

The wonderful thing about the Haskell version is that it looks human-readable, unlike the C version, yet runs at about the same speed. Score! ... not that I dislike C, far from it.

Name: Anonymous 2008-10-14 16:25

>>21
just an approximation.

Newer Posts
Don't change these.
Name: Email:
Entire Thread Thread List