最近在优化白板书写提速的FrameBuffer库,发现有一个循环:
void drawPixelRect(int x, int y, int width, int height, int* pixels) {
int loc = y * FIXED_WIDTH + x;
int i, j, k = 0;
//int endLoc = (y + height) * FIXED_WIDTH + x + width;
for(i = 0; i < height; i++) {
for(j = 0; j < width; j++) {
fb0[loc + j] = fb1[loc + j] = fb2[loc + j] = pixels[k++];
}
loc += FIXED_WIDTH; //add a row
}
}
这段代码的作用是分别给3个FrameBuffer端赋予同样的一个数据块,但我这个嵌套循环看着很不顺眼,因此优化成了:
void drawPixelRect(int x, int y, int width, int height, int* pixels) {
int loc = y * FIXED_WIDTH + x;
int i, j, k = 0;
//int endLoc = (y + height) * FIXED_WIDTH + x + width;
for(i = 0; i < height; i++) {
memcpy(fb0 + loc, pixels + k, width * sizeof(int));
memcpy(fb1 + loc, pixels + k, width * sizeof(int));
memcpy(fb2 + loc, pixels + k, width * sizeof(int));
k += width;
loc += FIXED_WIDTH; //add a row
}
}
只要一个循环就可以了,而且循环展开之后对CPU流水线利用按我的认知应该会更充分,我认为应该是会更快的。那么究竟有多块呢?
因此我写了个Demo来进行测试:
#include "stdio.h"
#include "stdlib.h"
#include "sys/time.h"
#define TEST_SIZE 100005
int pixels[TEST_SIZE];
void test1(){
int* frame = (int*)malloc(sizeof(int) * 300000);
int* fb0 = frame;
int* fb1 = frame + TEST_SIZE;
int* fb2 = frame + TEST_SIZE * 2;
int i;
struct timeval tv;
int startTime, endTime;
//纪录代码开始时间戳
gettimeofday(&tv, NULL);
startTime = tv.tv_sec * 1000000 + tv.tv_usec; //微秒
for(i = 0; i < TEST_SIZE; i++){
fb0[i] = fb1[i] = fb2[i] = pixels[i];
}
gettimeofday(&tv, NULL);
//纪录代码结束时间戳
endTime = tv.tv_sec * 1000000 + tv.tv_usec; //微秒
printf("used time :%d\n", endTime - startTime);
/*for(i = 0; i < 300000; i++){
printf("%d\t", frame[i]);
}*/
free(frame);
}
void test2(){
int* frame = (int*)malloc(sizeof(int) * 300000);
int* fb0 = frame;
int* fb1 = frame + TEST_SIZE;
int* fb2 = frame + TEST_SIZE * 2;
int i;
struct timeval tv;
int startTime, endTime;
//纪录代码开始时间戳
gettimeofday(&tv, NULL);
startTime = tv.tv_sec * 1000000 + tv.tv_usec; //微秒
//使用memcpy复制数据
memcpy(fb0, pixels, TEST_SIZE * sizeof(int));
memcpy(fb1, pixels, TEST_SIZE * sizeof(int));
memcpy(fb2, pixels, TEST_SIZE * sizeof(int));
//纪录代码结束时间戳
gettimeofday(&tv, NULL);
endTime = tv.tv_sec * 1000000 + tv.tv_usec; //微秒
printf("used time :%d\n", endTime - startTime);
/*for(i = 0; i < 300000; i++){
printf("%d\t", frame[i]);
}*/
free(frame);
}
void test3(){
int* frame = (int*)malloc(sizeof(int) * 300000);
int* fb0 = frame;
int* fb1 = frame + TEST_SIZE;
int* fb2 = frame + TEST_SIZE * 2;
int i;
struct timeval tv;
int startTime, endTime;
//纪录代码开始时间戳
gettimeofday(&tv, NULL);
startTime = tv.tv_sec * 1000000 + tv.tv_usec; //微秒
//使用memcpy和循环混合复制数据
for(i = 0; i < 3; i++){
memcpy(fb0 + TEST_SIZE * i, pixels, TEST_SIZE * sizeof(int));
}
//纪录代码结束时间戳
gettimeofday(&tv, NULL);
endTime = tv.tv_sec * 1000000 + tv.tv_usec; //微秒
printf("used time :%d\n", endTime - startTime);
/*for(i = 0; i < 300000; i++){
printf("%d\t", frame[i]);
}*/
free(frame);
}
int main(){
int i;
for(i = 0; i < TEST_SIZE; i++){
pixels[i] = i;
}
test1();
test2();
test3();
return 0;
}
其中,函数test1使用了循环赋值的方法进行分段给数据,test2没有任何循环,直接用三段memcpy给数据;test3使用了3次循环和memcpy + 偏移地址进行给数据,结果可以发现,循环给数据速度是最慢的,使用memcpy,并且尽可能减少循环的层级,速度则快得多。所以在需要执行效率的模块上,尽可能少用循环,尤其是嵌套循环,并尽可能把循环多展开一些,可以提高程序的执行效率。