performance - 为什么我在使用范围时看到某些尺寸的 map 速度变慢?

标签 performance map go

在我的计算机上,当我访问特定大小的 map 时,我看到每秒读取量下降,但它不会以线性方式降低。事实上,性能会立即下降,然后随着大小的增加缓慢恢复:

$ go run map.go 425984 1 425985
    273578 wps ::   18488800 rps
    227909 wps ::    1790311 rps

$ go run map.go 400000 10000 500000
    271355 wps ::   18060069 rps
    254804 wps ::   18404288 rps
    267067 wps ::   18673778 rps
    216442 wps ::    1984859 rps
    246724 wps ::    2461281 rps
    282316 wps ::    3634125 rps
    216615 wps ::    4989007 rps
    276769 wps ::    6972233 rps
    212019 wps ::    9756720 rps
    286027 wps ::   14488593 rps
    227073 wps ::   17309822 rps

我预计写入偶尔会变慢(因为调整了底层数据结构的大小),但读取对大小敏感(并且是一个数量级)让我感到惊讶。

这是我用来测试的代码:

package main

import (
    "bytes"
    "fmt"
    "math/rand"
    "os"
    "strconv"
    "time"
)

func main() {
    start, _ := strconv.ParseInt(os.Args[1], 10, 64)
    step, _ := strconv.ParseInt(os.Args[2], 10, 64)
    end, _ := strconv.ParseInt(os.Args[3], 10, 64)
    for n := start; n <= end; n += step {
        runNTimes(n)
    }
}

func randomString() string {
    var b bytes.Buffer

    for i := 0; i < 16; i++ {
        b.WriteByte(byte(0x61 + rand.Intn(26)))
    }

    return b.String()
}

func perSecond(end time.Time, start time.Time, n int64) float64 {
    return float64(n) / end.Sub(start).Seconds()
}

func runNTimes(n int64) {
    m := make(map[string]int64)

    startAdd := time.Now()
    for i := int64(0); i < n; i++ {
        m[randomString()]++
    }
    endAdd := time.Now()

    totalInMap := int64(0)
    startRead := time.Now()
    for _, v := range m {
        //get around unused variable error,
        //v should always be > 0
        if v != 0 {
            totalInMap++
        } 
    }
    endRead := time.Now()

    fmt.Printf("%10.0f wps :: %10.0f rps\n",
        perSecond(endAdd, startAdd, n),
        perSecond(endRead, startRead, totalInMap),
    )
}

最佳答案

您的代码本身不衡量 map 性能。您的代码测量执行随机数生成(不保证是恒定时间操作)、测距 map (不保证是恒定时间操作并且不保证以与普通 map 访问性能相关的任何可预测方式)和也许它甚至可以测量干扰“停止世界”垃圾收集。

  • 不要编写自己的工作台功能,使用什么package "testing"优惠,就好多了。例如,它从不依赖样本大小 == 1(就像您的代码错误地那样)。
  • 此外,在测量时间之外生成所有测试 key 。
  • 然后,为了尽量减少 GC 影响,perform runtime.GC() .
  • 现在终于可以使用了B.StartTimer并执行测量的代码路径。

无论如何,无论您要正确测量什么,都不会太有用。 map 代码是 Go 运行时的一个实现细节,可以随时更改。据我所知,当前的实现与 Go 1 中的完全不同。

最后:是的,调整良好的 map 实现可能对许多事情敏感,包括其中项目的数量和/或大小和/或类型 - 甚至架构和 CPU 步进和缓存大小也可以发挥作用在这方面的作用。

关于performance - 为什么我在使用范围时看到某些尺寸的 map 速度变慢?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17909822/

相关文章:

java - 使对象成为静态的好处,即使它们不需要如此?

performance - 平均响应时间与平均周转时间

go-swagger - 找不到 swagger 服务命令

go - 通过替换相同的子字符串来格式化字符串

soap - 如何在 Golang 中解析 Soap Envelope?

performance - RabbitMQ/ActiveMQ 或 Redis 超过 250,000 msg/s

php - 如何确定哪个 PHP 代码打开了未关闭的 MySQL 连接

java - 为什么 IdentityHashMap 使用线性探测来解决冲突

c++ - STL图插入效率: [] vs.插入

windows-phone-7 - 离线模式下带有自定义图层的 Windows Phone 7 map 控制