我们正在为高级编译语言编写一个字节代码,经过一些分析和优化后,很明显当前最大的性能开销是我们用来跳转到字节的switch语句 - 代码案例。
我们调查了拉出每个案例标签的地址并将其存储在字节码本身的流中,而不是我们通常打开的指令ID。如果我们这样做,我们可以跳过跳转表,并直接跳转到当前正在执行的指令的代码位置。这在GCC中非常有用,但是,MSVC似乎不支持这样的功能。
我们尝试使用内联汇编来获取标签的地址(并跳转到它们),但它可以工作,但是,使用内联汇编会导致MSVC优化器避免使用整个函数。
有没有办法允许优化器仍然运行代码?遗憾的是,我们无法将内联汇编提取到另一个函数中,而不是制作标签的函数,因为即使在内联汇编中也无法为另一个函数引用标签。有什么想法或想法吗?非常感谢您的意见,谢谢!
答案 0 :(得分:16)
在MSVC中执行此操作的唯一方法是使用内联汇编(这基本上会让您对x64感到厌烦):
int _tmain(int argc, _TCHAR* argv[])
{
case_1:
void* p;
__asm{ mov [p],offset case_1 }
printf("0x%p\n",p);
return 0;
}
如果您打算做这样的事情,那么最好的方法是在程序集中编写整个解释器,然后通过链接器将其链接到主二进制文件(这是LuaJIT所做的,这是主要原因,当VM没有运行JIT代码时,VM非常快速。
LuaJIT is open-source,所以如果你走这条路,你可能会从中获取一些提示。或者,您可能希望查看第四个来源(其创建者开发the principle您尝试使用),如果有MSVC构建,您可以看到他们是如何完成它的,否则您需要坚持使用GCC(不是是一件坏事,它适用于所有主要平台)。
答案 1 :(得分:10)
看看Erlang在Windows上构建的内容。他们使用MSVC进行大部分构建,然后使用GCC作为一个文件来使用label-as-values扩展。然后,对生成的目标代码进行黑客攻击,使其与MSVC链接器兼容。
http://www.erlang.org/doc/installation_guide/INSTALL-WIN32.html
答案 2 :(得分:4)
似乎您可以将实际代码移动到函数,而不是案例标签。然后可以将字节代码简单地转换为直接调用。即字节码1将转换为CALL BC1
。由于您正在生成直接调用,因此您没有函数指针的开销。大多数CPU的管道都可以遵循这种无条件的直接分支。
因此,每个字节代码的实际实现都得到了优化,从字节代码到machince代码的转换是一个简单的1:1转换。你得到一些代码扩展,因为每个CALL
是5个字节(假设x86-32),但这不太可能是一个主要问题。