哪一个更适合访问数组?

     2023-02-17     116

关键词:

【中文标题】哪一个更适合访问数组?【英文标题】:Which one is more optimized for accessing array? 【发布时间】:2013-02-14 17:57:14 【问题描述】:

解决以下练习:

编写三个不同版本的程序来打印元素 我。一个版本应该使用一个范围来管理迭代, 在使用下标的一种情况下,其他两个应该使用普通的 for 循环 在另一个使用指针。在所有三个程序中写入所有 直接输入。也就是说,不要使用类型别名、auto 或 decltype 简化代码。[C++ Primer]

出现了一个问题:这些访问数组的方法中,哪些在速度方面进行了优化?为什么?


我的解决方案:

    Foreach 循环:

    int ia[3][4]=1,2,3,4,5,6,7,8,9,10,11,12;    
    for (int (&i)[4]:ia)        //1st method using for each loop
        for(int j:i)
            cout<<j<<" ";
    

    嵌套for循环:

    for (int i=0;i<3;i++)       //2nd method normal for loop
        for(int j=0;j<4;j++)
            cout<<ia[i][j]<<" ";
    

    使用指针:

    int (*i)[4]=ia;
    for(int t=0;t<3;i++,t++)  //3rd method.  using pointers.
        for(int x=0;x<4;x++)
            cout<<(*i)[x]<<" ";
    

    使用auto

    for(auto &i:ia)             //4th one using auto but I think it is similar to 1st.  
        for(auto j:i)
             cout<<j<<" ";
    

使用clock()进行基准测试

1st: 3.6  (6,4,4,3,2,3) 
2nd: 3.3  (6,3,4,2,3,2)
3rd: 3.1  (4,2,4,2,3,4)
4th: 3.6  (4,2,4,5,3,4)

每个方法模拟1000次:

1st: 2.29375  2nd: 2.17592  3rd: 2.14383  4th: 2.33333
Process returned 0 (0x0)   execution time : 13.568 s

使用的编译器:MingW 3.2 c++11 标志已启用。 IDE:代码块

【问题讨论】:

