昨晚散步的随机幻想:
我终于想试试给 bpf 版本的 tcpdump 做一下 benchmark 和 tcpdump 对比一下性能,但是很快发现 bpf 版本(简称 etcpdump(被人吐槽取名太烂))里充满了没必要的指令,比如:
; if (CONFIG.skb_track)
22: movabsq $-118460502762736, %rdi
2c: movzbq (%rdi), %rdi
; if (CONFIG.skb_track)
31: testq %rdi, %rdi
34: je 0x61
36: movq %rbp, %rsi
;
39: addq $-16, %rsi
; if (bpf_map_lookup_elem(&skb_addresses, &skb_addr))
3d: movabsq $-118459148791808, %rdi
47: movq -24(%rbp), %rax
4e: callq 0xffffffffde9b37e4
53: testq %rax, %rax
56: je 0x5c
58: addq $56, %rax
这一分支由 CONFIG.skb_track 这个注入的配置来决定是否跳入,但是从性能考虑,我们连这个 if (CONFIG.skb_track) 条件判断的指令都最好删掉,而这是完全可以做到的,因为相比传统可执行的 ELF,运行 bpf 是要先在用户态读 bpf 字节码,这多的一层就足以让我们在用户态根据运行时配置裁剪 bpf 字节码,删除死分支。
这样一想很多新东西就出现了,比如有些语言喜欢玩“依赖注入”,直接在这个阶段(以下称为 loader 阶段)把指令换了就行;而 linker 做的事情,粗糙来看也是替换一些 ELF 里的字节,只是我们这里把这个概念扩展成更复杂的操作,不仅是修改 plt/got,而是根据用户传入的命令行参数+环境变量动态修改 .text。
这确实有点极端了,也就 bpf 这种极端性能主义才会绞尽脑汁做这些优化。
我终于想试试给 bpf 版本的 tcpdump 做一下 benchmark 和 tcpdump 对比一下性能,但是很快发现 bpf 版本(简称 etcpdump(被人吐槽取名太烂))里充满了没必要的指令,比如:
; if (CONFIG.skb_track)
22: movabsq $-118460502762736, %rdi
2c: movzbq (%rdi), %rdi
; if (CONFIG.skb_track)
31: testq %rdi, %rdi
34: je 0x61
36: movq %rbp, %rsi
;
39: addq $-16, %rsi
; if (bpf_map_lookup_elem(&skb_addresses, &skb_addr))
3d: movabsq $-118459148791808, %rdi
47: movq -24(%rbp), %rax
4e: callq 0xffffffffde9b37e4
53: testq %rax, %rax
56: je 0x5c
58: addq $56, %rax
这一分支由 CONFIG.skb_track 这个注入的配置来决定是否跳入,但是从性能考虑,我们连这个 if (CONFIG.skb_track) 条件判断的指令都最好删掉,而这是完全可以做到的,因为相比传统可执行的 ELF,运行 bpf 是要先在用户态读 bpf 字节码,这多的一层就足以让我们在用户态根据运行时配置裁剪 bpf 字节码,删除死分支。
这样一想很多新东西就出现了,比如有些语言喜欢玩“依赖注入”,直接在这个阶段(以下称为 loader 阶段)把指令换了就行;而 linker 做的事情,粗糙来看也是替换一些 ELF 里的字节,只是我们这里把这个概念扩展成更复杂的操作,不仅是修改 plt/got,而是根据用户传入的命令行参数+环境变量动态修改 .text。
这确实有点极端了,也就 bpf 这种极端性能主义才会绞尽脑汁做这些优化。