惨,给Go提的代码被批麻了

捉虫大师
• 阅读 1396

hello大家好,我是小楼。

不知道大家还记不记得我上次找到了一个Go的Benchmark执行会超时的Bug?就是这篇文章《我好像发现了一个Go的Bug?》

之后我就向Go提交了一个PR进行修复,本想等着代码被Merge进去,以后也可以吹牛说自己是个Go的Contributor,但事情并不顺利,今天就来分享一下这次失败的代码提交。

第一次提交

在我意识到Bug时,就迫不及待想去修复,于是有了这一次提交。

在说代码前,先说点关于Go仓库的问题,Go并没有直接托管在github,而是自建的Gerrit Code Review,github上只是个镜像仓库,所有在github上提交的issue和代码都会被一个机器人搬运到Gerrit上。

而且Go对提交代码的要求是必须关联一个issue,于是我就提了一个,自问自答了属于是。

惨,给Go提的代码被批麻了

描述了一下遇到的问题,但隔天被一位大佬认为是重复问题,并且关闭了这个issue

惨,给Go提的代码被批麻了

但我点进去仔细看了下,和我说的应该没有关系,他们讨论的是单测超时不生效的问题,于是我狡辩了一下。

惨,给Go提的代码被批麻了

果然狡辩是有用的,另一位大佬同意我的观点,于是我给他点了个赞,但他也指出我的代码存在问题。

下面进入今天的正题,为了便于讲解,我先把有问题的代码段摘出来:

func (b *B) launch() {
   ...
    // n(int64)可能会溢出
   n = goalns * prevIters / prevns
   ...
}

既然知道n会溢出,还不简单?加个判断就完了。

惨,给Go提的代码被批麻了

溢出考虑不全

这位大佬说我的代码在防止int64溢出时不够安全,难道溢出不是这样判断吗?

惨,给Go提的代码被批麻了

不过还好,大佬给了一点点指导

惨,给Go提的代码被批麻了

同时也发来一段演示代码

惨,给Go提的代码被批麻了

果然 「show me the code」 最好使,简单点来说就是正数溢出成了负数,再溢出就又是正数,只要溢出足够多,结果可正可负。

得有测试

大佬还指出了另一个问题,兄弟,你写的代码得有有测试啊!

惨,给Go提的代码被批麻了

虽然我给开源项目提交代码不多,但也知道这点,为什么这次没写呢?主要是我觉得单测不太好写,既然大佬提出来,硬着头皮也得写了。

第二次提交

第二次提交,改掉了之前判断int64溢出的方法,用逆运算还原回去和原值做对比来看是否溢出,这个方法上次用到还是在大学的C语言课程中

惨,给Go提的代码被批麻了

还附加了一个单元测试

惨,给Go提的代码被批麻了

这个单元测试稍微解释下:

设置了150s的单测时间,每次试探单测时,次数都加1,如果试探次数超过6次,就说明有问题,终止单测。

惨,给Go提的代码被批麻了

这段代码在上述溢出判断加之前执行,一定是失败的,溢出判断加了之后,则可正常执行。

接下来就是等待回复,等了很久很久,Go的研发周期是以半年记,等得我都差点忘了这件事了,直到一天邮件提醒我。

前方高能,来看看另一位大佬是如何review我的代码的。

commit message不规范

首先,commit message不规范,我的commit message是这样的,我是在github上提交的,被机器人搬运过去。

惨,给Go提的代码被批麻了

给出的意见是

惨,给Go提的代码被批麻了

原来Go的commit message是有一个文档专门介绍的,之前没注意到,点进去看了下

惨,给Go提的代码被批麻了

翻译下就是commit message的第一行应该是简短的摘要,并且要指出影响了哪些包,第一行后得有一个空行。

commit message的主要内容应该详细说明变更的上下文,并解释其作用,语句完整、标点正确,不要使用HTML、Markdown等标记语言。相关的信息,如基准测试数据等也需要写进来。

最后需要有关联的issue,如果是修复某问题,需要用Fixes #12345来关联12345号问题,如果只是解决部分问题,使用Updates #12345,如果修复的是golang.org/x/库,使用Fixes golang/go#159

一个好的例子如下:

math: improve Sin, Cos and Tan precision for very large arguments

The existing implementation has poor numerical properties for large arguments, so use the McGillicutty algorithm to improve accuracy above 1e10.

The algorithm is described at https://wikipedia.org/wiki/McGillicutty_Algorithm Fixes #159

看来我得好好改下commit message。

可以考虑集成测试

单测提了不少问题,首先是这个

惨,给Go提的代码被批麻了

我把Benchmark的单测包名改了,改这个是为了能调用包内未导出的方法,确实不太好,但当时没想到别的方案。

接着是不应该直接调用未暴露的cleanups和内部的一些变量,和上面呼应。

惨,给Go提的代码被批麻了

可以用flag.Lookup来set flag,这点没用过,所以不知道。

