将它使用的代码和只读数据放在彼此旁边是一个好主意吗?

Bee*_*ope 6 optimization performance x86 assembly lookup-tables

在为另一个问题编写查询表相关的答案时,我想起了一些我一直想知道的事情:在函数旁边找到函数所需的少量非代码数据是否明智,而不是传统的方法把它放在另一个部分?

假设您有一个小函数,它使用一个小的只读查找表.通常的方法似乎是将查找表定位在数据部分中,例如.rodata通常将其放置在距函数本身的文本一定距离处.

例如,一个使用16项LUT计算字节奇偶校验的简单函数:

GLOBAL parity

SECTION .text
parity:
  mov   eax, edi
  shr   edi, 4
  xor   eax, edi
  and   eax, 15
  movzx eax, byte [lut + eax]
  ret

SECTION .rodata
lut:
db 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1
Run Code Online (Sandbox Code Playgroud)

现在,该方法恰好是大约16个字节的代码,查找表也是16个字节.因此,它们可以很容易地适应相同的缓存行.这看起来像是一个双赢 - lut 总是在函数中访问,并且只能由函数访问,因此我们可以通过放置代码和代码来降低调用此函数的成本.数据并排:

GLOBAL parity

SECTION .text
parity:
  mov   eax, edi
  shr   edi, 4
  xor   eax, edi
  and   eax, 15
  movzx eax, byte [lut + eax]
  ret

lut:
db 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1
Run Code Online (Sandbox Code Playgroud)

它与之前的相同,只是.text紧跟在函数1之后的部分中的表.

据我所知,大多数架构/可执行格式通常允许它,所以它有什么问题?

例如,CPU的指令获取机制是否可以通过获取超出ret我的示例并尝试将查找表解释为(无意义)指令而混淆?


1请注意,我故意将表放在代码之后,因为首先需要代码,但是根据关键字首先可能并不重要,无论如何交互都不清楚.

Bee*_*ope 1

我能想到的一件事是,虽然它很好地利用了 L2 和更高级别的缓存,但在分割1的架构上,L1 数据和指令缓存的优势并不明显,因为会出现该行在L1D和L1I缓存中,都浪费了一些空间(例如,浪费了L1I中用于缓存查找表的空间)。


1即,今天几乎所有主要架构 - 几乎每个人都经过修改哈佛......


归档时间:

查看次数:

131 次

最近记录:

8 年,1 月 前