for循环效率

本文探讨了在不同语言中,二维数组遍历顺序(先行后列 vs 先列后行)对效率的影响。通过实例分析,展示了在C/C++中先行后列的遍历方式可能导致更少的页面调度,从而提高效率。并通过VS2015的代码测试,观察到在不同场景下遍历时间的变化趋势,强调遍历顺序与效率之间的关系可能与cache命中率有关。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

《高质量程序设计语言》提到for循环遍历数组中“先行后列”和“先列后行”有不同的效率。这个勾起了我的兴趣,原文介绍的应该是针对二维数组及其以上的多维数组。效率不同的原因是不同的语言安排数组元素的存储空间的顺序不一样。比如C/C++语言按照“先行后列”的顺序安排数组元素的存储空间。Fortran语言按照“先列后行”的顺序安排数组元素的存储空间。会导致什么后果呢?
引用书中的例子来说明,假设二维数组num[N][M],每一行数据刚好占据一页内存(4KB),且C/C++语言按照“先行后列”的顺序安排数组元素的存储空间,如下图所示:
二维数组
我们来看看不同的循环遍历顺序需要多少次页面调度?
1、“先行后列”。从图中可以发现,一共需要进行12次页面调度。
2、“先列后行”。从图中可以发现,一共需要12 * 1024次页面调度。
这才是两种遍历顺序导致的效率不同的主要原因。

————————————————–分割线———————————————————

OK下面进行代码测试,测试平台是VS2015。测试情况分为四种:
1、创建一个二维数组,先进行“先行后列”后进行“先列后行”遍历。
2、创建一个二维数组,先进行“先列后行”后进行“先行后列”遍历。
3、创建一个二维数组,进行“先列后行”遍历。
4、创建一个二维数组,进行“先行后列”遍历。
代码如下

#include "stdafx.h"

int main()
{
    int N, M, i,j,k;
    int **num = NULL;
    DWORD start, end;
    while (fscanf_s(stdin, "%d%d", &N, &M) != EOF) {
        //创建二维数组
        num = (int **)malloc(N*sizeof(int *));
        if (NULL == num) {
            printf("malloc error\n");
            return -1;
        }
        for (i = 0; i < N; i++) {
            num[i] = (int *)malloc(M*sizeof(int));
            if (NULL == num[i]) {
                printf("malloc error\n");
                k = i;
                goto error;
            }
        }
        //先行后列
        start = GetTickCount();
        for (i = 0; i < N; i++)
            for (j = 0; j < M; j++)
                num[i][j] = 1;
        end = GetTickCount();
        printf("first row next colum: \n");
        printf("N=%d, M=%d : %dms\n",N,M,end-start);
        //先列后行
        start = GetTickCount();
        for (i = 0; i < M; i++)
            for (j = 0; j < N; j++)
                num[j][i] = 1;
        end = GetTickCount();
        printf("first colum next row: \n");
        printf("N=%d, M=%d : %dms\n", N, M, end - start);
        //释放二维数组
        for (i = 0; i < N; i++)
            free(num[i]);
        free(num);
        printf("\n");
    }
    return 0;
error:
    if (NULL != num) {
        for (i = 0; i < k; i++) {
            if (NULL != num[i])
                free(num[i]);
        }
        free(num);
    }
    return -1;
}

测试结果
测试情况1
for效率
测试情况2
for效率
测试情况3
for效率
测试情况4
for效率

结论(限于测试次数的原因,调度遍历方式对遍历时间的影响只作为观测结果)
调度遍历方式顺序一:“先行后列”-> “先列后行”;
调度遍历方式顺序二:“先列后行”-> “先行后列”;
1、当列数远大于行数时,“先行后列”的遍历时间最小。但在本次测试中发现是与调度遍历方式的顺序无关。
2、当行数远大于列数时,“先行后列”的遍历时间最小。调度顺序二明显使整体的遍历时间变小,但是增加了“先行后列”的遍历时间,减小了“先列后行”的遍历时间。
3、当行列数相等时,“先行后列”的遍历时间最小。调度顺序基本无影响。
4、当行列数都比较小时,遍历顺序基本无影响。
具体的解释可能与cache命中率有关,这里没有进行进一步的分析==

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值