或者可以考虑使用集成测试来代替单元测试,Go的集成测试在cmd/go/testdata/script,这个之前也没接触过,所以也不知道,这个集成测试具体怎么用可以看cmd/go/testdata/script/README

这点可以看出我真是个Go新手,需要多看多学,测试不光只有单测,Go还支持集成测试。

缺少注释

再接着看

惨,给Go提的代码被批麻了

这里模拟150s的单测,大佬就提问了,这个单测真的会跑150s吗?如果是的话,那也太长了!

如果不是,也没给我解释清楚啊~

还有这个

惨,给Go提的代码被批麻了

你咋知道执行次数一定小于6呢?Go可没保证这个。

对于这两点的疑问,核心问题在于没写注释,别人不知道你的想法呀,如果开源的代码里面充斥着这种看不懂的玩意,那不是要命。

首先对于第一个,模拟150s,实际上不会真的跑那么久,因为后面有试探次数的限制,如果超过6次,就终止了,这个6次是怎么得到的呢?答案其实在《我好像发现了一个Go的Bug》中。

Benchmark在一个方法上跑的最多的次数是1e9次,也就是1000000000次,如果待测试方法执行时间非常短,且在Benchmark时间比较长的情况下,计算需要执行多少次一定会溢出,所以试探的执行次数会是这个增长序列:

100、10000、1000000、100000000、100000001、100000002......

实际可能>4就完事了,可能是我之前测试的有问题,emm...

溢出需要重新考虑

惨,给Go提的代码被批麻了

别判断n是否溢出,如果判断上一层,即goalns是否大于等于 int64最大值 * prevIters是否更合理呢?

n = goalns * prevIters / prevns,goalns 是设置的执行时间(单位纳秒)

看来是我格局小了,别急,还有

惨,给Go提的代码被批麻了

怎么知道100 * last是不是也溢出了呢?所以我们是不是全程的计算都用float64更合理呢?

测试了下,float64范围大的离谱,感兴趣可以试试,就不贴数据了,太长!

最后说一句

虽然这次提交比较失败,但还是有点收获,等我忙完这阵,抽空出来再改改,说不定就被Merge了,大家祝我好运吧,今天的分享到这,我们下期再见!对了,文中的issue参考


搜索关注微信公众号"捉虫大师",后端技术分享,架构设计、性能优化、源码阅读、问题排查、踩坑实践。 惨,给Go提的代码被批麻了

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Wesley13 Wesley13
2年前
java将前端的json数组字符串转换为列表
记录下在前端通过ajax提交了一个json数组的字符串,在后端如何转换为列表。前端数据转化与请求varcontracts{id:'1',name:'yanggb合同1'},{id:'2',name:'yanggb合同2'},{id:'3',name:'yang
Jacquelyn38 Jacquelyn38
2年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Stella981 Stella981
2年前
Android So动态加载 优雅实现与原理分析
背景:漫品Android客户端集成适配转换功能(基于目标识别(So库35M)和人脸识别库(5M)),导致apk体积50M左右,为优化客户端体验,决定实现So文件动态加载.!(https://oscimg.oschina.net/oscnet/00d1ff90e4b34869664fef59e3ec3fdd20b.png)点击上方“蓝字”关注我
Wesley13 Wesley13
2年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
2年前
35岁,真的是程序员的一道坎吗?
“程序员35岁是道坎”,“程序员35岁被裁”……这些话咱们可能都听腻了,但每当触及还是会感到丝丝焦虑,毕竟每个人都会到35岁。而国内互联网环境确实对35岁以上的程序员不太友好:薪资要得高,却不如年轻人加班猛;虽说经验丰富,但大部分公司并不需要太资深的程序员。但35岁危机并不是不可避免的,比如你可以不断精进技术,将来做技术管理或者
Wesley13 Wesley13
2年前
35岁是技术人的天花板吗?
35岁是技术人的天花板吗?我非常不认同“35岁现象”,人类没有那么脆弱,人类的智力不会说是35岁之后就停止发展,更不是说35岁之后就没有机会了。马云35岁还在教书,任正非35岁还在工厂上班。为什么技术人员到35岁就应该退役了呢?所以35岁根本就不是一个问题,我今年已经37岁了,我发现我才刚刚找到自己的节奏,刚刚上路。
捉虫大师 捉虫大师
1年前
对不起,我错了,这代码不好写
hello,大家好呀,我是小楼。前几天不是写了这篇文章嘛。文章介绍了Sentinl的自适应缓存时间戳算法,从原理到实现都手把手解读了,而且还发现SentinelGo还未实现这个自适应算法,于是我就觉得,这简单啊,把Java代码翻译成Go不就可以混个PR?甚至在文章初稿中把这个描述为:「有手就可以」,感觉不太妥当,后来被我删掉了。过了几天,我想去看看有没有人看
Python进阶者 Python进阶者
3个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这