Arc Forumnew | comments | leaders | submitlogin
1 point by Pauan 4545 days ago | link | parent

Update: I just tested this code:

  (time (repeat 1000000 (apply (fn (a) a) (list 1))))
And the times:

  Nu time:      2875 msec.
  ar time:      1776 msec.
  Arc 3.1 time: 1725 msec.
So it seems the problem is with Nu's apply, not with Racket's apply. That's good, since that means I should be able to fix the problem.


2 points by Pauan 4544 days ago | link

I haven't fixed `apply` yet, but I did put in a workaround. Using Racket's `apply`, Nu is actually faster than Arc 3.1 and ar!

   (timeit (let a 1 a))
   
  ar      time: 8.101  gc: 0.268  mem:   973.008
  Nu      time: 6.923  gc: 0.0    mem:    88.736
  Arc 3.1 time: 4.77   gc: 0.28   mem: -3305.9


   (timeit (let a (list 1 2) (car a)))

  Arc 3.1 time: 17.3    gc: 0.86   mem: -7,759.58
  ar      time: 10.303  gc: 0.552  mem: 1258.64
  Nu      time:  8.158  gc: 0.196  mem: -515.648


   (timeit (let (a b) (list 1 2) a))

  Arc 3.1 time: 17.47   gc: 1.0    mem:  -6997.07
  ar      time: 13.166  gc: 0.696  mem: -16510.112
  Nu      time: 12.102  gc: 0.512  mem: -10028.488
  
So, it seems my idea of applying nested functions to implement destructuring is good in essentially every way: faster, shorter, and easier to implement.

Interestingly, judging by the data above, it would seem Arc 3.1 is very slow at creating lists, probably because `list` is implemented in arc.arc, whereas ar and Nu provide faster implementations.

---

Now let's test optional args:

   (timeit ((fn (a) a) 1))
  
  ar      time: 7.534  gc: 0.352  mem:   866.232
  Nu      time: 6.976  gc: 0.0    mem:    88.368
  Arc 3.1 time: 4.78   gc: 0.28   mem: -3295.58
  
  
   (timeit ((fn (a (o b)) a) 1))
  
  ar      time: 14.493  gc: 0.464  mem:  1639.648
  Nu      time:  7.903  gc: 0.248  mem: -1664.792
  Arc 3.1 time:  5.84   gc: 0.36   mem: -2097.19


   Overhead
  ar      - 6.959
  Arc 3.1 - 1.06
  Nu      - 0.927
  
As you can see, in Nu and Arc 3.1, there's very little overhead from optional args, but in ar, optional args are quite costly.

-----

1 point by Pauan 4544 days ago | link

Update: I didn't want to be unfair to Arc 3.1 because of its slow implementation of `list`, so I redid the tests using `quote` instead:

   (timeit (let a '(1 2) (car a)))
  
  Nu      time: 10.628  gc: 0.196  mem: -1747.08
  ar      time: 8.529   gc: 0.252  mem:   967.432
  Arc 3.1 time: 5.26    gc: 0.34   mem:  4952.98
  
  
   (timeit (let (a b) '(1 2) a))
   
  Nu      time: 14.066   gc: 0.52   mem:    90.504
  ar      time: 13.305   gc: 0.376  mem: -9236.904
  Arc 3.1 time: 6.79     gc: 0.35   mem: -2,093.93


   Overhead
  ar      - 4.776
  Nu      - 3.438
  Arc 3.1 - 1.53
  
As expected, Arc 3.1 is miles ahead of both ar and Nu. Interestingly, Nu is now listed as slower than ar... it would appear that either Nu has a faster implementation of `list`, a slower implementation of `quote`, or possibly both. In any case, this demonstrates that applying nested functions should be approximately the same as complex fns in terms of speed.

One thing I noticed is that Nu has drastically greater overhead than Arc 3.1, but less than ar.

-----

1 point by Pauan 4543 days ago | link

It seems the problem was that quote was slow in Nu. I've fixed that, so here's the new times:

   (timeit (let a '(1 2) (car a)))
  
  ar      time: 8.613  gc: 0.308  mem:  460.696
  Nu      time: 7.671  gc: 0.0    mem:   88.976
  Arc 3.1 time: 5.33   gc: 0.35   mem: 5050.25
  
  
   (timeit (let (a b) '(1 2) a))
   
  ar      time: 12.111  gc: 0.436  mem: -19278.128
  Nu      time: 11.438  gc: 0.324  mem:   1435.016  (apply fn)
  Nu      time: 8.96    gc: 0.0    mem:    125.352  (Racket let*)
  Arc 3.1 time: 7.0     gc: 0.35   mem:  -2124.82


   Overhead
  ar      - 3.498
  Arc 3.1 - 1.67
  Nu      - 1.289
Nu now has the lowest overhead out of the three...! Also note that Nu does not spend any time in garbage collection, unlike ar and Arc 3.1.

This seems to be a common trend: Nu either spending no time in garbage collection, or less time than ar and Arc 3.1. Not sure how important that is compared to raw speed, but it's nice.

Unfortunately, this also demonstrates that applying nested functions is slower than using a Racket let*. So the reason Nu won the speed contest earlier wasn't because of my destructuring idea: Nu was just plain faster than ar in general.

-----

2 points by Pauan 4545 days ago | link

And since I'm in a timing mood, here's the times for optional args:

  (time (repeat 1000000 ((fn (a (o b 3)) (list a b)) 1 2)))

  Arc 3.1 time: 1828 msec.
  ar time:      1814 msec.
  Nu time:      1554 msec.
So, it does make a difference that Nu uses plain lambdas, rather than complex fns! Now I just need to get apply to be faster.

---

On a related note: racket-set! is slow. Using racket-let or Arc's let is faster, by a fairly significant amount. So I'll be changing my compiler so it doesn't use mutation.

-----