0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

Go高性能-两种内存大小为0的数据类型

冬至子 来源:洋芋土豆 作者:真没什么深度 2023-05-22 17:33 次阅读

概述

Go 中的空结构体 struct{}{} 的内存大小等于 0,除此之外,还有别的数据类型内存大小也等于 0 吗?

map 实现 set

Go 的标准库没有内置的 Set 类型,在不引用第三方包的情况下,一般是结合内置的 map 类型实现 Set 类型相关功能。

这里假设 Set 元素类型为 int, 那么我们就以 int 作为 map 的键类型,以 bool 作为 map 的值类型 (之所以选择 bool 类型,是因为其大小为 1 个字节,相对其他数据类型可以节省内存,当然,也可以使用 byte 类型,其大小同样是 1 个字节)。

package main

import "fmt"

// Set 类型定义
type set map[int]bool

// 初始化一个新的 Set
func newSet() set {
    return make(set)
}

// 元素是否存在于与集合中
func (s set) contains(ele int) bool {
    _, ok := s[ele]
    return ok
}

// 添加元素到集合
func (s set) add(ele int) {
    s[ele] = true
}

// 从集合中删除某个元素
func (s set) remove(ele int) {
    delete(s, ele)
}

func main() {
    s := newSet()

    fmt.Println(s.contains(100))
    s.add(100)
    fmt.Println(s.contains(100))
    s.remove(100)
    fmt.Println(s.contains(100))
}
$ go run main.go

# 输出如下
false
true
false

上述示例代码通过内置类型 map 实现了 set 类型相关功能,美中不足的一点在于: 每个元素都要浪费一个 bool 类型作为标识占位符, 能否进一步的优化呢 ?当然是可以的,这就是接下来要讲到的 空结构体 了。

空结构体

Go 中的空结构体 struct{}{}一个底层的内置变量,不占用任何内存 :

package main

import (
    "fmt"
    "unsafe"
)

func main() {
    fmt.Printf("size = %d\\n", unsafe.Sizeof(struct{}{}))
}
$ go run main.go

# 输出如下
size = 0

结合刚才的例子,可以将 struct{}{} 作为 Set 的元素,这样不论 Set 有多少个元素,标志位 内存占用始终为 0 。

使用 bool 实现 Set

测试代码

package performance

import (
    "testing"
)

// Set 类型定义, 使用 bool 类型作为占位符
type set map[int]bool

// 初始化一个新的 Set
func newSet() set {
    return make(set)
}

// 元素是否存在于与集合中
func (s set) contains(ele int) bool {
    _, ok := s[ele]
    return ok
}

// 添加元素到集合
func (s set) add(ele int) {
    s[ele] = true
}

// 从集合中删除某个元素
func (s set) remove(ele int) {
    delete(s, ele)
}

func Benchmark_Compare(b *testing.B) {
    s := newSet()

    for i := 0; i < b.N; i++ {
        s.add(i)
    }
    for i := 0; i < b.N; i++ {
        _ = s.contains(i)
        s.remove(i)
    }
}

运行测试,并将基准测试结果写入文件:

$ go test -run='^$' -bench=. -count=1 -benchtime=10000000x -benchmem > bool.txt

使用空结构体实现 Set

测试代码

package performance

import (
    "testing"
)

// Set 类型定义, 使用 bool 类型作为占位符
type set map[int]struct{}

// 初始化一个新的 Set
func newSet() set {
    return make(set)
}

// 元素是否存在于与集合中
func (s set) contains(ele int) bool {
    _, ok := s[ele]
    return ok
}

// 添加元素到集合
func (s set) add(ele int) {
    s[ele] = struct{}{}
}

// 从集合中删除某个元素
func (s set) remove(ele int) {
    delete(s, ele)
}

func Benchmark_Compare(b *testing.B) {
    s := newSet()

    for i := 0; i < b.N; i++ {
        s.add(i)
    }
    for i := 0; i < b.N; i++ {
        _ = s.contains(i)
        s.remove(i)
    }
}

运行测试,并将基准测试结果写入文件:

$ go test -run='^$' -bench=. -count=1 -benchtime=10000000x -benchmem > struct.txt

使用 benchstat 比较差异

$ benchstat -alpha=100 bool.txt struct.txt

# 输出如下
name        old time/op    new time/op    delta
_Compare-8     371ns ± 0%     332ns ± 0%  -10.47%  (p=1.000 n=1+1)

name        old alloc/op   new alloc/op   delta
_Compare-8     44.0B ± 0%     40.0B ± 0%   -9.09%  (p=1.000 n=1+1)

name        old allocs/op  new allocs/op  delta
_Compare-8      0.00           0.00          ~     (all equal)

从输出的结果中可以看到,相比于使用 bool 作为 Set 元素占位符,使用 空结构体 在性能和内存占用方面,都有了小幅度的优化提升 (10% 左右)。因为时间关系,这里的基准测试只运行了 10000000 次, 运行次数越大,优化的效果越明显 。感兴趣的读者可以将 -benchtime 调大后看看优化效果。

小结

