Optimize(程序优化)

  1. 更快(本课程重点!)

  2. 更省(存储空间、运行空间)

  3. 更美(UI 交互)

  4. 更正确(本课程重点!各种条件下)

  5. 更可靠

  6. 可移植

  7. 更强大(功能)

  8. 更方便(使用)

  9. 更范(格式符合编程规范、接口规范 )

  10. 更易懂(能读明白、有注释、模块化)

一个图像处理程序实现图像的平滑,其图像分辨率为1920*1080,每一点颜色值为64b,用long img [1920] [1080]存储屏幕上的所有点颜色值,颜色值可从0依行列递增,或真实图像。

平滑算法为:任一点的颜色值为其上下左右4个点颜色的平均值,即:

img[i] [j] = (img [i-1] [j] + img[i+1] [j]+ img[i] [j-1] + img[i] [j+1] ) / 4。

请面向你的CPU与cache,利用本课程学过的优化技术,编写程序,并说明你所采用的优化方法。

关于本次的平滑算法: 不考虑四周,不考虑前后变量改变带来的正确性问题,只作为优化的任务。

但如果考虑其正确性,用一个 dst [1920]] [1080] 存储平滑后的图像值也是可以的。

使用C语言 time.h 库, 获取运行前后时间钟,算出运行时间。

void Test(void (*function)())
{
    clock_t t1 = clock();
    int t = 100;
    while(t--)
    {
        function();
    }
    clock_t t2 = clock();
    printf("COST %ldms\n",(t2 - t1) * 1000 / CLOCKS_PER_SEC);
}

先要写为可优化的版本,所以先枚举列再枚举行。

int i, j;
for(j = 1; j < WIDTH - 1; j ++ )
{
    for(i = 1; i < HEIGHT - 1; i ++ )
    {
        img[i][j] = (img[i - 1][j] + img[i + 1][j] + img[i][j + 1] + img[i][j - 1]) / 4;
    }
}

改为先枚举行,再枚举列。

int i, j;
for(i = 1; i < HEIGHT - 1; i ++ )
{
    for(j = 1; j < WIDTH - 1; j ++ )
    {
        img[i][j] = (img[i - 1][j] + img[i + 1][j] + img[i][j + 1] + img[i][j - 1]) / 4;
    }
}

减少迭代次数。但是实验结果是性能并没有优化(相比较上一个)。

原因应该是:前后变量有运算依赖关系。

int block = 4;
int i, j;

for(i = 1; i < HEIGHT - 1; i ++ )
{
    for(j = 1; j < WIDTH - 4; j += block)
    {
        img[i][j] = (img[i - 1][j] + img[i + 1][j] + img[i][j + 1] + img[i][j - 1]) / 4;
        img[i][j + 1] = (img[i - 1][j + 1] + img[i + 1][j + 1] + img[i][j + 1 + 1] + img[i][j - 1 + 1]) / 4;
        img[i][j + 2] = (img[i - 1][j + 2] + img[i + 1][j + 2] + img[i][j + 1 + 2] + img[i][j - 1 + 2]) / 4;
        img[i][j + 3] = (img[i - 1][j + 3] + img[i + 1][j + 3] + img[i][j + 1 + 3] + img[i][j - 1 + 3]) / 4;
    }
    for(;j < WIDTH - 1; j ++ )
    {
        img[i][j] = (img[i - 1][j] + img[i + 1][j] + img[i][j + 1] + img[i][j - 1]) / 4;
    }
}

既然前后变量有运算依赖关系,那我们就不让有依赖关系,并保持循环展开的形式。

但实验结果是:没有优化多少,这个原因仍没搞懂,或许需要查看汇编代码。

