mzh/blog

龙芯 & Golang!

龙芯,不少人都比较陌生,见过的就更少了。

龙芯活着,还在云时代的2019年拯救了一下MIPS这棵34年的枯树。

一点背景故事

事情还要从去18年底,Go的MIPS架构的构建机(builder)集体下线说起。 这里顺便说一下builder的功能,其实就是Go在验证各个平台兼容性用的机器, 主要是各大公司和志愿者捐赠的,绝大部分是国内开发者很少见的PowerPC、ARM、S390X这类ISA, 操作系统更多,具体可以看看build.golang.org

19年4月时, Go核心团队的Bradfitz发现ImgTec负责的mips、 mipsle、mips64le、mips64(大小端/32/64位)四种机型的builder已经下线半年多了, 根据Go的平台支持条件要求,任何一个架构+操作系统组合都需要有验证机型,否则就要踢出Go的支持列表。 所以Bradfitz发邮件给ImgTec维护者, 收到的只有查无此人的自动回复, 他觉得是这哥们离职的原因, 但实际上是2017年底的时候MIPS背后的ImgTec把MIPS卖了……这些builder竟然还多撑了一年。

大概同时,我从国内Go开发大牛Ben那里获得了一台龙芯3A1500, 这台机器是龙芯团队希望能有人维护Go MIPS,毕竟Go已经是云时代的C了, 不少服务是运行在Go的runtime上的, 另一方面docker已经成了事实标准,龙芯云也是基于docker的。 所以把机器寄给了Ben,但Ben忙于工作,我又喜欢多管闲事性能优化……于是我愉快地收下了这台3A1500。

Loongson 3A1500

不过这台机器可能因为暴力的快递摔坏了,一直点不亮,我只好退给了Ben, 从龙梦公司通过古老的转账汇款方式买了一台3A3000。

就在我搜索MIPS可优化点的时候,发现了MIPS要被踢出去的帖子, 所以我回帖说可以让我的这台龙芯替代ImgTec做builder。 经过自己

  1. 编译4.19内核
  2. 申请密钥
  3. 改Go build项目代码
  4. 艰难地设置网络之后

龙芯的builder: linux-mipsle-mengzhuo终于上线了。(名字不是我挑的) 具体的申请builder的wiki可以看这里

龙芯Go现状

毕竟3A3000是16年的CPU,加上是1.5Ghz/8KB L3 Cache/28nm 制程自然也不能和Intel、AMD比。

其他问题嘛……

Unalign access penalty,没有Hyper Threading,SIMD支持也几乎没有。 就算这么多缺陷,龙芯也是目前市面上零售方式能买到的唯一的MIPS架构的CPU了, MIPS新东家Wave computing是搞AI的,不知道买MIPS来干嘛,架构不发展,只是 开源了r6架构,但是看官网制程还是28nm的…… 所以可以说龙芯是MIPS,这个1985年就出现的架构最后脸面了(欢迎打脸)。

大家可以看看龙芯的cpuinfo哈

Linux ls-3a3k 4.19.53+ #1 SMP PREEMPT Wed Jul 10 15:12:52 UTC 2019 mips64 mips64 mips64 GNU/Linux
system type             : generic-loongson-machine
machine                 : loongson,generic
processor               : 0
cpu model               : Loongson-3 V0.13  FPU V0.1
model name              : Loongson-3A R3 (Loongson-3A3000) @ 1450MHz
CPU MHz                 : 1450.00
BogoMIPS                : 2887.52
wait instruction        : yes
microsecond timers      : yes
tlb_entries             : 1088
extra interrupt vector  : no
hardware watchpoint     : yes, count: 0, address/irw mask: []
isa                     : mips1 mips2 mips3 mips4 mips5 mips32r1 mips32r2 mips64r1 mips64r2
ASEs implemented        : dsp dsp2 vz
shadow register sets    : 1
kscratch registers      : 6
package                 : 0
core                    : 0
VCED exceptions         : not available
VCEI exceptions         : not available

说到Go在龙芯上的实际性能,通过观察,大概比PPC64、ARM这些builder快点, Go所有源代码编译+测试一次大概要耗时25分钟左右。

不过我发现不少性能关键路径上的代码甚至都没按一台正常的64位机器写, 而是明显的“能用”的状态,可能和Minux和Cherry移植的时候先让MIPS架构能跑起来有关。 更要命的是,Go不知道为啥,最小版本要求竟然是MIPS III(1993年发布), 想在Go上用常见的优化指令,比如count leading zero(CLZ), conditional move (CMOV.CON), BSWAP ( ROR DSHB ) prefech统统都不行……

不过我还是提交了一些优化的CL,平时还要忙无聊的工作,精力有限,目前只有:

未来的展望

如果我能多提交一些bytealg,syscall,SSA相关优化之后应该就能更快点, 就算没有向量优化,硬件指令集,至少总体性能也应该能提升30%左右。 国内我知道在优化的人也就Xenon一个了,如果你也有兴趣搞龙芯Go优化的欢迎联系我。

有可能的话,我也想尽可能地推动核心团队提升Go MIPS的版本,MIPS III 实在是太老了。

同时我也希望各位开发们能借着“国产化”的春风,在工作中多用国产CPU,帮助提升性能, 丰富一下生态,多影响一下上游。至少不是做个冷嘲热讽的键盘侠。顺便祈祷MIPS的新东家Wave computing 不要再搞什么幺蛾子把MIPS真的送进博物馆里了。

最后附上这台builder的样子,毕竟应该是国内第一台在Go项目里的服务器。

LS 3A3K

Go Contributor Summit 2019参会记

今年有幸去美国圣地亚哥参加了Go Contributor Summit 2019,主要是Go的代码贡献者和核心开发团队在一起讨论关于Go相关的话题。

紧跟着的GopherCon也是第一次换城市到了圣地亚哥,还在中途岛号航母(USS Midway)上搞了欢迎party,真是毕生难忘了。

破冰环节

刚进门,就发现Russ Cox站在门口,跟他打了打招呼,顺便吐槽了一番他的范型设计在国内引起的巨大讨论,不过大佬很快就被其他人抓去讨论别的话题了。 这时热情的Joe Tsai(Protobuf维护者)跟我打招呼,然后给我介绍他认识的Google同事, 但毕竟是大型网友见面会,除了Github上见过照片的,基本都名字对不上脸,大家很腼腆,当然也有像Möhrmann(Google SRE)热心地跑来跑去介绍自己的人。

这么尴尬怎么玩?没事,Google组织方已经想好了,接下来就是破冰环节。

图1

图1: 会议日程表,左spf13 右rsc

