企业🤖AI Agent构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
### Maps, silces 与 Go 垃圾回收器 在本节中,我将向你提供一些示例,这些示例说明了为什么你对于垃圾收集器的操作应保持谨慎。本节的重点是要了解,存储指针的方式对垃圾收集器的性能有很大影响,尤其是在处理大量指针时。 > Tip: 所提供的示例使用了指针,切片和映射,它们都是原生 Go 数据类型。你将在*第 3 章,使用基本 Go 数据类型*了解有关指针,切片和映射的更多信息。 #### 使用 slice 这在一节的例子中我们将使用**slice**来存储大量的结构体数据。每一个结构体数据存储两个整数值。`sliceGC.go`中的 Go 代码如下: ```Go package main import ( "runtime" ) type data struct { i, j int } func main() { var N = 40000000 var structure []data for i := 0; i < N; i++ { value := int(i) structure = append(structure, data{value, value}) } runtime.GC() _ = structure[0] } ``` 最后一条语句`(_ = structure[0])`用于防止垃圾回收器过早地垃圾收集结构体变量,因为未在`for`循环之外对其进行引用或使用。随后的三个 Go 程序将使用相同的技术。除此重要细节外,`for`循环用于将所有值放入存储在 slice 中的结构中。 #### 使用 pointers 操作 map 在本小节中,我们将使用映射将所有指针存储为整数。该程序的名称为`mapStar.go`,其中包含以下 Go 代码: ```Go package main import ( "runtime" ) func main() { var N = 40000000 myMap := make(map[int]*int) for i := 0; i < N; i++ { value := int(i) myMap[value] = &value } runtime.GC() _ = myMap[0] } ``` 存储整数指针的 map 的名称为 `myMap`, `for` 循环用于将整数值放入 map。 #### 不使用 pointers 操作 map 在本小节中,我们将使用一个存储无指针纯值的 map, `mapNoStar.go`的 Go 代码如下: ```Go package main import ( "runtime" ) func main() { var N = 40000000 myMap := make(map[int]int) for i := 0; i < N; i++ { value := int(i) myMap[value] = value } runtime.GC() _ = myMap[0] } ``` 和之前一样,使用 for 循环将整数值放入 map 中。 #### 分割 map 本小节的实现会将 map 拆分为 maps,这也称为**分片**。本小节的程序另存为`mapSplit.go`,将分两部分介绍。 mapSplit.go 的第一部分包含以下 Go 代码: ```Go package main import ( "runtime" ) func main() { var N = 40000000 split := make([]map[int]int, 200) ``` 这是定义哈希的哈希值的地方。 第二段代码如下: ```Go for i := range split { split[i] = make(map[int]int) } for i := 0; i < N; i++ { value := int(i) split[i%200][value] = value } runtime.GC() _ = split[0][0] } ``` 这次,我们使用了两个`for`循环:一个用于创建哈希散列的`for`循环,以及 另一个用于在哈希哈希中存储所需数据。 #### Comparing the performance of the presented techniques 由于这四个程序都使用巨大复杂的数据结构,因此它们正在消耗大量内存。占用大量内存空间的程序会更频繁地触发 Go 垃圾收集器。因此,在本小节中,我们将使用`time(1)`命令比较这四个实现中每个实现的性能。 输出中重要的不是确切的数字,而是四种不同方法之间的时间差: ```shell $ time go run sliceGC.go real 1.50s user 1.72s sys 0.71s $ time go run mapStar.go real 13.62s user 23.74s sys 1.89s $ time go run mapNoStar.go real 11.41s user 10.35s sys 1.15s $ time go run mapSplit.go real 10.60s user 10.01s sys 0.74s ``` 因此,事实证明,maps 会减慢 Go 垃圾收集器的速度,而 slices 则可以更好地协作。这里应该注意,这不是 map 的问题,而是 Go 垃圾收集器工作方式的结果。但是,除非你要处理的是存储大量数据的 map,否则此问题在你的程序中将不会变得明显。 > Tip: 你将在第 11 章*代码测试,优化和性能分析*中学习有关基准测试的更多信息。此外,在第 3 章*使用基本 Go 数据类型*中,你将学到更专业的方法来测量 Go 中执行命令或程序所花费的时间。 你了解垃圾收集已经足够多了;下一节的主题将是`unsafe code`和`unsafe` Go package。