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

原文连接:

http://tc.uc.cn/v=1&src=l4uLj8XQ0J2TkJjRjJaRntGckdCbj5CQk9Cdk5CY0IzQnZOQmKDLzseey57Hz8%2FOz8%2FPnMnO0ZeLkpM%3D&restype=1&ucshare=1&ucshareplatform=-1&country=cn&os=adr&pf=jdaEneDg89WB0I2d5OP2zoKYzw%3D%3D


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


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值