其实很简单,就是一桌人按生日的月排序,小的介绍大的。介绍内容除了名字公司以外还要介绍个自己的fun fact,这下真难倒我了, 幸好我的介绍人Julie Qiu(Go文档维护)给我想到了一个,坐了14个小时的飞机第一次来美国(虽然我没get到fun是啥…… 轮到我介绍了,正好介绍的是Joe,他的fun fact是一家3姐弟都在Google上班,又一次没get到。 最让我震惊的是Cherry Zhang(ARM/MIPS 维护者)是个妹子!主要是看代码和说话风格,感觉都是个男的,轮到她介绍的时候了才发现是个妹子。

因为是贡献者大会,所以主办方希望大家自选话题并分组讨论各自感兴趣的话题。 大家先头脑风暴,讲出了自己关心的话题,然后经过投票之后,上午的话题分成以下4组:

  1. 编译器
  2. 范型和错误处理
  3. 社区相关问题,发展和规划
  4. 工具相关问题,proxy mod这类

讨论环节

每个组讨论都要选一个负责人来组织讨论,防止一言堂,然后有个两个志愿者来记录一下大家说的话题。 我参加了编译器组,见识到了Keith Randell(编译器维护),Austin Clements(编译器组负责人)和其他大牛们讨论的问题:

后来的事实证明,我适合去社区相关组,毕竟我在组里几乎没发言,而我又是唯一一个从中国来的开发者,而Go的核心团队相当关心Go在中国的发展。

图2

图2:社区组讨论如何提Bug和proposal,大佬云集

杂事体会

国外开会真心体验到了什么叫人性化,每40分钟就停下来休息,然后每2次休息就有茶歇。这样开会一点都不累。 由于是Contributor Summit,所以是管午饭的,吃的嘛……真不敢恭维,都是三明治+沙拉。 在午饭时,Joe Tsai跟我聊了聊中国用户时怎么使用文档的,我跟他说大家基本都能看懂英语的或者代码, 所以一般不用操心中国用户看文档,当然也聊了protobuf维护的坑啊,中国美国社会、政治问题之类的。 期间还有些其他开发者看到我衣服上的公司名字,跑过来问某鹅怎么用Go啊,中国开发者怎么盗用weather.com的API的这类的事。

还有就是我真心不建议英语不好的朋友来参加,我雅思听力6.5、平时基本能不看字幕看美剧也扛不住各种口音的攻击,特别是8小时的大型听力考试+技术能力考试+时差, 脑子基本转不动了,而且很多俚语和文化确实需要浸入式地学习才能了解,出现了几次全场大笑,我还很懵逼的状态。

总体还是收获特别大,不仅是跟一堆核心开发面对面,而且还认识了不少好朋友哈~

沈阳美签记

水一篇。

念念不忘,必有回响。

这真是对我一直想去美国看看的真实写照。 本来Gopher Contributor Summit并没有邀请我,但是Ben不去参加,把名额转给我了。 我想着我的贡献度远比他低,只是10个左右的CL而已,没想到询问了主办方后2周后,突然就收到了同意参加的邮件。 6月22号填完查户口般的DS160后发现,我应该去的广州领事馆面签预约时间竟然到了一个月以后,可是这个会就在7月23号开了啊。这样肯定是不行的, 四处找啊找,上海、北京也是1个多月,最后在预约栏里发现了“沈阳”这个选项。点进去一看,只要7天!! 最快就是7月1号办!!赶紧买了机票。

以前都是看文章,说什么东三省衰落,落后啥的,没想到飞到沈阳以后发现,压根不是这么回事! 要啥有啥!地铁还比南宁还多! 真是应验了主席的“没有调查就没有发言权”。 坐了出租车到订的“公寓式酒店”,发现真是公寓改的酒店……因为这酒店就在一栋40多层高的高档居民楼里。 不过到的时候凌晨1点了,所以也没看清城市啥样,虽然确实不能和深圳的灯火通明比,但也不算差了。

因为看网上攻略美签不能带手机啥的,只能拿个透明资料袋,又懒得取现金来打车,所以订的酒店就隔了20分钟走路时间。第二天醒了之后时间还早,所以走着去看了看,9点就已经好多人呆在领事馆外头等叫号了。 不过领事馆安保太严了,不让拍照,我前面有个人拍了一张,就被保安追过去,站在身后要求他删掉。

走回酒店的路上发现有个饺子馆,想着午饭就吃个正宗东北酸菜饺子好了。 早饭就匆匆吃了个面包,然后等到时间去做面签。

到了以后,坐在领事馆对面的椅子上,边上的一个妈妈跟我攀谈了起来,问我为啥去,我说开个会,我礼貌性地反问她,她很自豪地说是送女儿上普渡大学。听着很野鸡,但回头一查……常青藤呵呵 就在尴尬的时候,幸好边上一对夫妇听见她也是送小孩上学,就跟她聊了起来,自曝一年要花60万学杂费,好可怕。

轮到我了,跟着前面的人做,拿资料,录指纹。 真的面签了,一个白人大叔,中文问我为啥去美国,我答开会。 然后跟网上攻略一样,他直接英语问工作在哪里,多久了,bla bla,特意问了我是不是做AI相关的开发,还没问我要全家福和资产证明时,就把我的护照收走了,丢了张通过的红纸出来……果然是大厂牌子响么。

离晚上的飞机时间还早,我就吃了个酸菜饺子做午饭,嗯……跟我们家楼下的东北人夫妇开的店里卖的是一个味道,就是皮得多,吃起来有嚼劲。

东北酸菜饺子

然后在浑河边上转了转,名不副实,这河清着呢,沈阳还有个比深圳好的一个地方就是,特别凉快~ 最后水几张沈阳的照片

沈阳电视塔

沈阳电视塔

好像是图书馆

好像是图书馆

电车

电车

NABHash介绍及原理

项目地址

github.com/mengzhuo/nabhash

什么是NABHash?

NABHash 是一种 超级快的 基于AES的非密码学安全哈希算法(Hash Algorithm)

有以下特点:

性能对比图

NABHash性能对比图

可以看到,在65536字节时,NABHash的速度是xxhash、murmur3这些常见快速哈希算法的10倍左右。

为啥可以这么快?

因为“简约”。

一般哈希算法是使用加法、异或、移位完成的(Shift Add XOR) 而哈希算法为了满足雪崩性和分布性,会多计算几轮,导致就算使用SIMD技术,也无可避免性能下降。

这就是NABHash优化的地方,NABHash对于一个16字节数据块,在Intel平台上仅仅使用了1个指令:

AESENC

即AES加密。而选择AES的主要原因是,现在主流的CPU都支持了AES硬件加速(ARM、PPC),而SHA系列虽然有硬件支持,但指令复杂,往往一个数据块还需要多个指令才能完成计算。

主要流程可见下图: NABHash流程图

相当于用数据不停地加密初始向量值(0x5A827999 0x6ED9EBA1),最终得到一个哈希值。

可能有些小朋友就会问了,为啥初始向量是这个值,是随机生成的么?不,这是sha1的初始key值。

最后,为啥叫NABHash

因为这是Non-Crypto-Safe AES Based Hash,简写NABHash,基于AES的非密码学安全哈希算法。

Go开启UDP SO_REUSEPORT支持

Linux的bind REUSEPORT选项是为了充分利用多核CPU,允许不同进程绑定同一个地址+端口号,避免出现仅仅有一个核在处理数据包,其他核闲着的问题。

启用前后的对比图(借用nginx的 reuseport before after

不过这么好的特性,Go默认是不开启的,需要自己手动调用net.ListenConfig(Go1.11以上)

一看API就懵了,咋是一个interface……

type ListenConfig struct {
        // If Control is not nil, it is called after creating the network
        // connection but before binding it to the operating system.
        //
        // Network and address parameters passed to Control method are not
        // necessarily the ones passed to Listen. For example, passing "tcp" to
        // Listen will cause the Control function to be called with "tcp4" or "tcp6".
        Control func(network, address string, c syscall.RawConn) error
}

这是因为Go有近乎强迫症的向下兼容|backward compatible,不会在已有的接口上添加选项,所以肯定是新开一个API了。但是这个API跟常见POSIX API差太远了,仔细阅读了文档才摸索出下面这个例子:

import (
	"net"
	"syscall"
	"golang.org/x/sys/unix"
)

func newUDPListener(address string) (conn *net.UDPConn, err error) {

	// 构造Control闭包
	cfgFn := func(network, address string, conn syscall.RawConn) (err error) {
		// 构造fd控制函数
		fn := func(fd uintptr) {
			// 设置REUSEPORT
			err = syscall.SetsockoptInt(int(fd),
				syscall.SOL_SOCKET,
				unix.SO_REUSEPORT, 1)
			if err != nil {
				return
			}
		}

		if err = conn.Control(fn); err != nil {
			return
		}
		return
	}

	lc := net.ListenConfig{Control: cfgFn}
	lp, err := lc.ListenPacket(context.Background(), "udp", address)
	if err != nil {
		return
	}
	conn = lp.(*net.UDPConn)
	return
}

效果方面,我的GoNTPD项目一到高峰期就没办法响应太多的请求,导致UDP recv buf 过高,图中的绿线是网卡吞吐量,红线就是UDP recv buf的使用量,越高就说明无法立即处理的请求越多,可以看到更新后,就没有出现UDP recv buf堆积的情况了。

UDP recv buf VS throughput