admin管理员组

文章数量:1420159

Out of curiosity I wrote some trivial benchmarks paring the performance of golang maps to JavaScript (v8/node.js) objects used as maps and am surprised at their relative performance. JavaScript objects appear to perform roughly twice as fast as go maps (even including some minor performance edges for go)!

Here is the go implementation:

// map.go
package main
import "fmt"
import "time"
func elapsedMillis(t0, t1 time.Time) float64 {
  n0, n1 := float64(t0.UnixNano()), float64(t1.UnixNano())
  return (n1 - n0) / 1e6
}
func main() {
  m := make(map[int]int, 1000000)
  t0 := time.Now()
  for i := 0; i < 1000000; i++ {
    m[i] = i     // Put.
    _ = m[i] + 1 // Get, use, discard.
  }
  t1 := time.Now()
  fmt.Printf("go: %fms\n", elapsedMillis(t0, t1))
}

And here is the JavaScript:

#!/usr/bin/env node
// map.js
function elapsedMillis(hrtime0, hrtime1) {
  var n0 = hrtime0[0] * 1e9 + hrtime0[1];
  var n1 = hrtime1[0] * 1e9 + hrtime1[1];
  return (n1 - n0) / 1e6;
}
var m = {};
var t0 = process.hrtime();
for (var i=0; i<1000000; i++) {
  m[i] = i; // Put.
  var _ = m[i] + 1; // Get, use, discard.
}
var t1 = process.hrtime();
console.log('js: ' + elapsedMillis(t0, t1) + 'ms');

Note that the go implementation has a couple of minor potential performance edges in that:

  1. Go is mapping integers to integers directly, whereas JavaScript will convert the integer keys to string property names.

  2. Go makes its map with initial capacity equal to the benchmark size, whereas JavaScript is growing from its default capacity).

However, despite the potential performance benefits listed above the go map usage seems to perform at about half the rate of the JavaScript object map! For example (representative):

go: 128.318976ms
js: 48.18517ms

Am I doing something obviously wrong with go maps or somehow paring apples to oranges?

I would have expected go maps to perform at least as well - if not better than JavaScript objects as maps. Is this just a sign of go's immaturity (1.4 on darwin/amd64) or does it represent some fundamental difference between the two language data structures that I'm missing?

[Update]

Note that if you explicitly use string keys (e.g. via s := strconv.Itoa(i) and var s = ''+i in Go and JavaScript, respectively) then their performance is roughly equivalent.

My guess is that the very high performance from v8 is related to a specific optimization in that runtime for objects whose keys are consecutive integers (e.g. by substituting an array implementation instead of a hashtable).

I'm voting to close since there is likely nothing to see here...

Out of curiosity I wrote some trivial benchmarks paring the performance of golang maps to JavaScript (v8/node.js) objects used as maps and am surprised at their relative performance. JavaScript objects appear to perform roughly twice as fast as go maps (even including some minor performance edges for go)!

Here is the go implementation:

// map.go
package main
import "fmt"
import "time"
func elapsedMillis(t0, t1 time.Time) float64 {
  n0, n1 := float64(t0.UnixNano()), float64(t1.UnixNano())
  return (n1 - n0) / 1e6
}
func main() {
  m := make(map[int]int, 1000000)
  t0 := time.Now()
  for i := 0; i < 1000000; i++ {
    m[i] = i     // Put.
    _ = m[i] + 1 // Get, use, discard.
  }
  t1 := time.Now()
  fmt.Printf("go: %fms\n", elapsedMillis(t0, t1))
}

And here is the JavaScript:

#!/usr/bin/env node
// map.js
function elapsedMillis(hrtime0, hrtime1) {
  var n0 = hrtime0[0] * 1e9 + hrtime0[1];
  var n1 = hrtime1[0] * 1e9 + hrtime1[1];
  return (n1 - n0) / 1e6;
}
var m = {};
var t0 = process.hrtime();
for (var i=0; i<1000000; i++) {
  m[i] = i; // Put.
  var _ = m[i] + 1; // Get, use, discard.
}
var t1 = process.hrtime();
console.log('js: ' + elapsedMillis(t0, t1) + 'ms');

Note that the go implementation has a couple of minor potential performance edges in that:

  1. Go is mapping integers to integers directly, whereas JavaScript will convert the integer keys to string property names.

  2. Go makes its map with initial capacity equal to the benchmark size, whereas JavaScript is growing from its default capacity).

However, despite the potential performance benefits listed above the go map usage seems to perform at about half the rate of the JavaScript object map! For example (representative):

go: 128.318976ms
js: 48.18517ms

Am I doing something obviously wrong with go maps or somehow paring apples to oranges?

I would have expected go maps to perform at least as well - if not better than JavaScript objects as maps. Is this just a sign of go's immaturity (1.4 on darwin/amd64) or does it represent some fundamental difference between the two language data structures that I'm missing?

[Update]

Note that if you explicitly use string keys (e.g. via s := strconv.Itoa(i) and var s = ''+i in Go and JavaScript, respectively) then their performance is roughly equivalent.

My guess is that the very high performance from v8 is related to a specific optimization in that runtime for objects whose keys are consecutive integers (e.g. by substituting an array implementation instead of a hashtable).

I'm voting to close since there is likely nothing to see here...

Share Improve this question edited Apr 21, 2015 at 15:45 maerics asked Apr 20, 2015 at 21:44 maericsmaerics 157k47 gold badges277 silver badges299 bronze badges 8
  • I'm not sure Go is not hashing the numbers, and I'm not sure javascript does the same. A map of consecutive integers between 0...1M is basically an array. Initializing m as an array of 1M ints, reduces the execution time to 2ms. Now do this benchmarks with strings and it's a real parison. I won't be surprised if v8's speed would be faster then as well. – Not_a_Golfer Commented Apr 20, 2015 at 21:56
  • @Not_a_Golfer: ya I was kinda guessing that go might use int keys directly instead of hashing them; I suppose I could read the hashtable implementation to find out. Funny enough - if I use string keys in both languages (using s:=strconv.Itoa(i) and var s=''+i) then they perform roughly equally; though using a numeric key in js is still much faster. Many mysteries afoot here... – maerics Commented Apr 20, 2015 at 22:01
  • "JavaScript will convert the integer keys to string property names." - no it doesn't. Even if they are equivalent, that's not what the engine does. It soon realizes that you are abusing an object for an array, and will use an array underneath. – Bergi Commented Apr 20, 2015 at 22:06
  • @Bergi: yes, that would explain a lot (see my ment to @Not_a_Golfer). It looks like this silly benchmark might be flexing a well optimized part of the v8 engine... – maerics Commented Apr 20, 2015 at 22:07
  • 2 BTW, in Go it's easier and better to use the testing package for benchmarks. – Dave C Commented Apr 21, 2015 at 16:13
 |  Show 3 more ments

1 Answer 1

Reset to default 4

Your benchmark is synthetic a bit, just like any benchmarks are. Just for curious try

for i := 0; i < 1000000; i += 9 {

in Go implementation. You may be surprised.

本文标签: Golang vs JavaScript (v8nodejs) map performanceStack Overflow