将32位代码向64位平台移植的注意事项
发布时间:2006-02-10 00:30:53 来源:天极开发 网友评论 0 条 移植到64位平台之后的性能降低
当代码移植到64位平台之后,也许发现性能实际上降低了。原因与在LP64中的指针长度和数据大小有关,并由此引发的缓存命中率降低、数据结构膨胀、数据对齐等问题。
由于64位环境中指针所占用的字节更大,致使原来运行良好的32位代码出现不同程度的缓存问题,具体表现为执行效率降低。可使用工具来分析缓存命中率的变化,以确认性能降低是否由此引起。
在迁移到LP64之后,数据结构的大小可能会改变,此时程序可能会需要更多的内存和磁盘空间。例如,图2中的结构在ILP32中只需要16字节,但在LP64中,却需要32字节,整整增长了100%。这缘于此时的long已是64位,编译器为了对齐需要而加入了额外的填充数据。
通过改变结构中数据排列的先后顺序,能将此问题所带来的影响降到最小,并能减少所需的存储空间。如果把两个32位int值放在一起,会因为少了填充数据,存储空间也随之减少,现在存储整个结构只需要24字节。
在重排数据结构之前,在根据数据使用的频度仔细衡量,以免因降低缓存命中率而带来性能上的损失。
如何生成64位代码
在一些情况中,32位和64位程序在源代码级别的接口上很难区分。不少头文件中,都是通过一些测试宏来区分它们,不幸的是,这些特定的宏依赖于特定的平台、特定的编译器或特定的编译器版本。举例来说,GCC 3.4或之后的版本都定义了__LP64__,以便为所有的64位平台 通过选项-m64编译产生64位代码。然而,GCC 3.4之前的版本却是特定于平台和操作系统的。
也许你的编译器使用了不同于__LP64__的宏,例如IBM XL的编译器当用-q64编译程序时,使用了__64bit__宏,而另一些平台使用_LP64,具体情况可用__WORDSIZE来测试一下。请查看相关编译器文档,以便找出最适合的宏。例5可适用于多种平台和编译器:
例5:
当代码移植到64位平台之后,也许发现性能实际上降低了。原因与在LP64中的指针长度和数据大小有关,并由此引发的缓存命中率降低、数据结构膨胀、数据对齐等问题。
由于64位环境中指针所占用的字节更大,致使原来运行良好的32位代码出现不同程度的缓存问题,具体表现为执行效率降低。可使用工具来分析缓存命中率的变化,以确认性能降低是否由此引起。
在迁移到LP64之后,数据结构的大小可能会改变,此时程序可能会需要更多的内存和磁盘空间。例如,图2中的结构在ILP32中只需要16字节,但在LP64中,却需要32字节,整整增长了100%。这缘于此时的long已是64位,编译器为了对齐需要而加入了额外的填充数据。
通过改变结构中数据排列的先后顺序,能将此问题所带来的影响降到最小,并能减少所需的存储空间。如果把两个32位int值放在一起,会因为少了填充数据,存储空间也随之减少,现在存储整个结构只需要24字节。
在重排数据结构之前,在根据数据使用的频度仔细衡量,以免因降低缓存命中率而带来性能上的损失。
如何生成64位代码
在一些情况中,32位和64位程序在源代码级别的接口上很难区分。不少头文件中,都是通过一些测试宏来区分它们,不幸的是,这些特定的宏依赖于特定的平台、特定的编译器或特定的编译器版本。举例来说,GCC 3.4或之后的版本都定义了__LP64__,以便为所有的64位平台 通过选项-m64编译产生64位代码。然而,GCC 3.4之前的版本却是特定于平台和操作系统的。
也许你的编译器使用了不同于__LP64__的宏,例如IBM XL的编译器当用-q64编译程序时,使用了__64bit__宏,而另一些平台使用_LP64,具体情况可用__WORDSIZE来测试一下。请查看相关编译器文档,以便找出最适合的宏。例5可适用于多种平台和编译器:
例5:
| #if defined (__LP64__) || defined (__64BIT__) || defined (_LP64) || (__WORDSIZE == 64) printf("I am LP64/n"); #else printf("I am ILP32 /n"); #endif |
共享数据
在移植到64位平台时的一个典型问题是,如何在32位和64位程序之间读取和共享数据。例如一个32位程序可能把结构体作为二进制文件存储在磁盘上,现在你要在64位代码中读取这些文件,很可能会因LP64环境中结构大小的不同而导致问题。
对那些必须同时运行在32位和64位平台上的新程序而言,建议不要使用可能会因LP64和ILP32而改变长度的数据类型(如long),如果实在要用,可使用头文件<inttypes.h>中的定宽整数,这样不管是通过文件还是网络,都可在32位和64位的二进制层面共享数据。
例6:
| #include <stdio.h> #include <inttypes.h> struct on_disk { /* ILP32|LP64共享时,这个应该使用int32_t */ long foo; }; int main() { FILE *file; struct on_disk data; #ifdef WRITE file=fopen("test","w"); data.foo = 65535; fwrite(&data, sizeof(struct on_disk), 1, file); #else file = fopen("test","r"); fread(&data, sizeof(struct on_disk), 1, file); printf("data: %ld/n", data.foo); #endif fclose(file); } |
来看一下例6,在理想的情况下,这个程序在32位和64位平台上都可正常运行,并且可以读取对方的数据。但实际上却不行,因为long在ILP32和LP64之中长度会变化。结构on_disk里的变量foo应该声明为int32_t,这个定宽类型可保证在当前ILP32或移植到的LP64数据模型下,都生成相同大小的数据。
混合Fortran和C的问题
许多科学运算程序从C/C++中调用Fortran的功能,Fortran从它本身来说并不存在移植到64位平台的问题,因为Fortran的数据类型有明确的比特大小。然而,如果混合Fortran和C语言,问题就来了,如下:例7中C语言程序调用例8中Fortran语言的子例程。
例7:
| void FOO(long *l); main () { long l = 5000; FOO(&l); } |
例8:
| subroutine foo( i ) integer i write(*,*) 'In Fortran' write(*,*) i return end subroutine foo |
例9:
| % gcc -m64 -c cfoo.c % /opt/absoft/bin/f90 -m64 cfoo.o foo.f90 -o out % ./out In Fortran 0 |
当链接这两个文件后,程序将打印出变量i的值为"5000"。而在LP64中,程序打印出"0",因为在LP64模式下,子例程foo通过地址传递一个64位的参数,而实际上,Fortran子例程想要的是一个32位的参数。如果要改正这个错误,在声明Fortran子例程变量i时,把它声明为INTEGER*8,此时和C语言中的long为一样长度。
结论
64位平台是解决大型复杂科学及商业问题的希望,大多数编写良好的程序可轻松地移植到新平台上,但要注意ILP32和LP64数据模型的差异,以保证有一个平滑的移植过程。
- 推荐阅讯
- 成为嵌入式程序员应知道的0x10个问题
- 新浪博客式的Web2.0分析
- 关于Web2.0的最后几句话
- 浅析Spring提供的事务管理方法
- Eclipse插件开发之EasyExplorer
- 一个用于J2EE应用程序的异常处理框架
- 三大编程语言性能PK:Java, C/C++和Ruby
- Oracle 10g Release2新功能之Ref Cursor
- OpenGL编程轻松入门之菜单管理
- OpenGL编程轻松入门之堆栈操作
- 阅读排行
- 1..net页面间的参数传递简单实例
- 2.VC++与Matlab混合编程之引擎操作详解
- 3.Oracle数据库数据对象分析
- 4.Eclipse3.2+Tomcat5.5.17+Oracle9配置
- 5.Oracle数据库中索引的维护
- 6.在Oracle的网络结构中解决连接问题
- 7.Oracle数据安全面面观
- 8.Oracle数据库的ORA-00257故障解决过程
- 9.Oracle数据库备份与恢复的三种方法
- 10.Oracle与SQL Server在企业应用中的比较
- 专题教程
- Windows Server-Windows Server文档-Windows Server新闻-Windows Ser PostgreSQL-PostgreSQL文档-PostgreSQL新闻-PostgreSQL专家
- WebLogic-WebLogic文档-WebLogic新闻-WebLogic专家 FreeBSD-FreeBSD文档-FreeBSD新闻-FreeBSD专家
- Linux-内核 GUI KDE Gnome DNS FTP 安全 安装-Linux专区 Windows-AD IIS ServerCore 虚拟化 安全 HPC-Windows专区
- 大话G游 专题:手机病毒揭密
- ARP攻击防范与解决方案 路由故障处理手册
