通过 jit-compiling 值立即优化内存负载
Optimize away memory loads by jit-compiling value as immediate
假设我们有以下使用某种抽象编程语言编写的代码片段:
map<string, int> user_settings;
//somewhere at run-time user_settings are populated
for(i=0; i < 1000000000000; i++) {
if (user_settings["do_stuff"] == 1) { do some stuff... }
else { do other stuff }
}
这里的问题是我们会在某些事情的运行时在循环内进行大量加载,即 "runtime constant"(即用户设置的某些值)。有可能通过立即加载提供价值,甚至完全优化掉无用的分支,只使用需要的 then-else-branch 的代码。
是否有 systems/programming languages/jit 编译器执行此类优化?我唯一能找到的相关工作是麻省理工学院某个人的论文:http://groups.csail.mit.edu/cag/rio/josh-meng-thesis.pdf
P.S.我不是要求优化this代码。我知道,您可以预先加载值,该值将被加载到本地寄存器。有时,您无法手动执行这样的 "optimization"。
一个好的编译器优化步骤会可能 从字典中预加载和测试"do stuff" 并将所有迭代一起删除。如果它可以确定没有 side-effect 会改变字典中 "do stuff" 的值,它就会这样做。
如果您正在编写自己的编译器,则有大量的优化技术。当然,它们会有所不同,具体取决于您是在开发命令式语言还是函数式语言,以及随之而来的 tenets
。
例如,在大多数函数式语言中,我确信大多数模块声明的数据类型(列表、集合、映射)的内容不会随时间改变(持久化)。在某些情况下,这些语言支持 side-effect 或可变功能,在这种情况下,您的优化也必须考虑到这一点。
例如在动态链接器中使用的一种巧妙的方法在这里可能会有用:
让你的编译器生成动态代码,这样它就知道是谁调用了它。
这样,如果动态代码发现它接收到的数据永远不会改变,它可以在调用站点重写代码。因此,它会用 PUSH int 1
或其他任何东西代替 CALL get_user_setting "do_stuff"
代替指令,也许还有一些 NOP
来填充 CALL 指令的大小。
动态链接器经常这样做。第一次尝试调用动态链接函数时,它们实际上跳入了加载程序。它加载实际的函数,然后重写 CALL 指令,以便它调用加载的函数而不是例程来加载函数。以后的任何调用都不必再次执行 look-up。 (我在博客上写了这个 here)
假设我们有以下使用某种抽象编程语言编写的代码片段:
map<string, int> user_settings;
//somewhere at run-time user_settings are populated
for(i=0; i < 1000000000000; i++) {
if (user_settings["do_stuff"] == 1) { do some stuff... }
else { do other stuff }
}
这里的问题是我们会在某些事情的运行时在循环内进行大量加载,即 "runtime constant"(即用户设置的某些值)。有可能通过立即加载提供价值,甚至完全优化掉无用的分支,只使用需要的 then-else-branch 的代码。
是否有 systems/programming languages/jit 编译器执行此类优化?我唯一能找到的相关工作是麻省理工学院某个人的论文:http://groups.csail.mit.edu/cag/rio/josh-meng-thesis.pdf
P.S.我不是要求优化this代码。我知道,您可以预先加载值,该值将被加载到本地寄存器。有时,您无法手动执行这样的 "optimization"。
一个好的编译器优化步骤会可能 从字典中预加载和测试"do stuff" 并将所有迭代一起删除。如果它可以确定没有 side-effect 会改变字典中 "do stuff" 的值,它就会这样做。
如果您正在编写自己的编译器,则有大量的优化技术。当然,它们会有所不同,具体取决于您是在开发命令式语言还是函数式语言,以及随之而来的 tenets
。
例如,在大多数函数式语言中,我确信大多数模块声明的数据类型(列表、集合、映射)的内容不会随时间改变(持久化)。在某些情况下,这些语言支持 side-effect 或可变功能,在这种情况下,您的优化也必须考虑到这一点。
例如在动态链接器中使用的一种巧妙的方法在这里可能会有用:
让你的编译器生成动态代码,这样它就知道是谁调用了它。
这样,如果动态代码发现它接收到的数据永远不会改变,它可以在调用站点重写代码。因此,它会用 PUSH int 1
或其他任何东西代替 CALL get_user_setting "do_stuff"
代替指令,也许还有一些 NOP
来填充 CALL 指令的大小。
动态链接器经常这样做。第一次尝试调用动态链接函数时,它们实际上跳入了加载程序。它加载实际的函数,然后重写 CALL 指令,以便它调用加载的函数而不是例程来加载函数。以后的任何调用都不必再次执行 look-up。 (我在博客上写了这个 here)