AFAIK c 编译器足以优化“正常 for 循环”,所以我更喜欢它(为了可读性) 可读性,可读性,可读性... 如果你真的需要知道,看汇编代码。如果你不知道如何解释它,你不需要知道并且应该为了可读性而编写它并相信编译器会做正确的事情。 针对什么进行了优化?速度?物体大小?可读性?工作保障? @Arpit 我怀疑您使用指针的解决方案是本书要求您使用指针时的意图。我认为它应该是这样的: for (int i = 0, * p = &(ia[0][0]); i 【参考方案1】:

我有一些观察和要点,希望您能从中得到答案。

    第四版,正如您自己所说,与第一版基本相同。 auto 可以被认为只是一种编码快捷方式(这当然不完全正确,因为使用 auto 可能会导致获得与您预期不同的类型,从而导致不同的运行时行为。但大多数时候这是真的。)

    您使用指针的解决方案可能不是人们说他们正在使用指针时的意思!一种解决方案可能是这样的:

    for (int i = 0, *p = &(ia[0][0]); i < 3 * 4; ++i, ++p)
        cout << *p << " ";
    

    或使用两个嵌套循环(这可能毫无意义):

    for (int i = 0, *p = &(ia[0][0]); i < 3; ++i)
        for (int j = 0; j < 4; ++j, ++p)
            cout << *p << " ";
    

    从现在开始,我假设这是您编写的指针解决方案。

    在这种微不足道的情况下,绝对会支配你的运行时间的部分是cout。与执行 I/O 相比,记账和检查循环所花费的时间完全可以忽略不计。因此,您使用哪种循环技术并不重要。

    现代编译器非常擅长优化此类普遍存在的任务和访问模式(迭代数组)。因此,所有这些方法都有可能生成完全相同的代码(指针版本可能例外,即我稍后会谈到。)

    大多数这样的代码的性能将更多地取决于内存访问模式,而不是编译器如何准确生成汇编分支指令(以及其余操作)。这是因为如果所需的内存块不在CPU 缓存,从 RAM 中获取这些字节大约需要数百个 CPU 周期(这只是一个大概的数字)的时间。由于所有示例都以完全相同的顺序访问内存,因此它们在内存和缓存方面的行为将相同,并且运行时间大致相同。

    附带说明,这些示例访问内存的方式是访问内存的最佳方式!线性的,连续的,从头到尾。同样,那里的cout 也存在问题,这可能是一个非常复杂的操作,甚至在每次调用时都会调用操作系统,这可能会导致几乎完全删除(驱逐)从CPU 缓存。

    在 32 位系统和程序上,int 和指针的大小通常相等(都是 32 位!)这意味着您是否传递并使用索引值或指针并不重要成数组。然而,在 64 位系统上,指针是 64 位,但 int 通常仍然是 32 位。这表明在 64 位系统和程序上,通常最好使用数组索引而不是指针(甚至迭代器)。

    在这个特定的例子中,这一点都不重要。

    您的代码非常具体和简单,但一般情况下,向编译器提供尽可能多的有关您的代码的信息几乎总是更好的选择。这意味着您必须使用可用的最窄、最具体的设备来完成工作。这反过来意味着对于编译器而言,通用的for 循环(即for (int i = 0; i &lt; n; ++i))比基于范围的for 循环(即for (auto i : v)更糟糕,因为在后一种情况下编译器只知道您将迭代整个范围,而不是超出它或跳出循环或其他东西,而在通用 for 循环情况下,特别是如果您的代码更复杂,编译器不能确保这一点,并且必须插入额外的检查和测试,以确保代码按照 C++ 标准的要求执行。

    在许多(大多数?)情况下,虽然您可能认为性能很重要,但 它并不。而且大多数时候你重写一些东西来获得性能,你并没有获得太多。在大多数情况下,您获得的性能提升值得您维持可读性和可维护性的损失。因此,请正确设计您的代码和数据结构(并牢记性能),但要避免这种“微优化”,因为它几乎总是值得,甚至还会损害代码的质量。

    一般来说,速度方面的性能非常很难推理。理想情况下,您必须使用可靠的科学测量和统计方法,在真实工作条件下使用真实硬件上的真实数据来测量时间。即使测量一段代码运行所花费的时间也不是一件容易的事。衡量性能很难,推理也更难,但如今它是识别瓶颈和优化代码的唯一方法。

希望我已经回答了你的问题。

编辑:我写了一个非常简单的基准测试你想要做什么。 code is here。它是为 Windows 编写的,应该可以在 Visual Studio 2012 上编译(因为基于范围的 for 循环。)这里是计时结果:

Simple iteration (nested loops): min:0.002140, avg:0.002160, max:0.002739
    Simple iteration (one loop): min:0.002140, avg:0.002160, max:0.002625
   Pointer iteration (one loop): min:0.002140, avg:0.002160, max:0.003149
 Range-based for (nested loops): min:0.002140, avg:0.002159, max:0.002862
 Range(const ref)(nested loops): min:0.002140, avg:0.002155, max:0.002906

相关数字是“最小”时间(对于 1000x1000 阵列,每个测试运行超过 2000 次。)如您所见,测试之间绝对没有区别。请注意,您应该打开编译器优化,否则测试 2 将是一场灾难,案例 4 和案例 5 会比案例 1 和案例 3 稍差。

以下是测试代码:

// 1. Simple iteration (nested loops)
unsigned sum = 0;
for (unsigned i = 0; i < gc_Rows; ++i)
    for (unsigned j = 0; j < gc_Cols; ++j)
        sum += g_Data[i][j];

// 2. Simple iteration (one loop)
unsigned sum = 0;
for (unsigned i = 0; i < gc_Rows * gc_Cols; ++i)
    sum += g_Data[i / gc_Cols][i % gc_Cols];

// 3. Pointer iteration (one loop)
unsigned sum = 0;
unsigned * p = &(g_Data[0][0]);
for (unsigned i = 0; i < gc_Rows * gc_Cols; ++i)
    sum += *p++;

// 4. Range-based for (nested loops)
unsigned sum = 0;
for (auto & i : g_Data)
    for (auto j : i)
        sum += j;

// 5. Range(const ref)(nested loops)
unsigned sum = 0;
for (auto const & i : g_Data)
    for (auto const & j : i)
        sum += j;

【讨论】:

第 3 点:我知道这一点,我故意添加了它,否则我将得到所有的零。但是在所有方法中添加它必须具有相同的效果。在大多数情况下,我最快速地使用第三种方法。为什么? 我想奖励你赏金。但你没有回复我的问题,所以我在等待。 @Arpit 这很难说清楚,因为它很大程度上取决于您的基准测试代码。我猜基准时间的差异来自(a)clock() 具有的低分辨率和精度,(b)cout 和控制台 I/O 代码内部的缓冲和刷新差异和/或(c)多任务性质导致中断和其他进程与您的进程间歇性运行并导致时间上的微小差异的操作系统。 @Arpit 如果你想做一个好的基准测试,我建议至少以下步骤: - 使用更高分辨率和更高精度的计时器,例如Windows 上的 QueryPerformanceCounter() 或 Linux 上的 clock_gettime()。或者您可以在 x86 上使用 RDTSC,但它有其自身的问题。 - 运行更长的循环。为数万或数十万个元素运行一个循环。 - 多次运行每个测试(数十次和数百次)并为每个测试取最低时间值。 - 由于当今 CPU 和缓存的复杂架构,一些小东西实际上无法简单或轻松地进行基准测试。如果可以,请阅读程序集。 @Arpit - 绝对不要使用您不完全熟悉的复杂东西,并且可能会出现不可预测的行为,并且您无法控制高度可变的时间,例如基准测试循环中的 I/O,除非您实际上是在对 I/O 部分进行基准测试!【参考方案2】:

影响它的因素很多:

    这取决于编译器 这取决于使用的编译器标志 这取决于使用的计算机

只有一种方法可以知道确切的答案:测量处理大型数组(可能来自随机数生成器)时使用的时间,这与您已经完成的方法相同,只是数组大小应至少为 1000x1000 .

【讨论】:

fedora与ubuntu:到底哪一个更适合你?

...建议使用两种发行版。本文介绍了Fedora和Ubuntu,以比较哪一个更适合您。Fedora和Ubuntu的基础让我们从基础开始。本文介绍了这两个发行版的最新版本,它们是Fedora32Workstation和Ubuntu20.04LTS。Fedora和Ubuntu都是受欢迎的 查看详情

在 python 中解析 HTML - lxml 或 BeautifulSoup?其中哪一个更适合啥样的目的?

】在python中解析HTML-lxml或BeautifulSoup?其中哪一个更适合啥样的目的?【英文标题】:ParsingHTMLinpython-lxmlorBeautifulSoup?Whichoftheseisbetterforwhatkindsofpurposes?在python中解析HTML-lxml或BeautifulSoup?其中哪一个更适合什么样的目的?【发布时... 查看详情

哪一个更适合 jQuery.ajax 调用? .Net Web 服务还是 .ashx?

】哪一个更适合jQuery.ajax调用?.NetWeb服务还是.ashx?【英文标题】:WhichoneisbetterforjQuery.ajaxcalls?.NetWeb-Serviceoran.ashx?【发布时间】:2011-07-2910:07:40【问题描述】:我最近一直在练习jQuery.ajax()。我已经开始学习调用.Netweb-servicesqithjQu... 查看详情

其中哪一个更适合论坛的 ASP.NET Access 数据库[关闭]

】其中哪一个更适合论坛的ASP.NETAccess数据库[关闭]【英文标题】:WhichoneoftheseisbetterforASP.NETAccessdatabaseforforum[closed]【发布时间】:2014-04-0815:17:07【问题描述】:是有1个表有10000个条目更好,还是有100个表有100个条目更好。这个想... 查看详情

windowsrasdialordotras?哪一个更适合连接和断开

...要将我的应用程序从WindowsXP运行到Windows10,只是想确定哪一个是首选的?谢谢你的想法!答案这完全取决于你想要对你的应用程序做什么。RasDial.exe将让您的应用程序拨打连接,但不会给您太多控制权。DotRas授予您对Win32API的更... 查看详情

图标字体与svg,哪一个更适合与颤动一起使用?

对于使用图标字体和SVG图标的应用程序大小,性能和可扩展性,长期使用应用程序开发哪个更好?答案我认为Icon字体比SVG更好用。因为需要使用插件来添加SVG图像。如果你使用上千个SVG图像app会很慢。 查看详情

LSA 或 BERT 变压器?哪一个更适合用于短句的实时语义相似性和语义聚类? [关闭]

】LSA或BERT变压器?哪一个更适合用于短句的实时语义相似性和语义聚类?[关闭]【英文标题】:LSAorBERTtransformers?Whichoneisbettertouseforreal-timesemanticSimilairtyandsemanticclusteringofshortsentence?[closed]【发布时间】:2022-01-1810:25:55【问题描述... 查看详情

系统测试与端到端测试:哪一个更适合选择?

...版本和质量的版本之间的两难选择,但是两者之间总是有一个很好的平衡。我们都期望速度和质量同时,这是一个相当困难的一个。测试下软件产品的寿命什么是系统测试?为什么系统测试很重要?什么时候开始系统测试?什么... 查看详情

以下哪一个 PHP 数组结构将使用更少的内存?

】以下哪一个PHP数组结构将使用更少的内存?【英文标题】:WhichoneofthefollowingPHParraystructurewouldbeusinglessmemory?【发布时间】:2016-04-1909:41:26【问题描述】:以下哪一项会占用更少的内存?$myArray=array();$myArray[1]=array(1,2,3,4,5,6,7,8,9,10... 查看详情

typescript 访问修饰符和 javascript 访问修饰符有啥区别?在使用打字稿时我应该更喜欢哪一个?

...ipt访问修饰符有啥区别?在使用打字稿时我应该更喜欢哪一个?【英文标题】:Whatarethedifferencesbetweentypescriptaccessmodifiersandjavascriptones?AndwhichoneshouldIpreferwhileusingtypescript?typescript访问修饰符和javascript访问修饰符有什么区别?在使... 查看详情

file_get_contents("php://input") 或 $HTTP_RAW_POST_DATA,哪一个更适合获取 JSON 请求的正文?

】file_get_contents("php://input")或$HTTP_RAW_POST_DATA,哪一个更适合获取JSON请求的正文?【英文标题】:file_get_contents("php://input")or$HTTP_RAW_POST_DATA,whichoneisbettertogetthebodyofJSONrequest?【发布时间】:2011-02-1310:47:03 查看详情

HSM 和 Argon2 的区别?哪一个更可取

】HSM和Argon2的区别?哪一个更可取【英文标题】:DifferencebetweenHSMandArgon2?whichoneispreferrable【发布时间】:2020-03-1421:52:24【问题描述】:我正在开发一个处理客户详细信息的应用程序,我们希望将其以加密形式存储在我们的数据库... 查看详情

移位与数组索引,更适合 32 位 MCU 上的 uart 接口

...32bitMCUs【发布时间】:2019-07-1315:00:00【问题描述】:我有一个带有USARTHAL的嵌入式项目。此USART一次只能发送或接收8或16位(取决于我选择的usart寄存器,即单/双输入/输出)。由于它是一个32 查看详情

将 pandas 数据框转换为 numpy 数组 - 更喜欢哪种方法? [复制]

...。我知道有很多文档可以做到这一点。那么,你更喜欢哪一个?df.valuesdf._as_matrix( 查看详情

大数据:以下数据适合哪种 NoSQL

...示例数据如下所示,其中包含数百万客户,每个客户都有一个朋友列表,其中包含朋友姓名、ID和最喜欢的运动。这里哪个NoSQL数据库适合存储此类数据,以便于访问和频繁更新每个客户好友列表值。Cust1,(aaa,001,cricket),(bbb,00 查看详情

go语言做web应用开发的框架,哪一个更适合入门

...个请求都创建自己的goroutine来处理。Go语言Web框架:beego一个用Go开发的应用框架,思路来自于tornado,路由设计来源于sinatra。支持特性MVC;REST;智能路由;日志调试;配置管理;模板自动渲染;layout设计;中间件插入逻辑;方便... 查看详情

notion和trello相比较,你更看好哪一个?

...前者。notion功能更强大,适合要求高的专业用户。Trello是一个逻辑简单的项目规划类应用,适合普通用户做个人规划应用等。优点是界面美观,模板以及个人项目逻辑友好。其缺点是免费版的Power-Ups,也就是原生插件,只支持一... 查看详情

哪种 Windows IPC 方法更适合短命令?

...)的IPC方法。如果我的应用程序是基于CLI+windows服务,哪一个更好。附:我已经在单独的线程中实现了目标进程(CLI应用程序)中的消息队列。并通过PostThre 查看详情