在VC2008下将32位C++内嵌汇编迁移到64位

时间:2022-09-01 09:28:56

http://blog.sina.com.cn/s/blog_6296cebc0100fa5g.html

在VC2008下将32位C++内嵌汇编迁移到64位

——东方射日——

为什么要汇编?


现在正在做的一个项目,是关于高密度计算的,由于计算量很大,从性能考虑,除了算法的优化外,其中的的核心代码是用汇编写的,也许有人说,现在还有用汇编的吗?编译器的优化已经很好了,性能已经非常接近于汇编了,有必要用汇编实现吗?呵呵,再好编译器的优化也没有人工优化好。是的,两者的性能相差不大,可是也有10%-20%的差距啊,尤其是作为核心代码,在一个典型过程中要执行400万到1000万次,那么性能相差是相当明显的。就拿现在的例子来讲,算法优化后,多线程(88线程)执行一个过程在C++需要大约62毫秒,而用汇编实现则仅需要53毫秒。


好了,罗嗦了一段为什么要使用汇编的原因,下面进入这题。上述的汇编是采用内嵌汇编在VC2008-x86下实现的,现在要将该工程移植到64位下,字长长了,应该对并行计算大大得有好处,性能可以进一步提高。

VC2008-x64下编译工程,纯C++配置,编译出来上述的典型过程需要59毫秒,比32位快了5%

接着打开汇编编译开关,编译内嵌汇编,编译器报告_asm{}语法错误!查资料,原来C++还不支持内嵌的64位汇编。没办法,如果不实现汇编,那么移植是毫无意义的,毕竟还不如32位带汇编的效率高。看来要实现汇编只有一种方法,就是写单独的汇编代码,然后编译为obj文件再链接进原来的工程。


C++到汇编


不过本人所有的汇编经验仅限于内嵌汇编,对于独立的汇编程序,经验趋近于零。继续查资料,OMG,独立的汇编有太多的约定,比如接口定义,各个段的定义和标志,还有不同的调用约定,太麻烦了。而且项目的时间不允许我花上一两个星期来学习。怎么办呢?


VC++再调试中,你可以打开汇编语言页面,跟踪一条条汇编指令,也就是说VC++一定是将源代码先编译为汇编再编译为二进制目标码的,也许编译器有汇编的开关吧,说干就干,打开DOS窗口,到VC++编译器cl的目录下,敲入


cl /?


你在-OUTPUT FILES-一栏中可以看到这个选项:

/Fa[file] name assembly listing file


哈哈,原来还可以直接输出汇编代码。那么有办法了,我就先把核心代码那段拿出来,单独建一个c++文件,去掉所有不相关的头文件,仅将必要的数据类型和常量定义加上,然后在DOS窗口下编译:


cl /c /Fa decoder_asm.cpp


不出所料得到了汇编代码的输出:


decoder_asm.asm


代码内容如下:

; Listing generated by Microsoft (R) Optimizing Compiler Version 15.00.30729.01

include listing.inc

INCLUDELIB LIBCMT

INCLUDELIB OLDNAMES


PUBLIC $T3239

PUBLIC $T3240

:

:

PUBLIC decode_pass0_asm64

pdata SEGMENT

$pdata$decode_pass0_asm64 DD imagerel $LN160

DD imagerel $LN160+7895

DD imagerel $unwind$decode_pass0_asm64

pdata ENDS

xdata SEGMENT

$unwind$decode_pass0_asm64 DD 041d01H

DD 057011dH

DD 060157016H

; Function compile flags: /Odtp

xdata ENDS

_TEXT SEGMENT

width_by3$ = 0

sign_asm64$ = 784

decode_pass0_asm64 PROC

; File c:\jason\decoder\decoder\codec\src\decoder_asm.cpp

; Line 351

$LN160:

mov QWORD PTR [rsp+32], r9

mov DWORD PTR [rsp+24], r8d

mov QWORD PTR [rsp+16], rdx

mov QWORD PTR [rsp+8], rcx

push rsi

push rdi

sub rsp, 696 ; 000002b8H

; Line 366

mov rax, QWORD PTR coder$[rsp]

:

:

decode_pass0_asm64 ENDP

_TEXT ENDS

END

汇编到OBJ


太好了,下一步就是将这个asm文件编译为obj了。

查资料,VC下的64位汇编编译器是ml64


DOS下敲命令

ml64 /c decoder_asm.asm


报告错误

decoder_asm.asm(3) : fatal error A1000:cannot open file : listing.inc


看上面的汇编代码,要include一个listing.inc文件,这个文件是干什么的?搜搜,在C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\include下有这个文件,号称是:


;; LISTING.INC

;;

;; This file contains assembler macros and is included by the files created

;; with the -FA compiler switch to be assembled by MASM (Microsoft Macro

;; Assembler).

;;

;; Copyright (c) 1993-2003, Microsoft Corporation. All rights reserved.


那么就把这个文件copy到源文件目录下,继续执行编译。

好,那个链接错误消失了


不过这回又多出了若干个错误:


Assembling: decoder_asm.asm

decoder_asm.asm(42) : error A2006:undefined symbol : $LN160

decoder_asm.asm(43) : error A2006:undefined symbol : $LN160

:

:


原来是这行的错误:

$pdata$bmi_t1_decode_pass0_asm64 DD imagerel $LN160


那么$LN160是什么呢?为什么报错呢?

问了下网上的高手,告诉我这个很简单啊,就是一个标号,说在使用在定义前啊

呵呵,原来如此,那么既然是标号,我就野蛮些直接把这些注释掉,就是把上面整个pdata段全注释掉!!


再编译一次,果然编译成功,生成了obj文件。


OBJ链接回工程


下一步就是要将这个obj链接回去工程文件了,那么就是要改VC的工程配置。


首先,第一步建立的那个cpp文件decoder_asm.cpp仅仅是中间文件,我们不希望编译器编译链接的,那么在这个文件上打开Property对话框。在General下将Tool改为Custom Build Tool。(原来是C/C++ Compiler Tool


现在build,你自然得到几个说什么几个函数找不到的错误。


下一步,打开该projectProperty对话框,在Configuration Properties -> Linker -> Input 里的Additional Dependencies里加上上述的obj文件。


接着buildWOW!成功!


写汇编


下一步,我就可以在生成的汇编文件里修改汇编代码,因为所有的变量接口和调用约定都已经做好了,那么接下来的工作就和以前写内嵌汇编没什么差别了。

呵呵,笨人有笨办法,看看,从来没写过独立汇编的人就这样绕过了这一难题。


其他事项

当然还有其他的工作可以做做为以后的调试做准备。

首先可以将生成的汇编加到工程中,然后在Property->General->Tool改为Custom Build Tool。并自己定义一个build的过程,就是上文汇编到OBJ中提到的ml64,那么以后你就可以不用命令窗口而直接在VC下build了。

至于调试,可以就是用VC,没有源代码的部分就用汇编页面看吧——反正源代码也就是汇编。


下一步

今天刚刚生成了汇编的框架,再下一步就是要写汇编了,希望不会遇到什么大问题,有什么问题再和大家交流吧