青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

Prayer

在一般中尋求卓越
posts - 1256, comments - 190, trackbacks - 0, articles - 0
  C++博客 :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理

向64位進軍

Posted on 2009-10-09 09:31 Prayer 閱讀(766) 評論(0)  編輯 收藏 引用 所屬分類: C/C++
隨著低成本64位平臺的來臨,加上內存和硬盤價格的不斷下跌,無疑為32位程序向64位硬件的移植又加了一把勁,那些科學運算、數據庫、消耗大量內存或密集浮點運算的程序也搭上了這一順風車。在本文中,主要討論向64位平臺移植現有32位代碼時,應注意的一些細小問題。
新近的64位平臺在二進制上與32位應用程序兼容,這意味著可以非常簡單地移植現有的程序。許多目前在32位平臺上運行良好的程序也許不必移植,除非程序有以下要求:
Ø 需要多于4GB的內存。
Ø 使用的文件大小常大于2GB。
Ø 密集浮點運算,需要利用64位架構的優(yōu)勢。
Ø 能從64位平臺的優(yōu)化數學庫中受益。
否則,只需簡單地重新編譯一下,就已經足夠了。大多數編寫良好的程序不費吹灰之力就可移植到64位平臺之上,在此假定你的程序編寫良好,并熟悉本文將要討論的問題。
ILP32和LP64數據模型
32位環(huán)境涉及“ILP32數據模型,是因為C數據類型為32位的int、long、指針。而64位環(huán)境使用不同的數據模型,此時的long和指針已為64位,故稱作“LP64數據模型。
現今所有64位的類Unix平臺均使用LP64數據模型,而64位Windows使用LLP64數據模型,除了指針是64位,其他基本類型都沒有變。我們在此主要探討ILP32到LP64的移植問題,表1顯示了ILP32與LP64數據模型的差異。
向64位移植代碼時的所有問題差不多都可以總結出一個簡單的規(guī)律:千萬不要認為int、long、指針的長度一樣。任何違反這條規(guī)律的代碼,當運行在LP64數據模型下時,都會出現不同的問題,而且很難找出原因所在。例1中有許多違反這條規(guī)律的地方,其在移植到64位平臺上時都需要重寫。
例1:
1      int *myfunc(int i)
2      {
3               return(&i);
4      }
5
6       int main(void)
7       {
8               int     myint;
9               long    mylong;
10               int     *myptr;
11
12        char *name = (char * ) getlogin();
13
14                printf("Enter a number %s: ", name);
15               (void) scanf("%d", &mylong);
16                myint = mylong;
17                myptr = myfunc(mylong);
18                printf("mylong: %d pointer: %x \n", mylong, myptr);
19                myint = (int)mylong;
20                exit(0);
21
22      }
第一步是要求編譯器捕捉到移植時的問題,因所用編譯器的不同,選項可能也有所不同,但對IBM XL編譯器系列,可用的選項有-qwarn64 -qinfo=pro,為了得到64位可執(zhí)行文件,可使用選項-q64(如果使用GCC,選項應為-m64,表2中列出了其他可用的GCC選項)。圖1是編譯例1中代碼時的情況。
缺少原型的截斷
如果一個函數被調用時沒有指定函數原型,返回值將是32位的int。不使用原型的代碼可能會發(fā)生意料之外的數據截斷,由此導致一個分割錯誤。編譯器捕捉到了例1中第12行的這個錯誤。
char *name = (char *) getlogin();
編譯器假定函數返回一個int值,并截短結果指針。這行代碼在ILP32數據模型下工作正常,因為此時的int和指針是同樣長度,換到LP64模型中,就不一定正確了,甚至于類型轉換都不能避免這個錯誤,因為getlogin()在返回之后已經被截斷了。
要修正這個問題,需包括頭文件<unistd.h>,其中有getlogin()的函數原型。
格式指定符
如果對64位long、指針使用了32位格式指定符,將導致程序錯誤。編譯器捕捉到了例1中第15行的這個錯誤。
(void) scanf("%d", &mylong);
注意,scanf將向變量mylong中插入一個32位的值,而剩下的4字節(jié)就不管了。要修正這個問題,請在scanf中使用%ld指定符。
第18行也演示了在printf中的一個類似的問題:
printf("mylong: %d pointer: %x \n", mylong, myptr);
要修正此處的錯誤,mylong應使用%ld,對myptr使用 %p而不是%x。
賦值截斷
有關編譯器發(fā)現賦值截斷的一個例子在第16行中:
myint = mylong;
這在ILP32模型下不會有任何問題,因為此時的int、long都是32位,而在LP64中,當把mylong賦值給myint時,如果數值大于32位整數的最大值時,數值將被截短。
被截斷的參數
編譯器發(fā)現的下一個錯誤在第17行中,雖然myfunc函數只接受一個int參數,但調用時卻用了一個long,參數在傳遞時會悄無聲息地被截斷。
轉換截斷
轉換截斷發(fā)生在把long轉換成int時,比如說例1中的第19行:
myint = (int) mylong;
導致轉換截斷的原因是int與long非同樣長度。這些類型的轉換通常在代碼中以如下形式出現:
int length = (int) strlen(str);
strlen返回size_t(它在LP64中是unsigned long),當賦值給一個int時,截斷是必然發(fā)生的。而通常,截斷只會在str的長度大于2GB時才會發(fā)生,這種情況在程序中一般不會出現。雖然如此,也應該盡量使用適當的多態(tài)類型(如size_t、uintptr_t等等),而不要去管它最下面的基類型是什么。
一些其他的細小問題
編譯器可捕捉到移植方面的各種問題,但不能總指望編譯器為你找出一切錯誤。
那些以十六進制或二進制表示的常量,通常都是32位的。例如,無符號32位常量0xFFFFFFFF通常用來測試是否為-1:
#define INVALID_POINTER_VALUE 0xFFFFFFFF
然而,在64位系統(tǒng)中,這個值不是-1,而是4294967295;在64位系統(tǒng)中,-1正確的值應為0xFFFFFFFFFFFFFFFF。要避免這個問題,在聲明常量時,使用const,并且?guī)蟬igned或unsigned。
const signed int INVALID_POINTER_VALUE = 0xFFFFFFFF;
這行代碼將會在32位和64位系統(tǒng)上都運行正常。
其他有關于對常量硬編碼的問題,都是基于對ILP32數據模型的不當認識,如下:
int **p; p = (int**)malloc(4 * NO_ELEMENTS);
這行代碼假定指針的長度為4字節(jié),而這在LP64中是不正確的,此時是8字節(jié)。正確的方法應使用sizeof():
int **p; p = (int**)malloc( sizeof(*p) * NO_ELEMENTS);
注意對sizeof()的不正確用法,例如:
sizeof(int) = = sizeof(int *);
這在LP64中是錯誤的。
符號擴展
要避免有符號數與無符號數的算術運算。在把int與long數值作對比時,此時產生的數據提升在LP64和ILP32中是有差異的。因為是符號位擴展,所以這個問題很難被發(fā)現,只有保證兩端的操作數均為signed或均為unsigned,才能從根本上防止此問題的發(fā)生。
例2:
long k;
int i = -2;
unsigned int j = 1;
k = i + j;
printf("Answer: %ld\n", k);
你無法期望例2中的答案是-1,然而,當你在LP64環(huán)境中編譯此程序時,答案會是4294967295。原因在于表達式(i+j)是一個unsigned int表達式,但把它賦值給k時,符號位沒有被擴展。要解決這個問題,兩端的操作數只要均為signed或均為unsigned就可。像如下所示:
k = i + (int) j
聯合體問題(Union
當聯合本中混有不同長度的數據類型時,可能會導致問題。如例3是一個常見的開源代碼包,可在ILP32卻不可在LP64環(huán)境下運行。代碼假定長度為2的unsigned short數組,占用了與long同樣的空間,可這在LP64平臺上卻不正確。
例3:
typedef struct {
    unsigned short bom;
    unsigned short cnt;
    union {
        unsigned long bytes;
        unsigned short len[2];
    } size;
} _ucheader_t;
要在LP64上運行,代碼中的unsigned long應改為unsigned int。要在所有代碼中仔細檢查聯合體,以確認所有的數據成員在LP64中都為同等長度。
字節(jié)序問題(Endian
因64位平臺的差異,在移植32位程序時,可能會失敗,原因可歸咎于機器上字節(jié)序的不同。Intel、IBM PC等CISC芯片使用的是Little-endian,而Apple之類的RISC芯片使用的是Big-endian;小尾字節(jié)序(Little-endian)通常會隱藏移植過程中的截斷bug。
例4:
long k;
int *ptr;
int main(void)
{
    k = 2 ;
    ptr = &k;
    printf("k has the value %ld, value pointed to by ptr is %ld\n", k, *ptr);
    return 0;
}
例4是一個有此問題的明顯例子,一個聲明指向int的指針,卻不經意間指向了long。在ILP32上,這段代碼打印出2,因為int與long長度一樣。但到了LP64上,因為int與long的長度不一,而導致指針被截斷。不管怎么說,在小尾字節(jié)序的系統(tǒng)中,代碼依舊會給出k的正確答案2,但在大尾字節(jié)序(Big-endian)系統(tǒng)中,k的值卻是0。
表3說明了為什么在不同的字節(jié)序系統(tǒng)中,會因截斷問題而產生不同的答案。在小尾字節(jié)序中,被截斷的高位地址中全為0,所以答案仍為2;而在大尾字節(jié)序中,被截斷的高位地址中包含值2,這樣就導致結果為0,所以在兩種情況下,截斷都是一種bug。但要意識到,小尾字節(jié)序會隱藏小數值的截斷錯誤,而這個錯誤只有在移植到大尾字節(jié)序系統(tǒng)上時才可能被發(fā)現。
移植到64位平臺之后的性能降低
當代碼移植到64位平臺之后,也許發(fā)現性能實際上降低了。原因與在LP64中的指針長度和數據大小有關,并由此引發(fā)的緩存命中率降低、數據結構膨脹、數據對齊等問題。
由于64位環(huán)境中指針所占用的字節(jié)更大,致使原來運行良好的32位代碼出現不同程度的緩存問題,具體表現為執(zhí)行效率降低。可使用工具來分析緩存命中率的變化,以確認性能降低是否由此引起。
在遷移到LP64之后,數據結構的大小可能會改變,此時程序可能會需要更多的內存和磁盤空間。例如,圖2中的結構在ILP32中只需要16字節(jié),但在LP64中,卻需要32字節(jié),整整增長了100%。這緣于此時的long已是64位,編譯器為了對齊需要而加入了額外的填充數據。
通過改變結構中數據排列的先后順序,能將此問題所帶來的影響降到最小,并能減少所需的存儲空間。如果把兩個32位int值放在一起,會因為少了填充數據,存儲空間也隨之減少,現在存儲整個結構只需要24字節(jié)。
在重排數據結構之前,在根據數據使用的頻度仔細衡量,以免因降低緩存命中率而帶來性能上的損失。
如何生成64位代碼
在一些情況中,32位和64位程序在源代碼級別的接口上很難區(qū)分。不少頭文件中,都是通過一些測試宏來區(qū)分它們,不幸的是,這些特定的宏依賴于特定的平臺、特定的編譯器或特定的編譯器版本。舉例來說,GCC 3.4或之后的版本都定義了__LP64__,以便為所有的64位平臺通過選項-m64編譯產生64位代碼。然而,GCC 3.4之前的版本卻是特定于平臺和操作系統(tǒng)的。
也許你的編譯器使用了不同于__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環(huán)境中結構大小的不同而導致問題。
對那些必須同時運行在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位平臺是解決大型復雜科學及商業(yè)問題的希望,大多數編寫良好的程序可輕松地移植到新平臺上,但要注意ILP32和LP64數據模型的差異,以保證有一個平滑的移植過程。
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            亚洲欧美日韩一区| 欧美一区二区三区在线视频| 国模私拍视频一区| 99精品国产在热久久| 久久久久在线观看| 中文日韩在线| 欧美日韩午夜视频在线观看| 亚洲欧洲一区二区在线播放 | 亚洲精品老司机| 欧美1区免费| 久久精品一区二区三区不卡| 国产精品日日摸夜夜摸av| 亚洲毛片一区二区| 亚洲精品国产精品乱码不99按摩| 久久久久国产精品一区二区| 国产一区二区三区无遮挡| 欧美在线日韩精品| 亚洲欧美日韩网| 国模私拍一区二区三区| 美女精品在线观看| 美女图片一区二区| 亚洲国产另类久久久精品极度| 久久一区二区三区四区| 久久免费一区| 亚洲日本中文字幕免费在线不卡| 欧美激情一级片一区二区| 欧美高清在线一区二区| 一区二区三区视频在线| 亚洲性色视频| 国产在线拍揄自揄视频不卡99| 久久综合伊人77777| 美女精品网站| 亚洲一级电影| 性色av一区二区三区在线观看| 国产一区二区精品| 欧美成人精品不卡视频在线观看| 欧美激情亚洲视频| 午夜精品久久久久久久蜜桃app | 欧美国产精品一区| 在线综合欧美| 亚洲欧美网站| 亚洲国产免费| 在线亚洲+欧美+日本专区| 国产日韩在线视频| 亚洲第一综合天堂另类专| 欧美日韩福利在线观看| 久久久天天操| 欧美日本精品一区二区三区| 欧美一区二区私人影院日本| 免费高清在线视频一区·| 亚洲亚洲精品在线观看| 久久久999成人| 在线中文字幕一区| 久久久久看片| 亚洲专区一区| 老鸭窝91久久精品色噜噜导演| 国产精品99久久久久久久vr | 亚洲清纯自拍| 激情小说亚洲一区| 亚洲四色影视在线观看| 亚洲高清色综合| 午夜视频久久久| 日韩小视频在线观看| 欧美在线地址| 香蕉亚洲视频| 欧美日韩一区二区免费在线观看| 久久一二三四| 国产视频欧美视频| 亚洲午夜久久久久久尤物 | 亚洲黄色成人| 久久大综合网| 亚洲欧美亚洲| 欧美日韩精品欧美日韩精品| 毛片基地黄久久久久久天堂| 国产精品日韩电影| 亚洲乱码一区二区| 亚洲激情视频在线| 久久免费高清| 久久激五月天综合精品| 国产精品国产成人国产三级| 亚洲电影在线| 亚洲电影有码| 久久久999精品视频| 亚洲欧美视频在线观看| 欧美日韩国产首页在线观看| 亚洲国产经典视频| 亚洲东热激情| 老牛嫩草一区二区三区日本| 久久在线免费观看| 国语精品中文字幕| 久久精品国产清高在天天线| 午夜伦理片一区| 国产精品久久久久免费a∨| 日韩一级精品| 亚洲综合日本| 欧美日韩国产影片| 99视频精品全部免费在线| 亚洲免费播放| 在线一区二区日韩| 国产精品蜜臀在线观看| 在线一区二区三区四区五区| 亚洲免费一区二区| 国产精品va| 中日韩在线视频| 欧美与欧洲交xxxx免费观看| 国产日韩欧美精品在线| 亚洲视频专区在线| 久久精品国产欧美激情| 国产亚洲午夜| 久久免费视频在线| 亚洲国产综合视频在线观看| 99热免费精品在线观看| 欧美色另类天堂2015| 亚洲视频网站在线观看| 久久精品视频播放| 在线精品在线| 欧美日韩成人一区| 亚洲女人天堂av| 另类av导航| 99国内精品| 国产美女精品在线| 蜜桃av一区二区三区| 91久久在线视频| 亚洲欧美网站| 91久久综合| 国产精品你懂的在线| 久久久久99| 日韩视频免费观看高清在线视频| 亚洲国产第一页| 欧美午夜精品久久久久久久 | 欧美日本中文| 午夜精品久久久久久久| 欧美aⅴ一区二区三区视频| aa级大片欧美三级| 国产一区亚洲一区| 欧美日韩福利| 久久夜色精品国产亚洲aⅴ| 99综合电影在线视频| 久久综合给合| 亚洲欧美高清| 国产精品伦理| 欧美成人一区二区三区| 亚洲欧美韩国| 日韩一级大片| 欧美午夜视频在线观看| 久久精品99无色码中文字幕| 日韩亚洲在线| 亚洲高清不卡一区| 久久se精品一区精品二区| 99精品免费| 亚洲高清成人| 国内一区二区在线视频观看 | 亚洲国产一二三| 欧美激情五月| 久久视频精品在线| 亚洲欧美日韩国产一区| 亚洲精选在线观看| 嫩草影视亚洲| 欧美在线精品一区| 宅男在线国产精品| 亚洲美洲欧洲综合国产一区| 一区二区三区中文在线观看| 国产精品综合不卡av| 欧美视频日韩视频| 欧美精品午夜| 欧美国产精品中文字幕| 免费欧美高清视频| 久久理论片午夜琪琪电影网| 亚洲激情电影中文字幕| 欧美高清视频在线| 欧美xx视频| 久久久国际精品| 欧美一区二区三区视频免费播放| 亚洲午夜精品国产| 午夜精品网站| 香蕉国产精品偷在线观看不卡| 亚洲一区二区精品视频| 亚洲一二区在线| 亚洲欧美一区二区原创| 亚洲欧美日韩国产一区二区三区| 亚洲午夜在线观看视频在线| 亚洲婷婷免费| 亚洲永久免费精品| 亚洲免费视频中文字幕| 性xx色xx综合久久久xx| 久久国产66| 久久久噜噜噜久久中文字免| 久久久久久久久一区二区| 久久综合国产精品| 欧美国产另类| 日韩视频一区二区三区在线播放 | 国产精品久久久久国产a级| 欧美视频一区二区三区在线观看| 国产精品第一页第二页第三页| 日韩一级不卡| 在线一区免费观看| 午夜精品久久久久久久久| 久久国产色av| 欧美大片在线看| 亚洲精品在线观看免费|