// 450 millisecondsfn common_prefix(xs: &[u8], ys: &[u8]) -> usize {let chunk_size = 16;let mut result = 0;'outer: for (xs_chunk, ys_chunk) inzip(xs.chunks_exact(chunk_size), ys.chunks_exact(chunk_size)){for (x, y) in zip(xs_chunk, ys_chunk) {if x != y { break 'outer; }result += 1}}for (x, y) in zip(&xs[result..], &ys[result..]) {if x != y { break; }result += 1}result}【你可以信任由编译器优化的代码吗?】其实,上述代码在速度上的提升是远远不够的 。具体来说,SIMD需要以相同的方式,并行处理块中的所有值 。在上述代码中,我们用到了一个break 。这意味着第n对字节的处理,取决于第n-1对 。我们可以通过禁用短路(short-circuiting),来检查整个字节块是否匹配 。当然,我们并不关心具体哪个特定字节出现了不匹配:
// 80 millisecondsfn common_prefix3(xs: &[u8], ys: &[u8]) -> usize {let chunk_size = 16;let mut result = 0;for (xs_chunk, ys_chunk) inzip(xs.chunks_exact(chunk_size), ys.chunks_exact(chunk_size)){let mut chunk_equal: bool = true;for (x, y) in zip(xs_chunk, ys_chunk) {// NB: &, unlike &&, doesn't short-circuit.chunk_equal = chunk_equal & (x == y);}if !chunk_equal { break; }result += chunk_size;}for (x, y) in zip(&xs[result..], &ys[result..]) {if x != y { break; }result += 1}result}至此,矢量化已成功开始,而且几乎减少了一个数量级的运行时间 。我们现在可以使用迭代器来进行压缩了 。
// 80 millisecondsfn common_prefix5(xs: &[u8], ys: &[u8]) -> usize {let chunk_size = 16;let off =zip(xs.chunks_exact(chunk_size), ys.chunks_exact(chunk_size)).take_while(|(xs_chunk, ys_chunk)| xs_chunk == ys_chunk).count() * chunk_size;off + zip(&xs[off..], &ys[off..]).take_while(|(x, y)| x == y).count()}显然,此时的代码已与我们开始时有了显著不同 。可见,我们不应盲目依赖编译器的优化,而需要知道在何种情况下进行特定优化,以触发它们编写代码的方式 。例如,对于SIMD而言,我们需要根据处理元素块来表达算法 。而且在每个块中,我们应确保没有分支,让所有元素都能以相同的方式处理 。
原文链接:https://matklad.github.io/2023/04/09/can-you-trust-a-compiler-to-optimize-your-code.html
译者介绍陈峻 (Julian Chen),51CTO社区编辑,具有十多年的IT项目实施经验,善于对内外部资源与风险实施管控,专注传播网络与信息安全知识与经验 。
推荐阅读
- 图解DHCP协议,搞懂你的电脑/手机如何自动获取IP的
- 我们一起聊聊MySQL中的游标,你学会了吗?
- 车辆免检后,接下来需要做什么?手把手教你→
- 书房这样设计,让你的生活更有品质
- 养一台比亚迪海豹一年需要多少钱?算出来你还会考虑燃油车吗?
- 被行政拘留过还可以开无犯罪证明吗?
- 快充、闪充、无线充?一字之差相差千里!别让你的手机提前报废
- 微信wxid被泄露:如何保护自己的账号安全?
- 你的抖音还有一直没流量吗?干货教你玩转抖音精髓!
- 抖音feed流广告与信息流广告区别有哪些?
