返回顶部
首页 > 资讯 > 精选 >如何通过禁止比较让Go二进制文件变小
  • 495
分享到

如何通过禁止比较让Go二进制文件变小

2023-06-16 11:06:40 495人浏览 八月长安
摘要

这期内容当中小编将会给大家带来有关如何通过禁止比较让Go二进制文件变小,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。大家常规的认知是,Go  程序中声明的类型越多,生成的二进制文件就越大。这个符

这期内容当中小编将会给大家带来有关如何通过禁止比较让Go二进制文件变小,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。

大家常规的认知是,Go  程序中声明的类型越多,生成的二进制文件就越大。这个符合直觉,毕竟如果你写的代码不去操作定义的类型,那么定义一堆类型就没有意义了。然而,链接器的部分工作就是检测没有被程序引用的函数(比如说它们是一个库的一部分,其中只有一个子集的功能被使用),然后把它们从最后的编译产出中删除。常言道,“类型越多,二进制文件越大”,对于多数  Go 程序还是正确的。

我会深入讲解在 Go 程序的上下文中“相等”的意义,以及为什么像这样的修改会对 Go 程序的大小有重大的影响。

定义两个值相等

Go 的语法定义了“赋值”和“相等”的概念。赋值是把一个值赋给一个标识符的行为。并不是所有声明的标识符都可以被赋值,如常量和函数就不可以。相等是通过检查标识符的内容是否相等来比较两个标识符的行为。

作为强类型语言,“相同”的概念从根源上被植入标识符的类型中。两个标识符只有是相同类型的前提下,才有可能相同。除此之外,值的类型定义了如何比较该类型的两个值。

例如,整型是用算数方法进行比较的。对于指针类型,是否相等是指它们指向的地址是否相同。映射和通道等引用类型,跟指针类似,如果它们指向相同的地址,那么就认为它们是相同的。

上面都是按位比较相等的例子,即值占用的内存的位模式是相同的,那么这些值就相等。这就是所谓的 memcmp,即内存比较,相等是通过比较两个内存区域的内容来定义的。

记住这个思路,我过会儿再来谈。

结构体相等

除了整型、浮点型和指针等标量类型,还有复合类型:结构体。所有的结构体以程序中的顺序被排列在内存中。因此下面这个声明:

type S struct {    a, b, c, d int64}

会占用 32 字节的内存空间;a 占用 8 个字节,b 占用 8 个字节,以此类推。Go 的规则说如果结构体所有的字段都是可以比较的,那么结构体的值就是可以比较的。因此如果两个结构体所有的字段都相等,那么它们就相等。

a := S{1, 2, 3, 4}b := S{1, 2, 3, 4}fmt.Println(a == b) // 输出 true

编译器在底层使用 memcmp 来比较 a 的 32 个字节和 b 的 32 个字节。

填充和对齐

然而,在下面的场景下过分简单化的按位比较的策略会返回错误的结果:

type S struct {    a byte    b uint64    c int16    d uint32} func main()    a := S{1, 2, 3, 4}    b := S{1, 2, 3, 4}    fmt.Println(a == b) // 输出 true}

编译代码后,这个比较表达式的结果还是 true,但是编译器在底层并不能仅依赖比较 ab 的位模式,因为结构体有填充

Go 要求结构体的所有字段都对齐。2 字节的值必须从偶数地址开始,4 字节的值必须从 4 的倍数地址开始,以此类推 1。编译器根据字段的类型和底层平台加入了填充来确保字段都对齐。在填充之后,编译器实际上看到的是 2