int i, j;
//为什么是14:14|1918
for(i = 1; i < HEIGHT - 1; i ++ )
{
    for(j = 1; j < WIDTH - 1; j += 14)
    {
        img[i][j + 0] = (img[i - 1][j] + img[i + 1][j] + img[i][j + 1] + img[i][j - 1]) / 4;
        img[i][j + 2] = (img[i - 1][j + 2] + img[i + 1][j + 2] + img[i][j + 1 + 2] + img[i][j - 1 + 2]) / 4;
        img[i][j + 4] = (img[i - 1][j + 4] + img[i + 1][j + 4] + img[i][j + 1 + 4] + img[i][j - 1 + 4]) / 4;
        img[i][j + 6] = (img[i - 1][j + 6] + img[i + 1][j + 6] + img[i][j + 1 + 6] + img[i][j - 1 + 6]) / 4;
        img[i][j + 8] = (img[i - 1][j + 8] + img[i + 1][j + 8] + img[i][j + 1 + 8] + img[i][j - 1 + 8]) / 4;
        img[i][j + 10] = (img[i - 1][j + 10] + img[i + 1][j + 10] + img[i][j + 1 + 10] + img[i][j - 1 + 10]) / 4;
        img[i][j + 12] = (img[i - 1][j + 12] + img[i + 1][j + 12] + img[i][j + 1 + 12] + img[i][j - 1 + 12]) / 4;
    }
    for(j = 2; j < WIDTH - 1; j += 14)
    {
        img[i][j + 0] = (img[i - 1][j] + img[i + 1][j] + img[i][j + 1] + img[i][j - 1]) / 4;
        img[i][j + 2] = (img[i - 1][j + 2] + img[i + 1][j + 2] + img[i][j + 1 + 2] + img[i][j - 1 + 2]) / 4;
        img[i][j + 4] = (img[i - 1][j + 4] + img[i + 1][j + 4] + img[i][j + 1 + 4] + img[i][j - 1 + 4]) / 4;
        img[i][j + 6] = (img[i - 1][j + 6] + img[i + 1][j + 6] + img[i][j + 1 + 6] + img[i][j - 1 + 6]) / 4;
        img[i][j + 8] = (img[i - 1][j + 8] + img[i + 1][j + 8] + img[i][j + 1 + 8] + img[i][j - 1 + 8]) / 4;
        img[i][j + 10] = (img[i - 1][j + 10] + img[i + 1][j + 10] + img[i][j + 1 + 10] + img[i][j - 1 + 10]) / 4;
        img[i][j + 12] = (img[i - 1][j + 12] + img[i + 1][j + 12] + img[i][j + 1 + 12] + img[i][j - 1 + 12]) / 4;
    }
}

分块,使每次运算的数据恰好填满cache line,从而减少cache miss

register int i, j;
register int i_, j_;
register int i__, j__;
int block = 8;// 8 * 8 = 64 = cache line
for(i = 1; i < HEIGHT - 1; i += block)
{
    for(j = 1; j < WIDTH - 1; j += block)
    {
        i__ = minn(HEIGHT - 1, i + block);
        j__ = minn(WIDTH - 1, j + block);

        for(i_ = i; i_ < i__; i_ ++)
        {
            for(j_ = j; j_ < j__; j_ ++)
            {
                img[i_][j_] = (img[i_][j_ - 1] + img[i_][j_ + 1] + img[i_ - 1][j_] + img[i_ + 1][j_]) / 4;
            }
        }
    }
}

利用CPU多核的特点,将任务分为多个子任务。

这里使用C语言pthread库。优化效果显著!

点击查看代码

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

#define PTHREAD_NUM 6//&#x7EBF;&#x7A0B;&#x603B;&#x6570;
#define RECNUM 100

typedef struct
{
    int l;
    int r;
}PTH_ARGV;//&#x7EBF;&#x7A0B;&#x53C2;&#x6570;&#x7ED3;&#x6784;&#x4F53;

typedef struct
{
    int a;
}PTH_RETURN;//&#x7EBF;&#x7A0B;&#x8FD4;&#x56DE;&#x503C;&#x7ED3;&#x6784;&#x4F53;

#define HEIGHT 1080
#define WIDTH 1920

long img[HEIGHT][WIDTH];
int maxn(int x, int y)
{
    if(x >= y)
    {
        return x;
    }else
    {
        return y;
    }
}
int minn(int x, int y)
{
    if(x >= y)
    {
        return y;
    }else
    {
        return x;
    }
}

