This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

C66x关于compiler的优化问题



大家好,

我最近尝试把CPU上跑的程序移植到DSP上。听说代码在DSP上需要进行一定程度的优化才能赶上CPU的性能,所以这段时间我对DSP的优化进行了学习。

我尝试在DSP上测试一些基本的程序,然后查看它的测试结果,但是这其中发现很多我无法解释的问题。首先,我解释介绍一下我的环境:

我用的是TI C6678的EVM开发板,我跑的是SYS/BIOS,我在Core0上建了一个Task,在这个task上我写了一个基本的三重循环的程序:

for (i = 0; i < NI; i++)
{

for (j = 0; j < NJ; j++)
{
    C[i*NJ + j] *= BETA;

    for (k = 0; k < NK; ++k)
    {
        C[i*NJ + j] += ALPHA * A[i*NK + k] * B[k*NJ + j];

     }
}
}

其中NI,NL,NK都是4096.

在运行程序的时候,我发现了两件事情:

1. 当我使用restrict来限定A和B的时候,software pipeline会对程序做一定的优化,理想的运行时间会快三倍左右。但是系统能做出这么大的优化不是很可信。于是,我比较使用和不适用restrict关键词之后输出结果C[ ],然后我发现他们的计算结果存在一些一定的误差。restrict关键词是用在没有bad alias的时候,在程序中A,B,C之间没有数据重叠的情况,我不是很确定为什么restrict会对计算结果产生影响。

2. 我尝试打印一些运行时间,

// // Get Stop Time
// g_ui64StopTime = (uint64_t)(TSCL) ;
// g_ui64StopTime |= (uint64_t)((uint64_t)TSCH << 32 ) ;
// g_ui64ElapsedTime = g_ui64StopTime - g_ui64StartTime;
//
// // Get Start Time
// g_ui64StartTime = (uint64_t)(TSCL) ;
// g_ui64StartTime |= (uint64_t)((uint64_t)TSCH << 32 ) ;

然后发现编译之后software pipeline又出现了不同的优化结果。当我打印每一个k循环的时候,software pipeline显示的理想运行时间又提高了三倍,但是我在实际运行的时候,整体的运行时间却增长了很多。

另一件奇怪的事情是,当我打印每一个J循环的时候,software pipeline并没有什么不同的优化结果,运行的时间约为25分钟,但是如果我不打印任何时间的时候,整个运行的时间是140分钟 (我用手表掐表的)。

这两个问题至今让我摸不着头脑,如果大家有什么经验分享给我,我不胜感激。

  • 首先你要确定一下你定义的三个数组的长度,在循环中是否存在Index超出最大有效范围的可能性。

    如果三个数组不存在重叠,使用restirct可以最大限度给编译器自由去优化循环,三倍的增益也是有可能的。如果循环次数固定的话,建议使用#pragma MUST_ITERATE通知编译器。运行结果不一致是不正常的,你需要仔细检查程序的合理性,

    在循环内部加入打印,会导致编译器无法进行软件流水,不建议这么做。

  • Hi Adam,

    谢谢你的回复。

    1. 我在DRAM里面定义了三个等长的数组,

    #pragma DATA_SECTION(pfBuffer_A, ".ddr3_arr")
    DATA_TYPE pfBuffer_A[(NI+1)*(NJ+1)];

    #pragma DATA_SECTION(pfBuffer_B, ".ddr3_arr")
    DATA_TYPE pfBuffer_B[(NI+1)*(NJ+1)];

    #pragma DATA_SECTION(pfBuffer_C, ".ddr3_arr")
    DATA_TYPE pfBuffer_C[(NI+1)*(NJ+1)];

    但是我只用到了NI*NJ的大小,同时我觉得这三个数组不存在重叠的情况。

    2. 我也使用MUST_ITERATE,但是基本没有什么效果。

    3. 我现在遇到的情况是我在循环里面加入了计算timing的代码之后,执行时间从100分钟减少到25分钟。这个让我百思不得其解。

    这个function是这样的:

    void GEMM_Test(const DATA_TYPE * restrict A, const DATA_TYPE * restrict B, DATA_TYPE * restrict C, int start_x)
    {
    int i,j,k;
    start_x = start_x * NI;
    _nassert((int) A % 8 == 0);
    _nassert((int) B % 8 == 0);
    _nassert((int) C % 8 == 0);

    // Get Start Time
    g_ui64StartTime = (uint64_t)(TSCL) ;
    g_ui64StartTime |= (uint64_t)((uint64_t)TSCH << 32 ) ;

    #pragma MUST_ITERATE(8,,8)
    #pragma UNROLL(8)


    for (i = 0; i < NI; i++)
    {


    if(i%128==0){
    // Get Stop Time
    g_ui64StopTime = (uint64_t)(TSCL) ;
    g_ui64StopTime |= (uint64_t)((uint64_t)TSCH << 32 ) ;
    g_ui64ElapsedTime = g_ui64StopTime - g_ui64StartTime;

    // Get Start Time
    g_ui64StartTime = (uint64_t)(TSCL) ;
    g_ui64StartTime |= (uint64_t)((uint64_t)TSCH << 32 ) ;
    //printf("GEMM single-precision execution time is \t%llu\n",g_ui64ElapsedTime);
    }

    for (j = 0; j < NJ; j++)
    {
    C[i*NJ + j] *= BETA;


    for (k = 0; k < NK; ++k)
    {
    C[i*NJ + j] += ALPHA * A[i*NK + k] * B[k*NJ + j];
    //C[i*NJ + j] += ALPHA * (i*NK + k)*B[k*NJ + j];

    }
    }
    }
    }

  • 两种情况下的计算结果你比较过么?是否计算结果跟你预想的一致? 你可以保留编译时产生的汇编文件,并保留软件流水信息(编译选项加-k, -mw),通过汇编代码分别分析一下循环的ii是多少,来比较两种不同写法循环的优化效果。

    另外你编译选项选择的是什么?

  • Hi Adam,

    两种情况的计算结果我比较过,是一致的。 我保留的软件流水的信息并没有变化,因为我只是在第二重循环里面增加的读取time的代码。

    我的编译选项如下:

    -mv6600 --abi=eabi -O3 --include_path="C:/ti/ccsv5/tools/compiler/c6000_7.4.4/include" --display_error_number --diag_warning=225 --no_bad_aliases --debug_software_pipeline --entry_parm=address --exit_hook=exit_hook --exit_parm=address --entry_hook=entry_hook

    --no_bad_aliases 是 -wt

    --debug_software_pipeline 是-wm

    --entry_parm=address --exit_hook=exit_hook --exit_parm=address --entry_hook=entry_hook 这个是function hook,我想看DSP profile的信息,网上找到的资料说是用function hook。

    如果我哪里有做错的地方,请指正。