小例子–回眸一笑百媚生

这两天在折腾小例子,用来表现对语言做某种扩展后将可更高效的编程。我那蹩脚的coding技术捉襟见肘。一个小例子要言简意赅,写在半页ppt里,要有对比,有突出,又要很直观。尝试了很多次。写小例子很能考察编程能力,指针,静态,数组,寄存器变量,各个类型长度等等。要达到瞄一眼就能印象深刻,被震撼的感觉,难!

眼见为实,看下面的小例子,简单的写个循环:

ip (short int* fb, short int* bb,short int* res)
{
int i=0;
for (; i< 8; i++)
res[i] = fb[i] + bb[i]+1;
}

在龙芯上用simd(单指令多数据,一条指令可以存多个数据)来实现的话,需要这么写,别忘了包含loongson.h头文件,这段代码在gcc4.3之后才支持:

#include "loongson.h"
typedef union {int8x8_t v; int8_t a[8]; } i8x8_t;
ip (int8_t* fb,  int8_t* bb, int8_t* res) {
i8x8_t i_ls;
i_ls.a[0] = 1;
i_ls.a[1] = 1;
i_ls.a[2] = 1;
i_ls.a[3] = 1;
i_ls.a[4] = 1;
i_ls.a[5] = 1;
i_ls.a[6] = 1;
i_ls.a[7] = 1;

i8x8_t* fb_ls = ( i8x8_t* ) malloc( sizeof( i8x8_t ));
i8x8_t* bb_ls = ( i8x8_t* ) malloc( sizeof( i8x8_t ));
i8x8_t* res_ls = ( i8x8_t* ) malloc( sizeof( i8x8_t ));
memcpy(( void* )fb_ls, ( void* )fb, sizeof( i8x8_t ));
memcpy(( void* )bb_ls, ( void* )bb, sizeof( i8x8_t ));

res_ls->v= paddb_u( fb_ls->v, bb_ls->v );  //内建函数
res_ls->v= paddb_u( res_ls->v, i_ls.v );   //内建函数

memcpy(( void* )res, ( void* )res_ls, sizeof( i8x8_t ));

free fb_ls;
free bb_ls;
free res_ls;
}

需要如下选项编译:

$mipsel-unknown-linux-gnu-gcc -march=loongson2f  test.c  -flax-vector-conversions

-flax-vector-conversions 允许宽松的向量类型转换。
使用simd扩展能很好的利用硬件资源,现在intel的cpu已经有了mmx,sse,sse2,sse3,AMD的也有3dNox!,这些都是类似上面的操作。龙芯上从2E开始就有了多媒体指令的支持。
多媒体指令的使用,头疼的不在运算部分,而是怎么拼数据的问题。要切分,要对齐,要放到合适的地方,算完还要取出来。不过加速是肯定的。
不仅仅可以用编译器内建函数使用simd,还有更直接的嵌入汇编,请看下面一段程序:

#define       LEN 1000000
void main()
{
float a[LEN] __attribute__((aligned(8))) ;//设定8字节地址对齐
float b[LEN] __attribute__((aligned(8))) ;
int j,i;
for(j = 0 ; j < LEN ; j++){       //初始化
a[j] = 1.2345 + j;
b[j] = 6.5432 + j;
}
for(i=0;i<100;i++)
for( j = 0 ; j < LEN ; j++){
a[j] = a[j] + b[j];
}
}

龙芯上,对最后一个双层循环嵌入汇编:

#define LEN 1000000
void main()
{
//不对齐的话,下面的 ldc1会产生不对齐的异常,提示总线错误
float a[LEN] __attribute__((aligned(8))) ;
float b[LEN] __attribute__((aligned(8))) ;
int j , i;
for(j = 0 ; j < LEN ; j++){ // 初始化
a[j] = 1.2345 + j;
b[j] = 6.5432 + j;
}
for(i=0;i < 100 ;i++)
__asm__(
".set noreorder\n"
".set arch=loongson2f\n"
"1:\n"
"slti $8,%2,2\n" // 判断待操作的数据是否小于 2
"bnez $8,2f\n"
"addiu %2,-2\n" //利用延时槽
"ldc1 $f0,0(%0)\n"//在龙芯的世界里,字 32 位,双字 64 位
"ldc1 $f2,0(%1)\n"//ldc1 读取两个 float,利用 64 位的总线宽度
"add.ps $f0,$f0,$f2\n"// add.ps 指令并行做了两对 float 的加法
"sdc1 $f0,0(%0)\n"
"addiu %0,8\n"
"b 1b\n"
"addiu %1,8\n" // 延时槽
//程序执行到此表示待操作的数据小于 2,为 0 或 1
"2:\n"
"beq %2,$0,3f\n"
//这个延时槽没有合适的指令填充,用 nop 填充
"nop\n"
"lwc1 $f0,0(%0)\n"
"lwc1 $f2,0(%1)\n"
"add $f0,$f0,$f2\n"
"swc1 $f0,0(%0)\n"
"3:\n"
".set reorder\n"
::"r"(a),"r"(b),"r"(LEN)
:"$8","$f0","$f2","memory"
);
}

参考:

http://dev.lemote.com/files/document/loongson/SIMD指令使用手册v0.1.pdf

http://gcc.gnu.org/onlinedocs/gcc/MIPS-Loongson-Built_002din-Functions.html#MIPS-Loongson-Built_002din-Functions

http://gcc.gnu.org/viewcvs/trunk/gcc/testsuite/gcc.target/mips/loongson-simd.c?revision=142875&view=co

相关文章:

此条目发表在 编译技术, 编译理论实践和应用 分类目录,贴了 , , , , , , , , , , , , , , , , , 标签。将固定链接加入收藏夹。

小例子–回眸一笑百媚生》有 9 条评论

  1. zsc 说:

    A union can be initialized like below.

    #include
    union u {
    char c[8];
    unsigned long long ull;
    };

    main(){
    union u u1 = {1,1,1,1,1,1,1,1};
    printf(“%ld\n”,u1.ull);
    }

    • erlv 说:

      @zsc,
      奇怪的问题,我把你union定义的形式修改如下后:
      union u {
      unsigned long long ull;
      char c[8];
      };
      执行结果为1,不做修改,执行结果为16843009

  2. Figo 说:

    你代码插件用的哪个?名字告诉我。

  3. zsc 说:

    checking the assembly,
    union u {
    char c[8];
    unsigned long long ull;
    };
    is
    li $2,1
    sb $2,24($fp)
    li $2,1
    sb $2,25($fp)
    li $2,1
    sb $2,26($fp)

    union u {
    unsigned long long ull;
    char c[8];
    };
    is
    li $2,1 # 0×1
    move $3,$0
    sw $2,24($fp)

    • erlv 说:

      @zsc, 貌似后端问题,X86 Open64中diff了一下两个.B文件,仅在调用printf时指定打印union中的哪个部分时有如下差别:

      diff union.B.ull.t union.B.c.t
      398c398
      < U8U8LDID 0 <2,1,u1> T<64,u,4>

      > U8U8LDID 0 <2,1,u1> T<64,u,4>
      回头我查查C语言的标准去。

  4. nightfire 说:

    呃,好像GCC在x86上会自动生成simd指令吧,龙芯不能自动生成么?

    • erlv 说:

      @nightfire, 你说的是自动向量化吧,GCC应该支持自动向量化,但我没有做过实验。呵呵我这里写的小例子就是想验证程序员自己控制simd的操作,在程序中显式作simd操作:)

  5. Pingback 引用通告: 《编译点滴》半年记 « 编译点滴

发表评论

电子邮件地址不会被公开。 必填项已被标记为 *

*

您可以使用这些 HTML 标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>