void *func(void *argv)//&#x7EBF;&#x7A0B;&#x51FD;&#x6570;&#x4F53;
{
    PTH_ARGV *pth_argv;
    PTH_RETURN *pth_return = malloc(sizeof(PTH_RETURN));//&#x4E3A;&#x8FD4;&#x56DE;&#x503C;&#x7533;&#x8BF7;&#x7A7A;&#x95F4;
    pth_argv = (PTH_ARGV*)argv;//&#x5C06;&#x53C2;&#x6570;&#x5F3A;&#x8F6C;&#x4E3A;&#x53C2;&#x6570;&#x7ED3;&#x6784;&#x4F53;

    {//&#x7EBF;&#x7A0B;&#x8981;&#x505A;&#x7684;&#x4E8B;&#x60C5;
        register int i, j;
        register int i_, j_;
        register int i__, j__;
        int block = 8;// 8 * 8 = 64 = cache line
        for(i = pth_argv->l; i < pth_argv->r; i += block)
        {
            for(j = 1; j < WIDTH - 1; j += block)
            {
                i__ = minn(pth_argv->r, i + block);
                j__ = minn(WIDTH - 1, j + block);

                for(i_ = i; i_ < i__; i_ ++)
                {
                    for(j_ = j; j_ < j__; j_ ++)
                    {
                        img[i_][j_] = (img[i_][j_ - 1] + img[i_][j_ + 1] + img[i_ - 1][j_] + img[i_ + 1][j_]) / 4;
                    }
                }
            }
        }

    }

    free(argv);//&#x91CA;&#x653E;&#x7EBF;&#x7A0B;&#x53C2;&#x6570;&#x7A7A;&#x95F4;
    /*
    void pthread_exit(void *retval);
    &#x63CF;&#x8FF0;&#xFF1A;&#x7EBF;&#x7A0B;&#x7EC8;&#x6B62;&#xFF1B;&#x7C7B;&#x4F3C;&#x4E8E;exit&#xFF0C;exit&#x662F;&#x8FDB;&#x7A0B;&#x7EC8;&#x6B62;&#xFF0C;&#x4E24;&#x8005;&#x5DEE;&#x8DDD;&#x5728;&#x4E8E;&#x7ED3;&#x675F;&#x7684;&#x5BF9;&#x8C61;&#x4E0D;&#x540C;&#x3002;
    &#x53C2;&#x6570;&#xFF1A;
    retval -- &#x8981;&#x5E26;&#x56DE;&#x7684;&#x503C;,&#x53EF;&#x4EE5;&#x4E3A;NULL&#xFF0C;&#x5982;&#x679C;&#x4E3A;NULL&#xFF0C;&#x5219;&#x4E0D;&#x9700;&#x8981;&#x7EBF;&#x7A0B;&#x8FD4;&#x56DE;&#x503C;&#x7ED3;&#x6784;&#x4F53;&#xFF0C;&#x6BCD;&#x7EBF;&#x7A0B;&#x4E5F;&#x4E0D;&#x4F1A;&#x6536;&#x5230;&#x5B50;&#x7EBF;&#x7A0B;&#x7684;&#x8FD4;&#x56DE;&#x503C;
    */
    pthread_exit(pth_return);//&#x7EBF;&#x7A0B;&#x7ED3;&#x675F;&#xFF0C;&#x8FD4;&#x56DE;&#x6BCD;&#x7EBF;&#x7A0B;&#x9700;&#x8981;&#x7684;&#x8FD4;&#x56DE;&#x503C;&#xFF0C;
}

