在第二期极客挑战赛的ARM64赛道中,“HOOCCOOH”同学以480字节的成绩摘得赛道冠军。值得一提的是,这位同学为了参加arm64赛道,几乎是现学现用aarch64 指令集,瞬间新技能+1
或许,极客挑战赛的乐趣之一就在于通过不断学习、不断精钻细研,满足对技术的无穷探索欲。
下面由他带来ARM64赛道的解题思路和心得分享,也欢迎小伙伴们在文末留言,分享自己的解题报告链接。(原赛题传送门:腾讯极客挑战赛丨全世界最最最小的程序,等你来battle!)
看到这个题目,立刻想到那个输出自己 MD5 的 gif ,碰撞啊!于是弄了一个碰撞器,撞出 128 对冲突数据串起来,然后把结果 128bit encode 进去让程序通过判断识别得到最终答案。由于目前个人电脑可以计算的方案需要两个 block 来做一次碰撞,通过枚举弄到 32 个前导零,程序仍需要存 (128-32)*128 byte = 12 KB 。巨大无比,当场垫底,于是立刻换思路。
首先按 wiki 抄一个 MD5 实现,千万不要抄比赛样例,那个把计算全部 unroll 了,体积上毫无优势。然后在基本代码上进行一些通用优化,大部分和 x86 思路差异不大,不细展开了。
K[]
表,使用 sin 生成。x86(x87) 有 FSIN 指令可以直接用,但 aarch64 就没办法了,直接用个循环暴力求泰勒展开即可。我们需要 2^-32 的精度,搞不了奇技淫巧,循环次数要给够。记得之前需要把 x 缩放到 x % PI ,不然精度必挂;可使用循环减或除-取整-乘,指令数相同。S[]
表,发现每 16 个一组中为四个重复,可以去掉重复,然后稍微推导一下,通过 S[i >> 2 & 0b1100 | i & 0b11]
索引即可。i
的循环内的四种情况,我们不需要先比较跳转进去再跳转到结尾;因为我们都是设置 F
和 g
,于是可以:[0, 16)
轮的 ~B & D
和 [48, 64)
轮的 B | ~D
按位相反。反正 aarch64 寄存器多,而且可以位运算同时取反,这里可以省几条指令。orn
, eon
, bic
,别再额外 not
了。ldr
/str
有同时操作两个寄存器的版本 ldp
/stp
,一条指令 ldp x0, x1, [x8], #16
即可读入整个 16 byte state 并且步进指针。adr
。x0 = B << 32 | A, x1 = D << 32 | C
A-C
, B-D
之间的运算,直接作为 64bit 运算即可,extr
简化成三条指令。tbz
/tbnz
可以代替几乎所有 cmp
+ b
write
传参,加法不考虑进位通过随机调整程序来撞出一个正确答案等等。反正最后成果是 480 byte 啦,其实还有一定优化空间。
本来咱是 x86_64 选手... 但由于前几名过猛,咱又太菜,最后止步 400 byte 就优化不动了。于是想想之前了解过一点 ARM ,就现学了 aarch64 指令集换赛道跑。不得不说其实 aarch64 定长指令大大降低了心智负担,只需要专心于优化指令数就好,不用纠结各个变长指令长度了!
说起来有点奇怪,似乎 aarch64 赛道相比另两个挺冷门的,提交次数也没那么疯狂(逃
总之非常感谢这次比赛提供的机会吧,也学到了挺多新技术和技巧,非常有趣!
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。