一位老哥写文怒刚 Redis 做者

你好,我是 yes。java

这篇文章是关于 Redis 的一个技术类小八卦,我看完直呼 666。linux

开始表演

我无心间查到一位叫 mattsta 的老哥的 14 年写的文章,这老哥说 Redis 如今实现的 CRC 算法太简陋了!他有能提高 4 倍性能的加强版 CRC 算法 - CRCSpeed!git

我立刻去看了我本地 Redis 5.0 版本的源码,发现 CRC 算法并无采纳他的加强版,仍是老的实现。github

我又去看了最新 6.0 的版本,发现 CRC-64 改为了 CRCSpeed 的实现,在 2020 年 4 月 28 号提交的,提交者是 antirez,也就是 Redis 的做者。面试

这就让我愈加的好奇了,这 2014 到 2020 跨度有点大啊,到底发生了啥?redis

而后我又去翻了 mattsta 的 Github ,追踪了整个事情发展的历史脉络,事情愈来愈有意思了。算法

我已经火烧眉毛地想和你们一块儿再来看一遍这哥们的文章,过一遍这件事。数组

这哥们的文章是英文的,不太重点我在图中都标红了,也都作了相应翻译。微信

Fancy CRCing You Here

这老哥文章标题就有那味儿!Fancy CRCing You Here,这是要开炮了。memcached

而后这老哥文章的第一句话就很为人着想。

简单翻译下就是 short 的人直接点连接看结果,那我确定不 short 的啊,因此我选择接下去看。

紧接着这老哥就抛出了如下的话:

Redis 的 CRC-64 的实现有不少人拷贝到他们的项目中使用,CRC-16 也有少许拷贝。这算法是能用啊,可是能够有更好的实现。

当时我搜了下, Github 上有 2353 个项目用到了 Redis 的 CRC-64 实现,325 个项目用到了 Redis 的 CRC-16 实现。

这位老哥为他们感到可惜,他说:唉,他们值得更好的CRC算法呀。

紧接着老哥开启了第一波输出。

简单翻一下

  • Redis 决定忽略吞吐量和延迟方面的提升,由于他们更喜欢像1999年那样编码

  • 代价就是他们过期的传统设计慢了40倍。你们干得漂亮啊。(不知道这老哥是笔误仍是故意把4倍在开头写成40倍的,我就揣测下:),那个outdated legacy design超连接跳过去就是标注着 antirez 写的 CRC-64 的实现,直接粗暴,我很喜欢)。

  • ,若是如今有人写了一个代替 redis 和 memcached 的实现就行了啊。

这正面刚了一波,可是还没完,老哥紧接着开始放招。

What’s Wrong

他先说了下 CRC 本质上是没法并行的,由于下一次迭代的值取决于前一次迭代。而后又指出了 Redis 中的哪几个地方使用到 CRC。

CRC-64 用在了三个地方:

  • 在跨实例迁移键时添加校验和,(并验证上述校验和)

  • 为 RDB 输出添加一个校验和,用于复制和持久化(是可选项,可经过配置禁用,由于性能低)

  • 用于内存测试

CRC-16 用在了一个地方:

  • 做为集群中分配集群槽的键的散列函数

mattsta 接着放招:

简单翻译下:这 Redis 的实现极其简单(这个 extremely 单词我非常喜欢),就是一个简单的查表法,而后循环一个字节一个字节比过去,时间复杂度是O(N)。

先踩一波,而后抛出好的解决办法,啧啧老哥深谙此道

What’s Better

简单翻译下:我在网上乱翻了一番,想看看其余人是如何实现 CRC-64 的。可是大部分是拷贝 Redis 的(哎,恨其不争)。

而后 mattsta 发现了 stackoverflow 有个叫 Mark 的哥们本身写了个高速版 CRC-64 实现,他将 Mark 的实现和 Redis 的实现进行了对比,发现 Mark 的版本比 Redis 版本快了400% ,分别是1.6 GB/s 和400 MB/s。

可是!Mark 和 Redis 实现的 CRC-64 属于不一样的版本,没错 CRC-64 算法有不少变体

因而 mattsta 把枪口暂时对准了 CRC,没错他喷了一波 CRC 算法自己。

原谅个人孤陋寡闻,我一直觉得 pretty 和 girl 比较配,原来还能用来和 awful搭,我学到了,嗯。pretty fu..。

而后 mattsta 说咱们不能直接用 Mark 的实现,可是咱们能够看看是 Mark 的实现。

What’s Improved

mattsta 先展现了下 Redis 的实现,就是每一个字节循环操做,而后查表。

而后他又贴出了快版本的实现。

