Skip to content

Instantly share code, notes, and snippets.

@snodgrass23
Last active December 15, 2015 00:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save snodgrass23/5173452 to your computer and use it in GitHub Desktop.
Save snodgrass23/5173452 to your computer and use it in GitHub Desktop.
speed test comparisons for Node and Ruby
function getRands(i) {
var r = [];
while (i--) {
r[i] = Math.floor(Math.random() * 5);
}
return r;
}
function findMatchScore(profileDimensions, verseDimensions) {
var i = profileDimensions.length;
var diff, total = 0;
while (i--) {
diff = (profileDimensions[i] - verseDimensions[i]);
total += diff * diff;
}
return total;
}
function randomMatch(profile) {
var verse = getRands(25);
var score = findMatchScore(profile, verse);
//console.log('score:', score);
}
function timeIt(i) {
var profile = getRands(25);
console.log('Matching profile:', profile);
while (i--) {
randomMatch(profile);
}
}
function testIt() {
var a = [0, 0, 0];
var b = [0, 0, 0];
var c = [4, 4, 4];
console.log('should be 0:', findMatchScore(a, b));
console.log('should be 48:', findMatchScore(a, c));
}
timeIt(1000000);
def rand_n(n, max)
randoms = []
n.times do
randoms << rand(max)
end
randoms
end
def diff_score(a, b)
(a - b) * (a - b)
end
def match_score(a, b)
score = 0
a.length.times do |i|
score += diff_score a[i], b[i]
end
return score
end
user = rand_n(25, 4)
1000000.times do
message = rand_n(25, 4)
match_score user, message
end
@snodgrass23
Copy link
Author

$ time ruby speed_test.rb

real       13.177s
user      12.992s
sys        0.173s

@snodgrass23
Copy link
Author

time node node_speed.js

real        0.533s
user       0.518s
sys        0.016s

@snodgrass23
Copy link
Author

Can anyone explain why the Ruby example is so much slower than the Node example? I'll need to use a similar algorithm in a Rails project soon.

@foundrium
Copy link

I am not a Ruby expert but I have a couple of suggestions.

In the Ruby Array you can just do array differences. So for instance instead of

def match_score(a, b)
  score = 0
  a.length.times do |i|
    score += diff_score a[i], b[i]
  end
  return score
end

you could do

def match_score(a, b)
  score = 0
  c = a - b
  c.inject{|sum,x| score + x }
  return score
end

This only shaves off a few seconds though.

I think the bigger issue is the test itself. I believe v8 engine has been highly optimized for mathematics ops (more like a hunch here than any real evidence). So in your test here you are calling rand and Math.random() 25 million times. That is a lot of calls to function that could be implemented very differently in the two interpreters.

I might change the test instead to more closely mimic your end goal. Perhaps generate a sample text files with 1000000 lines and read that entire file in at the beginning or read it in chunks. i.e. essentially trying to easily mimic the database and reading aspect rather than generating random seed data as part of the algorithm.

This might at least give a better idea of what needs to truly be optimized in the Ruby code to make it as efficient as possible.

If is still too slow (which could happen - Ruby is certainly not known for its speed) than perhaps just setting up a node process who purpose is to handle it would be the way to go.

Hope this helps

@jroes
Copy link

jroes commented Mar 17, 2013

aint nobody got time fo dat

@jroes
Copy link

jroes commented Jul 9, 2013

○ → ruby-prof revised.rb
Thread ID: 70306702427860
Fiber ID: 70306722876660
Total: 211.227834
Sort by: self_time

 %self      total      self      wait     child     calls  name
 28.40     76.478    59.991     0.000    16.487 25000000   Object#diff_score
 17.98     54.562    37.980     0.000    16.582 25000025   Kernel#rand
 16.68     89.800    35.238     0.000    54.562  1000001   Array#initialize
  7.85     16.582    16.582     0.000     0.000 25000025   Kernel#respond_to_missing?
  7.81     16.487    16.487     0.000     0.000 25000000   Fixnum#**
  1.49    115.298     3.141     0.000   112.157  1000000   Object#match_score
  1.25    211.228     2.647     0.000   208.580  1000001  *Integer#times
  0.90     93.282     1.897     0.000    91.385  1000001   Object#rand_n
  0.75     91.385     1.585     0.000    89.800  1000001   Class#new
  0.00    211.228     0.000     0.000   211.228        2   Global#[No method]
  0.00      0.000     0.000     0.000     0.000        2   IO#set_encoding
  0.00      0.000     0.000     0.000     0.000        3   Module#method_added

* indicates recursively called methods

@jroes
Copy link

jroes commented Jul 10, 2013

Just for fun, I wrote a go implementation:

package main

import (
        "math/rand"
)

func main() {
  user := generateRand(25, 4)

  for i := 0; i < 1000000; i++ {
    numbers := generateRand(25, 4)
    matchScore(user, numbers)
  }
}

func generateRand(n int, max int) []int {
  numbers := make([]int, n)
  for i := 0; i < n; i++ {
    numbers[i] = rand.Intn(max)
  }
  return numbers
}

func matchScore(user []int, numbers []int) int {
  score := 0
  for i, _ := range user {
    score1, score2 := user[i], numbers[i]
    score += (score1 - score2) * (score1 - score2)
  }
  return score
}
○ → time go run revised.go

real    0m1.765s
user    0m1.723s
sys 0m0.040s

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment