-
-
Save snodgrass23/5173452 to your computer and use it in GitHub Desktop.
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 |
time node node_speed.js
real 0.533s
user 0.518s
sys 0.016s
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.
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
○ → 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
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
$ time ruby speed_test.rb