**Go 中的空结构体 **struct{}{} 不占用任何内存,而且有很清晰的语义性质 (作为占位符使用) 。除了刚才示例中实现 Set 类型功能外, 还可以使用空结构体作为 通道信号标识空对象 等,各种使用场景请读者自行探索。

彩蛋

除了空结构体 struct{}{} 之外,还有一个鲜为人知的内存大小为 0 的数据类型是: 空数组。但是相对 struct{}{} 丰富的表达性,空数组 使用的场景很少。

package main

import (
    "fmt"
    "unsafe"
)

func main() {
    fmt.Printf("size = %d\\n", unsafe.Sizeof([0]int{}))
}
$ go run main.go

# 输出如下
size = 0
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • go语言
    +关注

    关注

    1

    文章

    157

    浏览量

    9012
收藏 人收藏

    评论

    相关推荐

    LabVIEW的数据类型、存储格式

    ,左边的数字代表偏移(offset)数组在内存起始存储位置的字节数。其他数据类型布尔、字符串、字符串数组、簇、路径布尔(Boolean)型数据是字节型数据,存储
    发表于 12-19 15:36

    LabVIEW的数据类型、存储格式

    ,左边的数字代表偏移(offset)数组在内存起始存储位置的字节数。其他数据类型布尔、字符串、字符串数组、簇、路径布尔(Boolean)型数据是字节型数据,存储
    发表于 12-19 15:37

    Python中常用的数据类型

    字符串在Python中,加了引号的字符都被认为是字符串,其声明有三方式,分别是:单引号、双引号和三引号;Python中的字符串有两种数据类型,分别是str类型和unicode类型
    发表于 04-19 15:14

    STM32F103xx系列内存大小

    系列内存大小三、闪存概述四、写和擦除闪存五、读操作六、总代码实现一、内存大小内存的容量一般都是 2的整次方倍,系统对内存的识别是以Byte(字节)
    发表于 08-19 08:53

    电脑内存大小怎么查看

    日常办公或者玩游戏的时候,都需要确认一下电脑内存够不够运行将要办公的软件或者将要玩游戏的软件,问题来了,怎么查看电脑内存大小?现在给大家带来怎么查看电脑内存大小教程。
    发表于 05-27 10:13 3354次阅读

    Linux:测试进程占用的虚拟内存大小

    Linux:测试进程占用的虚拟内存大小
    的头像 发表于 06-23 09:23 2823次阅读
    Linux:测试进程占用的虚拟<b class='flag-5'>内存大小</b>

    c++ 之布尔类型和引用的学习总结

    在c语言里面我们知道是没有布尔数据类型的,而在C++中添加了布尔数据类型(bool),它的取值是:true或者false(也就是1或者0),在内存大小上它占用一个字节
    的头像 发表于 12-24 18:03 556次阅读

    重视变量的数据类型

    不管在什么语言中,定义一个变量时必然要在内存中开辟一个相应大小的空间来存储该变量。不同的数据类型内存所占的空间大小不同,其所能表示的
    发表于 01-13 15:05 1次下载
    重视变量的<b class='flag-5'>数据类型</b>

    C语言中内存四区模型的本质区别和代码分析

    数据类型可理解创建变量的模具:是固定内存大小的别名。
    的头像 发表于 03-09 14:45 1021次阅读

    什么是数据类型转换

    常用的3种数据类型:1、Python数据类型第一:字符串(str)。 2、Python数据类型第二:整数(int)。 3、Pytho
    的头像 发表于 02-23 15:21 1716次阅读

    CODESYS变量类型的范围和所占内存大小

    在使用通讯和数据类型转换时,需要特别注意数据类型的范围和所占存储空间的大小,为了方便查询,在这里和大家归纳总结下。
    的头像 发表于 05-25 14:57 5732次阅读
    CODESYS变量<b class='flag-5'>类型</b>的范围和所占<b class='flag-5'>内存大小</b>

    Verilog最常用的2种数据类型

    Verilog 最常用的 2 种数据类型就是线网(wire)与寄存器(reg),其余类型可以理解两种数据类型的扩展或辅助。
    的头像 发表于 05-29 16:27 2094次阅读
    Verilog最常用的2<b class='flag-5'>种数据类型</b>

    redis的五种数据类型

    Redis是一高性能内存数据库,常用于缓存、任务队列、分布式锁等场景。它提供了多种数据类型来满足各种不同的需求,包括字符串(string
    的头像 发表于 11-16 11:06 577次阅读

    weblogic设置jvm内存大小

    WebLogic是一Java EE应用服务器,用于构建和部署企业级Java应用程序。在配置WebLogic服务器时,设置JVM的内存大小非常重要,这可以提高应用程序的性能和可靠性。本文将详细介绍
    的头像 发表于 12-05 14:44 2835次阅读

    eclipse设置jvm内存大小

    内存大小,并对其背后的原理进行解释。 JVM(Java虚拟机)是Java程序的运行环境,它负责将Java字节码翻译成机器码,以便在不同的平台上执行。JVM使用内存来存储运行时对象和执行过程中的临时数据。如果JVM的
    的头像 发表于 12-06 11:43 1765次阅读