type S struct {    a byte    _ [7]byte // 填充    b uint64    c int16    _ [2]int16 // 填充    d uint32}

填充的存在保证了字段正确对齐,而填充确实占用了内存空间,但是填充字节的内容是未知的。你可能会认为在 Go 中 填充字节都是 0,但实际上并不是 — 填充字节的内容是未定义的。由于它们并不是被定义为某个确定的值,因此按位比较会因为分布在 s 的 24 字节中的 9 个填充字节不一样而返回错误结果。

Go 通过生成所谓的相等函数来解决这个问题。在这个例子中,s 的相等函数只比较函数中的字段略过填充部分,这样就能正确比较类型 s 的两个值。

类型算法

呵,这是个很大的设置,说明了为什么,对于 Go  程序中定义的每种类型,编译器都会生成几个支持函数,编译器内部把它们称作类型的算法。如果类型是一个映射的键,那么除相等函数外,编译器还会生成一个哈希函数。为了维持稳定,哈希函数在计算结果时也会像相等函数一样考虑诸如填充等因素。

凭直觉判断编译器什么时候生成这些函数实际上很难,有时并不明显,(因为)这超出了你的预期,而且链接器也很难消除没有被使用的函数,因为反射往往导致链接器在裁剪类型时变得更保守。

通过禁止比较来减小二进制文件的大小

现在,我们来解释一下 Brad 的修改。向类型添加一个不可比较的字段 3,结构体也随之变成不可比较的,从而强制编译器不再生成相等函数和哈希函数,规避了链接器对那些类型的消除,在实际应用中减小了生成的二进制文件的大小。作为这项技术的一个例子,下面的程序:

package main import "fmt" func main() {    type t struct {        // _ [0][]byte // 取消注释以阻止比较        a byte        b uint16        c int32        d uint64    }    var a t    fmt.Println(a)}

用 Go 1.14.2(darwin/amd64)编译,大小从 2174088 降到了 2174056,节省了 32 字节。单独看节省的这  32  字节似乎微不足道,但是考虑到你的程序中每个类型及其传递闭包都会生成相等和哈希函数,还有它们的依赖,这些函数的大小随类型大小和复杂度的不同而不同,禁止它们会大大减小最终的二进制文件的大小,效果比之前使用  -ldflags="-s -w" 还要好。

最后总结一下,如果你不想把类型定义为可比较的,可以在源码层级强制实现像这样的奇技淫巧,会使生成的二进制文件变小。

上述就是小编为大家分享的如何通过禁止比较让Go二进制文件变小了,如果刚好有类似的疑惑,不妨参照上述分析进行理解。如果想知道更多相关知识,欢迎关注编程网精选频道。

--结束END--

本文标题: 如何通过禁止比较让Go二进制文件变小

本文链接: https://lsjlt.com/news/283478.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • 如何通过禁止比较让Go二进制文件变小
    这期内容当中小编将会给大家带来有关如何通过禁止比较让Go二进制文件变小,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。大家常规的认知是,Go  程序中声明的类型越多,生成的二进制文件就越大。这个符...
    99+
    2023-06-16
  • linux如何禁用一个二进制文件的 SUID位
    ...
    99+
    2024-04-02
  • C#如何实现获取文件大小并进行比较
    本文小编为大家详细介绍“C#如何实现获取文件大小并进行比较”,内容详细,步骤清晰,细节处理妥当,希望这篇“C#如何实现获取文件大小并进行比较”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。一、实现功能①需要获取到文...
    99+
    2023-07-05
  • PHP如何二进制安全不区分大小写的字符串比较
    这篇文章将为大家详细讲解有关PHP如何二进制安全不区分大小写的字符串比较,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。PHP 中二进制安全不区分大小写的字符串比较 在 PHP 中,执行不区分大小写的字符串...
    99+
    2024-04-02
  • php如何修改图片二进制文件大小
    本篇内容主要讲解“php如何修改图片二进制文件大小”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“php如何修改图片二进制文件大小”吧!一、为什么需要对图片进行二进制文件大小修改在 Web 开发中...
    99+
    2023-07-05
  • 如何运行编译好的 Go 测试二进制文件?
    php小编西瓜为您解答如何运行编译好的Go测试二进制文件。在Go语言中,编译完成的二进制文件可以直接运行。首先,通过命令行进入到二进制文件所在的目录。然后,输入二进制文件的名称,并按下...
    99+
    2024-02-13
    go语言
  • SpringBoot如何通过配置文件(yml,properties)限制文件上传大小
    目录通过配置文件(yml,properties)限制文件上传大小properties类型配置文件设置yml类型配置文件设置设置文件上传大小限制--默认为1M解决方法通过配置文件(ym...
    99+
    2024-04-02
  • PHP如何二进制安全比较字符串开头的若干个字符(不区分大小写)
    这篇文章将为大家详细讲解有关PHP如何二进制安全比较字符串开头的若干个字符(不区分大小写),小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。PHP 二进制安全比较字符串开头的若干个字符(不区分大小写) 简介 ...
    99+
    2024-04-02
  • 如何在 golang 程序中运行二进制文件并通过发送一些输入并等待输出来保持与其交互?
    你在学习Golang相关的知识吗?本文《如何在 golang 程序中运行二进制文件并通过发送一些输入并等待输出来保持与其交互?》,主要介绍的内容就涉及到,如果你想提升自己的开发能力,就不要错过这篇文...
    99+
    2024-04-05
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作