科技时代新浪首页 > 科技时代 > 学园 > 正文

将32位代码向64位平台移植的注意事项(4)


http://www.sina.com.cn 2006年02月10日 07:39 天极yesky

    移植到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:

#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数据模型的差异,以保证有一个平滑的移植过程。

[上一页] [1] [2] [3] [4]

发表评论

爱问(iAsk.com)



评论】【论坛】【收藏此页】【 】【多种方式看新闻】【下载点点通】【打印】【关闭




科技时代意见反馈留言板 电话:010-82628888-5595   欢迎批评指正

新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 会员注册 | 产品答疑

Copyright © 1996 - 2006 SINA Corporation, All Rights Reserved

新浪公司 版权所有