惨,给Go提的代码被批麻了
<p>hello大家好,我是小楼。</p><p>不知道大家还记不记得我上次找到了一个Go的Benchmark执行会超时的Bug?就是这篇文章《我好像发现了一个Go的Bug?》。</p>
<p>之后我就向Go提交了一个PR进行修复,本想等着代码被Merge进去,以后也可以吹牛说自己是个Go的Contributor,但事情并不顺利,今天就来分享一下这次失败的代码提交。</p>
<h2 id="第一次提交">第一次提交</h2>
<p>在我意识到Bug时,就迫不及待想去修复,于是有了这一次提交。</p>
<p>在说代码前,先说点关于Go仓库的问题,Go并没有直接托管在github,而是自建的Gerrit Code Review,github上只是个镜像仓库,所有在github上提交的issue和代码都会被一个机器人搬运到Gerrit上。</p>
<p>而且Go对提交代码的要求是必须关联一个issue,于是我就提了一个,自问自答了属于是。</p>
<p><img src="https://img2022.cnblogs.com/blog/2379072/202203/2379072-20220308102201403-1664137799.png"></p>
<p>描述了一下遇到的问题,但隔天被一位大佬认为是重复问题,并且关闭了这个issue</p>
<p><img src="https://img2022.cnblogs.com/blog/2379072/202203/2379072-20220308102208123-682526384.png"></p>
<p>但我点进去仔细看了下,和我说的应该没有关系,他们讨论的是单测超时不生效的问题,于是我狡辩了一下。</p>
<p><img src="https://img2022.cnblogs.com/blog/2379072/202203/2379072-20220308102214389-335422219.png"></p>
<p>果然狡辩是有用的,另一位大佬同意我的观点,于是我给他点了个赞,但他也指出我的代码存在问题。</p>
<p>下面进入今天的正题,为了便于讲解,我先把有问题的代码段摘出来:</p>
<pre><code class="language-go">func (b *B) launch() {
...
// n(int64)可能会溢出
n = goalns * prevIters / prevns
...
}
</code></pre>
<p>既然知道n会溢出,还不简单?加个判断就完了。</p>
<p><img src="https://img2022.cnblogs.com/blog/2379072/202203/2379072-20220308102220577-539263140.png"></p>
<h3 id="溢出考虑不全">溢出考虑不全</h3>
<p>这位大佬说我的代码在防止int64溢出时不够安全,难道溢出不是这样判断吗?</p>
<p><img src="https://img2022.cnblogs.com/blog/2379072/202203/2379072-20220308102225981-421598762.png"></p>
<p>不过还好,大佬给了一点点指导</p>
<p><img src="https://img2022.cnblogs.com/blog/2379072/202203/2379072-20220308102232476-2017410132.png"></p>
<p>同时也发来一段演示代码</p>
<p><img src="https://img2022.cnblogs.com/blog/2379072/202203/2379072-20220308102237602-383343613.png"></p>
<p>果然 「show me the code」 最好使,简单点来说就是正数溢出成了负数,再溢出就又是正数,只要溢出足够多,结果可正可负。</p>
<h3 id="得有测试">得有测试</h3>
<p>大佬还指出了另一个问题,兄弟,你写的代码得有有测试啊!</p>
<p><img src="https://img2022.cnblogs.com/blog/2379072/202203/2379072-20220308102243680-467685755.png"></p>
<p>虽然我给开源项目提交代码不多,但也知道这点,为什么这次没写呢?主要是我觉得单测不太好写,既然大佬提出来,硬着头皮也得写了。</p>
<h2 id="第二次提交">第二次提交</h2>
<p>第二次提交,改掉了之前判断int64溢出的方法,用逆运算还原回去和原值做对比来看是否溢出,这个方法上次用到还是在大学的C语言课程中</p>
<p><img src="https://img2022.cnblogs.com/blog/2379072/202203/2379072-20220308102249865-1022480518.png"></p>
<p>还附加了一个单元测试</p>
<p><img src="https://img2022.cnblogs.com/blog/2379072/202203/2379072-20220308102254794-1734903273.png"></p>
<p>这个单元测试稍微解释下:</p>
<p>设置了150s的单测时间,每次试探单测时,次数都加1,如果试探次数超过6次,就说明有问题,终止单测。</p>
<p><img src="https://img2022.cnblogs.com/blog/2379072/202203/2379072-20220308102307834-316637334.png"></p>
<p>这段代码在上述溢出判断加之前执行,一定是失败的,溢出判断加了之后,则可正常执行。</p>
<p>接下来就是等待回复,等了很久很久,Go的研发周期是以半年记,等得我都差点忘了这件事了,直到一天邮件提醒我。</p>
<p>前方高能,来看看另一位大佬是如何review我的代码的。</p>
<h3 id="commit-message不规范">commit message不规范</h3>
<p>首先,commit message不规范,我的commit message是这样的,我是在github上提交的,被机器人搬运过去。</p>
<p><img src="https://img2022.cnblogs.com/blog/2379072/202203/2379072-20220308102332086-880831346.png"></p>
<p>给出的意见是</p>
<p><img src="https://img2022.cnblogs.com/blog/2379072/202203/2379072-20220308102337368-640251537.png"></p>
<p>原来Go的commit message是有一个文档专门介绍的,之前没注意到,点进去看了下</p>
<p><img src="https://img2022.cnblogs.com/blog/2379072/202203/2379072-20220308102343178-895110199.png"></p>
<p>翻译下就是commit message的第一行应该是简短的摘要,并且要指出影响了哪些包,第一行后得有一个空行。</p>
<p>commit message的主要内容应该详细说明变更的上下文,并解释其作用,语句完整、标点正确,不要使用HTML、Markdown等标记语言。相关的信息,如基准测试数据等也需要写进来。</p>
<p>最后需要有关联的issue,如果是修复某问题,需要用<code>Fixes #12345</code>来关联12345号问题,如果只是解决部分问题,使用<code>Updates #12345</code>,如果修复的是golang.org/x/库,使用<code>Fixes golang/go#159</code>。</p>
<p>一个好的例子如下:</p>
<blockquote>
<p>math: improve Sin, Cos and Tan precision for very large arguments</p>
<p>The existing implementation has poor numerical properties for<br>
large arguments, so use the McGillicutty algorithm to improve<br>
accuracy above 1e10.</p>
<p>The algorithm is described at https://wikipedia.org/wiki/McGillicutty_Algorithm<br>
Fixes #159</p>
</blockquote>
<p>看来我得好好改下commit message。</p>
<h3 id="可以考虑集成测试">可以考虑集成测试</h3>
<p>单测提了不少问题,首先是这个</p>
<p><img src="https://img2022.cnblogs.com/blog/2379072/202203/2379072-20220308102350607-153747537.png"></p>
<p>我把Benchmark的单测包名改了,改这个是为了能调用包内未导出的方法,确实不太好,但当时没想到别的方案。</p>
<p>接着是不应该直接调用未暴露的<code>cleanups</code>和内部的一些变量,和上面呼应。</p>
<p><img src="https://img2022.cnblogs.com/blog/2379072/202203/2379072-20220308102356334-58555937.png"></p>
<p>可以用<code>flag.Lookup</code>来set flag,这点没用过,所以不知道。</p>
<p>或者可以考虑使用集成测试来代替单元测试,Go的集成测试在<code>cmd/go/testdata/script</code>,这个之前也没接触过,所以也不知道,这个集成测试具体怎么用可以看<code>cmd/go/testdata/script/README</code></p>
<p>这点可以看出我真是个Go新手,需要多看多学,测试不光只有单测,Go还支持集成测试。</p>
<h3 id="缺少注释">缺少注释</h3>
<p>再接着看</p>
<p><img src="https://img2022.cnblogs.com/blog/2379072/202203/2379072-20220308102402183-732926420.png"></p>
<p>这里模拟150s的单测,大佬就提问了,这个单测真的会跑150s吗?如果是的话,那也太长了!</p>
<p>如果不是,也没给我解释清楚啊~</p>
<p>还有这个</p>
<p><img src="https://img2022.cnblogs.com/blog/2379072/202203/2379072-20220308102408736-1509583082.png"></p>
<p>你咋知道执行次数一定小于6呢?Go可没保证这个。</p>
<p>对于这两点的疑问,核心问题在于没写注释,别人不知道你的想法呀,如果开源的代码里面充斥着这种看不懂的玩意,那不是要命。</p>
<p>首先对于第一个,模拟150s,实际上不会真的跑那么久,因为后面有试探次数的限制,如果超过6次,就终止了,这个6次是怎么得到的呢?答案其实在《我好像发现了一个Go的Bug》中。</p>
<p>Benchmark在一个方法上跑的最多的次数是1e9次,也就是1000000000次,如果待测试方法执行时间非常短,且在Benchmark时间比较长的情况下,计算需要执行多少次一定会溢出,所以试探的执行次数会是这个增长序列:</p>
<p>100、10000、1000000、100000000、100000001、100000002......</p>
<p>实际可能>4就完事了,可能是我之前测试的有问题,emm...</p>
<h3 id="溢出需要重新考虑">溢出需要重新考虑</h3>
<p><img src="https://img2022.cnblogs.com/blog/2379072/202203/2379072-20220308102415230-1867369541.png"></p>
<p>别判断n是否溢出,如果判断上一层,即goalns是否大于等于 <code>int64最大值 * prevIters</code>是否更合理呢?</p>
<blockquote>
<p><code>n = goalns * prevIters / prevns</code>,goalns 是设置的执行时间(单位纳秒)</p>
</blockquote>
<p>看来是我格局小了,别急,还有</p>
<p><img src="https://img2022.cnblogs.com/blog/2379072/202203/2379072-20220308102421752-219354359.png"></p>
<p>怎么知道<code>100 * last</code>是不是也溢出了呢?所以我们是不是全程的计算都用float64更合理呢?</p>
<p>测试了下,float64范围大的离谱,感兴趣可以试试,就不贴数据了,太长!</p>
<h2 id="最后说一句">最后说一句</h2>
<p>虽然这次提交比较失败,但还是有点收获,等我忙完这阵,抽空出来再改改,说不定就被Merge了,大家祝我好运吧,今天的分享到这,我们下期再见!对了,文中的issue参考</p>
<ul>
<li>https://github.com/golang/go/pull/50023</li>
</ul>
<hr>
<blockquote>
<p>搜索关注微信公众号"捉虫大师",后端技术分享,架构设计、性能优化、源码阅读、问题排查、踩坑实践。</p>
</blockquote>
<p><img src="https://img2022.cnblogs.com/blog/2379072/202203/2379072-20220308102432015-809757944.jpg"></p><br><br>
来源:https://www.cnblogs.com/zhuochongdashi/p/15979382.html
頁:
[1]