能够看到也是查表法,不过一次不是处理 1 个字节而是 8 个字节。

mattsta 用了一个我以为很搞笑的形容 tiger prepping to devour your entrails 。这新版本的代码看起来像一只老虎准备吃掉你的内脏!再坚持一下子!这仍是个超连接,我点过去真的是一只老虎图!

哈哈哈,容我先笑一下子,这老哥真有意思!

而后他说强调了下这个算法快的缘由,就是利用多维数组查表,每次循环能够一次处理 8 个字节。

所以对于 500 MB 的输入来讲 Redis 的版本须要 5 亿次循环,而新版本只要 6250 万次。

这个slicing-by-8是英特尔公司的研究人员于 2006 年发布,也就是说利用 8 个查找表,每一个查找表包含另外一个 256 字节的查找表来实现一次能处理 8 个字节的 CRC-64 算法。

简单地说,就是空间换时间的操做。

因而,这位老哥找到了灵感,他要本身实现一个和 Redis 匹配的 CRC 算法。

Result

简单翻译下:就是通过一年的努力,mattsta 终于作出符合 Redis 匹配的 CRC-64 算法快速版本,并且做为额外奖励,也能用在CRC-16上噢,而且能够摒弃老版本源代码中一堆静态查找表。

能够在须要的时候再动态生成,而不是老是拖着它们使代码膨胀。

咱们来看看老版本的代码确实有一堆,我截了一小段。

也就是 mattsta 写的快速 CRC 实现版本-crcspeed,不只速度更快,并且清减了代码。

而后  mattsta 来了一波不要相信我说的话,我们来让数听说话(傲娇.jpg)。

经过 mattsta 本身的笔记本测出的 crcspeed 从耗时、吞吐量和每字节所需CPU周期三方面来看都优于 Redis 的实现。

Real-World Impact

mattsta 又指出 crcspeed 能给Redis 带来啥呢?

简单翻译下:Redis 在生成 RDB 的时候会 fork 出子进程,所以采用的是写时复制,因此内存的增加取决于写入的负载,那么快速的结束 RDB 退出 fork 的子进程,用在 COW 的内存就会更少,而生成 RDB 的时候又用到了 CRC-64 做为校验,那么 CRC-64 校验越快,RDB 生成的就越快,用于 COW 复制而使用的内存就越少。

而且

CRC-16来映射槽的时候,若是用户正在作一些古怪的事情,好比使用 300 MB 的键,那么快速的 CRC-16 能够减小 400% 的集群槽分配开销!

这个If users are doing wacky things,我非常赞同,无论公司内部仍是公司外部,你所实现的接口都要怀揣着使用者会以最大的恶意去调用来预防。

mattsta 说这是一个有效和高效的多方面的共赢

我本觉得这位老哥的文章写到这里就差很少了,然而并无。

Minor Notes

能够看出 mattsta 是不想造轮子的,可是实在是没有轮子啊!因而他只能本身实现一个,这是个新轮子!

Resources Consulted

而后他列出了他所参考的一些资源,他首先感谢了「A PAINLESS GUIDE TO CRC ERROR DETECTION ALGORITHMS」这篇文章。

让咱们学一下感谢参考资料的正确姿式。

看到没,这才是感谢的正确姿式!咱们来看下它所感谢的文章的样子。

老哥说:有一说一,确实,纯路人,身为一个 txt,编写良好、格式良好,有趣。在风格、布局和语气的全部方面都通过了专家的深思熟虑。

夸了一番以后, mattsta 以为还不够,还得加一点本身的想法。

简单翻一下:在某种程度上,互联网已经失去了保存写的好的、格式良好的、信息丰富的指南以及常见问题的解答和平易近人的研究论文的能力。咱们应该努力把那部分世界夺回来。这种损失该归咎于什么呢?对风格的过度依赖?CSS?仍是 JavaScript?PHP?。

世界上最好的语言警告!

看到没,这才是感谢的正确打开方式,我学废了。mattsta 老哥追求纯干货,说,别给我整一些花里胡哨的!

并且这篇文章还让 mattsta 确信他没有能力实现一个 CRC-64 算法,所以实际上他是依靠 pycrc 来实现的。

而后这位老哥又说 yahyahyah,linux kernel 也是这样用的,yahyahyah。

老哥还找到一篇介绍 slicing by 8 的放在英特尔网上的论文,不过如今已经没了。因此先嘲讽了一波英特尔,还顺带还吐槽了傻*谷歌代码,每次打开这个 pdf 都自动下载,而不是在线浏览。

暴躁老哥,真的对我胃口哈哈哈。

mattsta 老哥写的文章还没结束,后面是关于实现的细节,我就再也不继续翻译下去了,文末会放原文连接,有兴趣的朋友到时候能够看看。

