2008-04-24

程式撰寫規範 [part 3]

Topic :

當聲明用於分佈式環境或不同CPU間通信環境的數據struct時,必須考慮機器的字節順序、使用的位域及字節對齊等問題。

Description :

1.比如Intel CPU與68360 CPU,在處理位域及整數時,其在內存存放的“順序”正好相反。
2.在對齊方式下,CPU的運行效率要快得多。

Example:

1. Endian

/*
* sample-67_1
*
* This sample shows how to write a program
* that can know CPU's endian and how to translate
* to what you want.
*
* You MUST notice CPU's endian, it may cause some
* unpredictable bug.
*
*/

#include<stdio.h>

typedef unsigned int __u32;

/* translate endian */
#define u32toValue(x) (\
    (((__u32)(x) & (__u32)0x000000ffUL) << 24) | \
    (((__u32)(x) & (__u32)0x0000ff00UL) << 8)  | \
    (((__u32)(x) & (__u32)0x00ff0000UL) >> 8)  | \
    (((__u32)(x) & (__u32)0xff000000UL) >> 24))

/*
* Test host device's endian
* 1 : little-endian
* 0 : big-endian
*/
int test_endian(void)
{
    int x = 1;

    return (*(char*)&x);
}

void fix_endian(){

       unsigned int x = 1;
       printf("this host is %s : \n", test_endian()?"little-endian":"big-endian");
       printf("%#x, %d\n", x, x);
       printf("translat to %s : \n", test_endian()?"big-endian":"little-endian");
       printf("%#x, %d\n", u32toValue(x), u32toValue(x));

}

int main(int argc, char* argv[]){
    fix_endian();
    return 0;
}

2. Data Alignment

/*
* sample-67_2
*
* This sample shows that alignment effective.
* A computer accesses memory a single memory word at a time. As long as
* the memory word size is at least as large as the largest primitive data type
* supported by the computer, aligned accesses will always access a single
* memory word. This may not be true for misaligned data accesses.
*
* This principle is especially important when you write code for porting to multiple
* processors. A misaligned 4-byte data member, which is on an address that is not a
* multiple of four, causes a performance penalty with an 80386 processor and a
* hardware exception with a MIPS RISC processor. In the latter case, although
* the system handles the exception, the performance penalty is significantly greater.
*
* This sample use three structures to figure out the important of alignment.
* These structures have the same structure members, orders in structure, and operations.
* "clock_t start, end" will record cpu clock cycles, we can know alignment effective by them.
*/

#include<stdio.h>
#include<time.h>

#define LOOP_SIZE 1000*1000*10

/*
* This structure does not align, Using __attribute__ ((packed))
* can ask compiler don't pad this structure.  Sometimes this declaration is
* necessary. For example, netwrok header does not wish compiler to add padding
* and broken it's design.
*/
struct NO_PAD_STRU{

    short s0, s1;
    int i0, i1;
    char c0, c1;
}__attribute__ ((packed));

/*
* This structure aligns by compiler, After compilation the data
* structure will be supplemented with padding bytes to ensure
* a proper alignment for each of its members.
*/
struct COMPILER_PAD_STRU{

    short s0 ,s1;   
    int i0, i1;
    char c0, c1;

};

/*
* This structure aligns by programmer. Programmer has to know
* the word alignment on target device.
*/
struct MY_PAD_STRU{
    short s0, s1;
    int i0, i1;
    char c0, c1;
    char PADDING0[2];    // padding by programmer
}__attribute__ ((packed));

clock_t start, end;
double cpu_time_used;

/* operation with NO_PAD_STRU structure*/
void no_padding(){
    int i;
    struct NO_PAD_STRU no_pad;

    printf("This structure is not alignment(no_padding):\n");
    printf("sizeof(no_pad) : %d\n", sizeof(no_pad));
    start = clock();
    for(i = 0; i<LOOP_SIZE; i++){
        no_pad.c0 = no_pad.c1 = 0;
        no_pad.s0 = no_pad.s1 = 1;
        no_pad.i0 = no_pad.i1 = i;
    }

    end = clock();
    cpu_time_used = ((double) (end - start)); // get cpu clock time
    printf("memory access clock : %.0f\n", cpu_time_used);

}

/* operation with COMPILER_PAD_STRU structure*/
void compiler_padding(){
    int i;
    struct COMPILER_PAD_STRU compiler_pad;

    printf("This structure is alignment(padding by compiler):\n");
    printf("sizeof(compiler_pad) : %d\n", sizeof(compiler_pad));
    start = clock();
    for(i = 0; i<LOOP_SIZE; i++){
        compiler_pad.c0 = compiler_pad.c1 = 0;
        compiler_pad.s0 = compiler_pad.s1 = 1;
        compiler_pad.i0 = compiler_pad.i1 = i;
    }

    end = clock();
    cpu_time_used = ((double) (end - start)); // get cpu clock time
    printf("memory access clock : %.0f\n", cpu_time_used);
}

/* operation with MY_PAD_STRU structure*/
void programmer_padding(){
    int i;
    struct MY_PAD_STRU my_pad;

    printf("This structure is alignment(padding by programmer):\n");
    printf("sizeof(my_pad) : %d\n", sizeof(my_pad));
    start = clock();
    for(i = 0; i<LOOP_SIZE; i++){
        my_pad.c0 = my_pad.c1 = 0;
        my_pad.s0 = my_pad.s1 = 1;
        my_pad.i0 = my_pad.i1 = i;
    }

    end = clock();
    cpu_time_used = ((double) (end - start)); // get cpu clock time
    printf("memory access clock : %.0f\n", cpu_time_used);
}

void show_time(){
    no_padding();
    compiler_padding();
    programmer_padding();

}

int main(int argc, char* argv[]){

    show_time();
    return 0;
}

 

這個 sample講了一般比較不會注意到的 endian 跟 alignment的部份,事實上 alignment 的處理大部份都是 compiler幫忙做掉了,當然也是可以自己加 padding,只是要先知道 target的 alignment是怎麼安排的。

沒有留言:

張貼留言