Is FOOP slow or is it just me?

Started by cormullion, May 15, 2011, 02:31:02 AM

Previous topic - Next topic

cormullion

Running this object-oriented FOOP code:


(new Class 'C)
(context C)
(define (C:C) (list (self 0)))
(define (C:hw) (println "hello world"))

(define (C:do_it)
 (let ((i 0))
   (while (< (++ i) 1000000)
       (hw))))

(context MAIN)
(new C 'my-c)
(my-c:do_it)
(exit)


It's trying to be an exact copy of this Python:


class c:
def hw(self):
print 'Hello World!'

def do_it(self):
i = 0
while i < 1000000:
i = i + 1
self.hw()

inst = c()
inst.do_it()


But the newLISP seems very sluggish. Is it some FOOP-related issue that prevents this from being efficient code? (There are obviously many easier ways to accomplish the same thing without FOOP.)

Lutz

#1
The code is mainly measuring IO speed from 'println' and a badly (Python'ish) written loop in newLISP. Nothing specific to FOOP is happening here.

cormullion

#2
OK, I agree it's not the best way to do this particular task. But still, I don't think a basic while loop calling a function that prints is so unidiomatic newLISP that we can accept that it's over 10 times slower than Python. 10 times slower than Perl, OK, we know Perl's pretty quick, but not Python... :)



I was convinced that something must be wrong, but if you say not, that's good to know.



Still, it's only 5 times slower than Ruby! :)

Lutz

#3

> (time (while (< (++ i) 1000000)))
234.431
> (time (while (< $idx 1000000)))
131.081
> (time (dotimes (x 1000000)))
44.608
> (time (for (i 1 1000000)))
44.166


For benchmarking Perl, Python and newLISP see here:



http://www.newlisp.org/benchmarks/">http://www.newlisp.org/benchmarks/

m i c h a e l

#4
Hi cormullion!



Your constructor:


(define (C:C) (list (self 0)))


... is referencing an object through self, but a constructor doesn't have an object to reference since it's in the process of creating one. Your code doesn't actually create any objects because your constructor wasn't being called. This line:


(new C 'my-c)


... is making a new class called my-c and this:


(my-c:do_it)


... is more like calling a class method (a method belonging to a class, not an object).



To use objects, you could rewrite the code something like this:


(context (new Class 'C))
  ;; we'll use the default constructor
  (define (hw) (println "hello world"))
  (define (do-it) (dotimes (i 1000000) (:hw (self))))
(context MAIN)

(setq my-c (C)) ; we're calling the constructor here and creating an instance
(:do-it my-c)   ; sending the message 'do-it' to the object 'my-c'
(exit)


m i c h a e l

cormullion

#5
Thanks michael - you bring light into the FOOP darkness.



Lutz: to play devil's advocate for a moment ... surely it's also common to be testing conditions and incrementing counters in loops, rather than knowing in advance exactly how many iterations are required? Also to be fair I think Python also has for iterators.



I was assuming that the order of magnitude difference I was seeing was caused by some error on my part... The result is still surprising to me, but we'll have to accept it...

Lutz

#6
When correctly benchmarking 'while' and 'for' in Python and newLISP, I cannot see much of a difference.



# while.py
i = 0;
while i < 10000000:
    i = i + 1

# while.lsp
(setq i 0)
(while (< i 10000000)
(++ i 1))

(exit)


~/shootout> time python while.py



real   0m1.946s

user   0m1.924s

sys   0m0.019s

~/shootout> time newlisp while.lsp



real   0m2.591s

user   0m2.573s

sys   0m0.005s



Python about 33% faster using 'while'





# for.py
i = 0;
for k in xrange(1, 10000000):
    i = i + 1

# for.lsp
(set 'i 0)
(for (k 1 10000000)
(++ i 1))

(exit)


~/shootout> time python for.py



real   0m2.717s

user   0m2.696s

sys   0m0.019s

~/shootout> time newlisp for.lsp



real   0m1.712s

user   0m1.694s

sys   0m0.004s



newLISP 58% faster using 'for'



The difference you saw in your code was due to I/O speed difference for a simple 'print' statement with a short string. If you compare real world code examples, I/O speed between Python, Perl and newLISP, the difference is much smaller, Perl most of the time beeing the fastest, then Python then newLISP.



Ps: note that I am using 10,000,000 iterations instead of 1,000,000 to minimize the effect of newLISP's much faster startup time.

cormullion

#7
OK, that looks good. But then, if


Quotea simple 'print' statement with a short string


can give such a difference in performance, surely there must be some problems there, if it's not related to while loops... Perhaps there are some optimizations to be made in what must be quite old code by now?!



Thanks for your comments anyway - at least we know what the explanation is.