咱们再来看身为 Redis Contributor 为什么 mattsta 会写这一篇文章?不该该提 pr 直接解决吗?难道这算法有什么致命之处使得 Antirez 不接受?

咱们来追踪一下!

追踪事情的前因后果

首先这篇文章写于 2014-12-22

mattsta 在 2013-12 开始了对 redis.io 的第一次提交。

随后 mattsta 就开始了对 Redis 的输出,在 2014-04-01,提出了相关的 issue,而且附上了本身的对比。

提出的 issue 没有受到团队成员的响应,寂寞如雪,只有一位金毛小哥,为其打 call,这个issue 此时仍是 open 的。

而后在当年,2014-11-23,mattsta 建立了 crcspeed 库,而且提交了实现。

并在 2014-12-22 提交了pr,居然和写文章是同一天!并且是先写了文章再提的 pr。

一开始我觉得是提了 pr 迟迟得不到采纳,而后才一怒之下写的文章。

能够看到隔了一天有团队人员回应了,他说:我不知道这是否会被合并(我认为它应该) ,可是,该死的!这是一个伟大的提高!牛皮克拉斯!

2014-12-23,mattsta 又对 pr 作了一些补充说明,然而 antirez 仍是没有回应。

直到 2015-01-10,mattsta 对 pr 又作了一波更新。

终于在 2015-02-25 号,等到了 antirez 的回复。

antirez 说,颇有意思,可是我想看到固定的大于 5% 收益的可重现的测试用例,即便是综合性的测试也没事,只要明显的能在 Redis 中反映出来便可,我相信从集群的 crc16 入手测试能很简单的证实效果,如今对于合并更快的实现不是很急,不过若是有一天你完成了这样的测试,我将会很感激。

而后给这个 pr 加了个标 review - and - merge

还加了个ps: 一般来讲证实一个东西的性能提高是很重要啊,我在这里作了个例外,由于我看它单独的测试确实快了不少,我相信即便 Redis 没有使用上这个经验,可是早晚咱们也会受益于它。

简单地说就是:mattsta 啊,你得搞个 Redis 相关的测试来证实它真的使得 Redis 性能提高了啊,这样我才能合并啊,不过我作个例外,是承认你这个的,给你打个标!(可是没有真正的合并)。

也就是说 antirez 实际上是承认 mattsta 的实现的,可是 mattsta 没有给出和 Redis 相关的测试,因此还不能合并这个 pr。

这个 pr 就到这里过了,再也没有更新,也仍是 Open 的。

mattsta 也没有继续说啥,对 Redis 输出到 2015 年初以后就再也不输出了。

而 CRC-16 到如今还使用的是老版本,CRC-64 是 antirez 在时隔六年的2020-04-28作的修改,使用的就是 mattsta 的crcspeed。

再回头看

能够到 mattsta 在 2014-04-01 就提了 issue,而后没有任何回应的状况下本身研究,找了许多资料,最后实现了 crcspeed,也肝出了一篇文章,以后在同一天提了 PR,而后过了近两个月的时间获得 antirez 的回复,由其没有关于 Redis 的实质上的测试,所以不给合并,但被给予确定。

我我的猜想 mattsta 可能仍是有点生气的,这么一个通用的东西,我都给了横向对比测试了!这原理我也分析的这么清楚了!这明摆着确定是 ok 的,你还要我测试啥!不合并拉倒!(再次傲娇.jpg)。

而 antirez 所在的角度不同,他是 Redis 的亲爸爸。你说的没错,我承认你,可是你得拿出实质性的证实给我看看你帮个人 Redis 提高了多少,即便我知道这玩意确定是 ok 的。

其实双方我都能理解,所处角色不一样,我只能说若是我是 mattsta 我可能会在内心骂 antirez,若是我是 antirez 我以为就该这样。

来张 mattsta 老哥的靓照,哈哈头发不少!

这篇文章讲述的就是这么个事儿。

我就是带着八卦之心来看为什么身为 Contributor 的 mattsta 提的明显正确的 pr 没有被 merge,至于什么 CRC 的我不关心哈哈哈哈。

mattsta 老哥的钻研之心值得咱们学习,固然还有他那搞笑的形容和五彩斑斓的感谢。

欢迎关注个人公众号【yes的练级攻略】,更多硬核文章等你来读。

参考连接

  • https://matt.sh/redis-crcspeed

  • https://github.com/mattsta?tab=repositories


我是 yes,从一点点到亿点点,咱们下篇见

往期推荐:

本文分享自微信公众号 - yes的练级攻略(yes_java)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。

相关文章
相关标签/搜索