int main()
{
    pthread_t pd[PTHREAD_NUM];//pid
    PTH_ARGV *pth_argv;//&#x7EBF;&#x7A0B;&#x53C2;&#x6570;
    //PTH_RETURN *pth_return;//&#x7EBF;&#x7A0B;&#x8FD4;&#x56DE;&#x503C;

    int cnt = RECNUM;
    clock_t t1, t2;
    t1 = clock();
    while(cnt --)
    {
        int i;

        for(i = 0;i < PTHREAD_NUM;i ++)
        {
           //&#x4E3A;&#x7EBF;&#x7A0B;&#x53C2;&#x6570;&#x7533;&#x8BF7;&#x7A7A;&#x95F4;&#xFF08;&#x6CE8;&#xFF1A;&#x4E3A;&#x4EC0;&#x4E48;&#x8981;&#x7533;&#x8BF7;&#x7A7A;&#x95F4;&#xFF1F;&#x56E0;&#x4E3A;&#x4E0D;&#x7533;&#x8BF7;&#x7A7A;&#x95F4;&#xFF0C;&#x6240;&#x6709;&#x7EBF;&#x7A0B;&#x516C;&#x7528;&#x540C;&#x610F;&#x53C2;&#x6570;&#x7A7A;&#x95F4;&#xFF0C;&#x5F88;&#x53EF;&#x80FD;&#x53D1;&#x751F;&#x7EBF;&#x7A0B;&#x95F4;&#x7684;&#x62A2;&#x5360;&#x6548;&#x679C;&#xFF09;&#xFF0C;&#x6B64;&#x51FD;&#x6570;&#x9700;&#x8981;&#x7531;&#x5B50;&#x7EBF;&#x7A0B;&#x91CA;&#x653E;&#x6389;

            pth_argv = malloc(sizeof(PTH_ARGV));
            {//&#x5BF9;&#x7EBF;&#x7A0B;&#x53C2;&#x6570;&#x7ED3;&#x6784;&#x4F53;&#x8FDB;&#x884C;&#x521D;&#x59CB;&#x5316;
                pth_argv->l = maxn(1, i * HEIGHT / PTHREAD_NUM);
                pth_argv->r = minn(HEIGHT - 1, (i + 1) * HEIGHT / PTHREAD_NUM);
            }
            /*
        int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);

        &#x63CF;&#x8FF0;&#xFF1A;&#x521B;&#x5EFA;&#x4E00;&#x4E2A;&#x7EBF;&#x7A0B;&#x3002;
        &#x8FD4;&#x56DE;&#x503C;&#xFF1A;&#x6210;&#x529F;&#x8FD4;&#x56DE;0&#xFF0C;&#x5931;&#x8D25;&#x8FD4;&#x56DE;&#x4E00;&#x4E2A;&#x9519;&#x8BEF;&#x7F16;&#x53F7;&#x3002;
        &#x53C2;&#x6570;&#xFF1A;
        thread -- &#x56DE;&#x586B;&#x521B;&#x5EFA;&#x7684;&#x7EBF;&#x7A0B;&#x7684;PID&#x3002;
        attr -- &#x7279;&#x6B8A;&#x8981;&#x6C42;&#x3002;&#x9ED8;&#x8BA4;&#x4E3A;NULL.

        start_routine --  &#x88AB;&#x521B;&#x5EFA;&#x7684;&#x7EBF;&#x7A0B;&#x6240;&#x6267;&#x884C;&#x7684;&#x51FD;&#x6570;&#x3002;
                void *(*start_routine) (void *)
        arg -- start_routine&#x51FD;&#x6570;&#x7684;&#x4F20;&#x53C2;&#x3002;
        */
            pthread_create(pd + i,NULL,func,pth_argv);//&#x521B;&#x5EFA;&#x7EBF;&#x7A0B;
        }

        for(i = 0;i<pthread_num;i++) 1000 { * int pthread_join(pthread_t thread, void **retval); 描述:给线程号为thread的线程收尸(线程结束后会变成僵尸线程(不占用空间,但占用线程号),父线程需要等待子线程结束,然后释放掉线程的线程号), 一般是谁创建谁收尸(不是铁律,线程之间平等),可以起到阻塞非盲等的状态。 返回值:成功时返回 0;出错时,它返回一个错误编号。 参数: thread -- 线程id retval 回填pid为thread的线程的的返回值,可以为null,为null时,父线程将不在接收到子线程回传的返回值。 pthread_join(pd[i],(void **)&pth_return); 等待线程结束 pthread_join(pd[i],null); free(pth_return); 释放掉线程返回值结构体 } t2="clock();" printf("cost %ldms\n",(t2 - t1) clocks_per_sec); return 0; < code></pthread_num;i++)></time.h></pthread.h></stdlib.h></stdio.h>

也是没有明显的优化效果。

register int i, j;
register int i_, j_;
register int i__, j__;
int block = 8;
int id = fork();

if(id == 0)
{
    for(i = 1; i < HEIGHT / 2; i += block)
    {
        for(j = 1; j < WIDTH - 1; j += block)
        {
            i__ = minn(HEIGHT / 2, i + block);
            j__ = minn(WIDTH - 1, j + block);
            for(i_ = i; i_ < i__; i_ ++)
            {
                for(j_ = j; j_ < j__; j_ ++)
                {
                    img[i_][j_] = (img[i_][j_ - 1] + img[i_][j_ + 1] + img[i_ - 1][j_] + img[i_ + 1][j_]) / 4;
                }
            }
        }
    }
    exit(0);
}
else
{
    for(i = HEIGHT / 2; i < HEIGHT - 1; i += block)
    {
        for(j = 1; j < WIDTH - 1; j += block)
        {
            i__ = minn(HEIGHT - 1, i + block);
            j__ = minn(WIDTH - 1, j + block);
            for(i_ = i; i_ < i__; i_ ++)
            {
                for(j_ = j; j_ < j__; j_ ++)
                {
                    img[i_][j_] = (img[i_][j_ - 1] + img[i_][j_ + 1] + img[i_ - 1][j_] + img[i_ + 1][j_]) / 4;
                }
            }
        }
    }
}

Original: https://www.cnblogs.com/Az1r/p/16825441.html
Author: 江水为竭
Title: Optimize(程序优化)

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/684639/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球