向循环中添加代码如何使其速度更快?

2024-04-21

我有一个带有内部循环的简单函数 - 它缩放输入值,在查找表中查找输出值,并将其复制到目的地。 (ftol_ambient是我从网上复制的一个技巧,用于将float快速转换为int)。

for (i = 0;  i < iCount;  ++i)
{
    iScaled = ftol_ambient(*pSource * PRECISION3);
    if (iScaled <= 0)
        *pDestination = 0;
    else if (iScaled >= PRECISION3)
        *pDestination = 255;
    else
    {
        iSRGB = FloatToSRGBTable3[iScaled];
        *pDestination = iSRGB;
    }
    pSource++;
    pDestination++;
}

现在我的查找表是有限的,而浮点数是无限的,因此可能会出现差一错误。我使用一些代码创建了该函数的副本来处理这种情况。请注意,唯一的区别是添加了 2 行代码 - 请忽略丑陋的指针转换。

for (i = 0;  i < iCount;  ++i)
{
    iScaled = ftol_ambient(*pSource * PRECISION3);
    if (iScaled <= 0)
        *pDestination = 0;
    else if (iScaled >= PRECISION3)
        *pDestination = 255;
    else
    {
        iSRGB = FloatToSRGBTable3[iScaled];
        if (((int *)SRGBCeiling)[iSRGB] <= *((int *)pSource))
            ++iSRGB;
        *pDestination = (unsigned char) iSRGB;
    }
    pSource++;
    pDestination++;
}

这是奇怪的部分。我正在使用 100000 个元素的相同输入测试这两个版本,重复 100 次。在我的 Athlon 64 1.8 GHz(32 位模式)上,第一个函数需要 0.231 秒,第二个(较长)函数需要 0.185 秒。这两个函数在同一源文件中相邻,因此不可能有不同的编译器设置。我已经多次运行测试,颠倒它们运行的​​顺序,并且每次的时间都大致相同。

我知道现代处理器有很多谜团,但这怎么可能呢?

这里用于比较的是 Microsoft VC++6 编译器的相关汇编程序输出。


; 173  :    for (i = 0;  i < iCount;  ++i)

$L4455:

; 174  :    {
; 175  :        iScaled = ftol_ambient(*pSource * PRECISION3);

    fld DWORD PTR [esi]
    fmul    DWORD PTR __real@4@400b8000000000000000
    fstp    QWORD PTR $T5011[ebp]

; 170  :    int i;
; 171  :    int iScaled;
; 172  :    unsigned int iSRGB;

    fld QWORD PTR $T5011[ebp]

; 173  :    for (i = 0;  i < iCount;  ++i)

    fistp   DWORD PTR _i$5009[ebp]

; 176  :        if (iScaled <= 0)

    mov edx, DWORD PTR _i$5009[ebp]
    test    edx, edx
    jg  SHORT $L4458

; 177  :            *pDestination = 0;

    mov BYTE PTR [ecx], 0

; 178  :        else if (iScaled >= PRECISION3)

    jmp SHORT $L4461
$L4458:
    cmp edx, 4096               ; 00001000H
    jl  SHORT $L4460

; 179  :            *pDestination = 255;

    mov BYTE PTR [ecx], 255         ; 000000ffH

; 180  :        else

    jmp SHORT $L4461
$L4460:

; 181  :        {
; 182  :            iSRGB = FloatToSRGBTable3[iScaled];
; 183  :            *pDestination = (unsigned char) iSRGB;

    mov dl, BYTE PTR _FloatToSRGBTable3[edx]
    mov BYTE PTR [ecx], dl
$L4461:

; 184  :        }
; 185  :        pSource++;

    add esi, 4

; 186  :        pDestination++;

    inc ecx
    dec edi
    jne SHORT $L4455

$L4472:

; 199  :    {
; 200  :        iScaled = ftol_ambient(*pSource * PRECISION3);

    fld DWORD PTR [esi]
    fmul    DWORD PTR __real@4@400b8000000000000000
    fstp    QWORD PTR $T4865[ebp]

; 195  :    int i;
; 196  :    int iScaled;
; 197  :    unsigned int iSRGB;

    fld QWORD PTR $T4865[ebp]

; 198  :    for (i = 0;  i < iCount;  ++i)

    fistp   DWORD PTR _i$4863[ebp]

; 201  :        if (iScaled <= 0)

    mov edx, DWORD PTR _i$4863[ebp]
    test    edx, edx
    jg  SHORT $L4475

; 202  :            *pDestination = 0;

    mov BYTE PTR [edi], 0

; 203  :        else if (iScaled >= PRECISION3)

    jmp SHORT $L4478
$L4475:
    cmp edx, 4096               ; 00001000H
    jl  SHORT $L4477

; 204  :            *pDestination = 255;

    mov BYTE PTR [edi], 255         ; 000000ffH

; 205  :        else

    jmp SHORT $L4478
$L4477:

; 206  :        {
; 207  :            iSRGB = FloatToSRGBTable3[iScaled];

    xor ecx, ecx
    mov cl, BYTE PTR _FloatToSRGBTable3[edx]

; 208  :            if (((int *)SRGBCeiling)[iSRGB] <= *((int *)pSource))

    mov edx, DWORD PTR _SRGBCeiling[ecx*4]
    cmp edx, DWORD PTR [esi]
    jg  SHORT $L4481

; 209  :                ++iSRGB;

    inc ecx
$L4481:

; 210  :            *pDestination = (unsigned char) iSRGB;

    mov BYTE PTR [edi], cl
$L4478:

; 211  :        }
; 212  :        pSource++;

    add esi, 4

; 213  :        pDestination++;

    inc edi
    dec eax
    jne SHORT $L4472

Edit: Trying to test Nils Pipenbrinck's hypothesis https://stackoverflow.com/questions/688325/how-can-adding-code-to-a-loop-make-it-faster/688416#688416, I added a couple of lines before and inside of the loop of the first function:
int one = 1;
int two = 2;

        if (one == two)
            ++iSRGB;

第一个函数的运行时间现已降至 0.152 秒。有趣的。


Edit 2: Nils pointed out that the comparison would be optimized out of a release build, and indeed it is. The changes in the assembly code are very subtle, I will post it here to see if it provides any clues. At this point I'm wondering if it's code alignment?
; 175  :    for (i = 0;  i < iCount;  ++i)

$L4457:

; 176  :    {
; 177  :        iScaled = ftol_ambient(*pSource * PRECISION3);

    fld DWORD PTR [edi]
    fmul    DWORD PTR __real@4@400b8000000000000000
    fstp    QWORD PTR $T5014[ebp]

; 170  :    int i;
; 171  :    int iScaled;
; 172  :    int one = 1;

    fld QWORD PTR $T5014[ebp]

; 173  :    int two = 2;

    fistp   DWORD PTR _i$5012[ebp]

; 178  :        if (iScaled <= 0)

    mov esi, DWORD PTR _i$5012[ebp]
    test    esi, esi
    jg  SHORT $L4460

; 179  :            *pDestination = 0;

    mov BYTE PTR [edx], 0

; 180  :        else if (iScaled >= PRECISION3)

    jmp SHORT $L4463
$L4460:
    cmp esi, 4096               ; 00001000H
    jl  SHORT $L4462

; 181  :            *pDestination = 255;

    mov BYTE PTR [edx], 255         ; 000000ffH

; 182  :        else

    jmp SHORT $L4463
$L4462:

; 183  :        {
; 184  :            iSRGB = FloatToSRGBTable3[iScaled];

    xor ecx, ecx
    mov cl, BYTE PTR _FloatToSRGBTable3[esi]

; 185  :            if (one == two)
; 186  :                ++iSRGB;
; 187  :            *pDestination = (unsigned char) iSRGB;

    mov BYTE PTR [edx], cl
$L4463:

; 188  :        }
; 189  :        pSource++;

    add edi, 4

; 190  :        pDestination++;

    inc edx
    dec eax
    jne SHORT $L4457

我的猜测是,在第一种情况下,两个不同的分支最终出现在 CPU 上的同一个分支预测槽中。如果这两个分支每次预测都不同,代码就会变慢。

在第二个循环中,添加的代码可能足以将分支之一移动到不同的分支预测时隙。

为了确保您可以尝试使用 Intel VTune 分析器或 AMD CodeAnalyst 工具。这些工具将向您展示代码中到底发生了什么。

但是,请记住,很可能不值得进一步优化此代码。如果您将代码调整为在您的 CPU 上运行得更快,那么它在不同品牌上可能会同时变得更慢。


EDIT:

如果您想了解分支预测,请尝试 Agner Fog 的优秀网站:http://www.agner.org/optimize/ http://www.agner.org/optimize/

该 pdf 详细解释了分支预测时隙分配:http://www.agner.org/optimize/microarchitecture.pdf http://www.agner.org/optimize/microarchitecture.pdf

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

向循环中添加代码如何使其速度更快? 的相关文章

  • 从实体获取单列

    如何从查询中获取单个列而不是整个对象 我可以这样做来获取整个对象 但我想要的只是名称 IList
  • OpenGL缓冲区更新[重复]

    这个问题在这里已经有答案了 目前我正在编写一个模拟水的程序 以下是我所做的步骤 创建水面 平面 创建VAO 创建顶点缓冲区对象 在其中存储法线和顶点 将指针绑定到此 VBO 创建索引缓冲区对象 然后我使用 glDrawElements 渲染
  • libtool 在 Ubuntu 13.04 上构建 thrift 0.9.1 时出错

    在 Ubuntu 13 04 上构建 thrift 0 9 1 支持 C C java C perl python 时出现此错误 configure 不带任何选项运行 make 不带任何选项运行 Making all in test mak
  • 如何调整 Windows 窗体以适应任何屏幕分辨率?

    我知道这是重复的问题 但我检查了所有其他相关问题 他们的答案没有帮助 结果仍然与屏幕截图 2 中所示相同 我是 C Windows 窗体新手 如截图1所示 我有Form1有一些控件 每组控件都放在一个面板中 我在 PC1 中设计了应用程序
  • 如何查明 .exe 是否正在 C++ 中运行?

    给定进程名称 例如 程序 exe C 标准库没有这样的支持 您需要一个操作系统 API 来执行此操作 如果这是 Windows 那么您将使用 CreateToolhelp32Snapshot 然后使用 Process32First 和 Pr
  • 以下 PLINQ 代码没有改进

    我没有看到使用以下代码的处理速度有任何改进 IEnumerable
  • Nhibernate:连接表并从其他表获取单列

    我有以下表格 create table Users Id uniqueidentifier primary key InfoId uniqueidentifier not null unique Password nvarchar 255
  • 名称查找、实例化点 (POI) 和基本类型

    以下代码针对 X 进行编译 但不适用于 double struct X void foo double void foo X namespace NN struct A void foo A foo double error foo not
  • 如何设置消息队列的所有者?

    System Messaging MessageQueue 类不提供设置队列所有权的方法 如何以编程方式设置 MSMQ 消息队列的所有者 简短的答案是 p invoke 对 windows api 函数的调用MQSetQueueSecuri
  • 从点云检测平面集

    我有一组点云 我想测试3D房间中是否有角落 所以我想讨论一下我的方法 以及在速度方面是否有更好的方法 因为我想在手机上测试它 我将尝试使用霍夫变换来检测线 然后我将尝试查看是否有三条线相交 并且它们也形成了两个相交的平面 如果点云数据来自深
  • 在 C++ 代码 gdb 中回溯指针

    我在运行 C 应用程序时遇到段错误 在 gdb 中 它显示我的一个指针位置已损坏 但我在应用程序期间创建了 10 万个这样的对象指针 我怎样才能看到导致崩溃的一个 我可以在 bt 命令中执行任何操作来查看该指针的生命周期吗 谢谢 鲁奇 据我
  • WinForms - 加载表单时如何使用 PaintEventArgs 运行函数?

    我试图理解图形 在 Graphics FromImage 文档中 它有这样的示例 private void FromImageImage PaintEventArgs e Create image Image imageFile Image
  • 测验;这个编译了吗?如果是的话它会返回什么(我知道答案)

    我最近发现这个错字 if name find string npos 显然开发者的意思是输入 if name find string npos 但令我惊讶的是发现错误甚至编译 Wall Werror 没有尝试过 pedantic 那么 咖啡
  • C++ 模板可以提供 N 个给定类的公共父类吗?

    我正在寻找一个 C 模板 它可以找到一组给定类的共同父级 例如 class Animal class Mammal public Animal class Fish public Animal class Cat public Mammal
  • 使用 Unity 在 C# 中发送 http 请求

    如何使用 Unity 在 C 中发送 HTTP GET 和 POST 请求 我想要的是 在post请求中发送json数据 我使用Unity序列化器 所以不需要 新的 我只想在发布数据中传递一个字符串并且能够 将 ContentType 设置
  • 如何测试某些代码在 C++ 中无法编译? [复制]

    这个问题在这里已经有答案了 可能的重复 单元测试编译时错误 https stackoverflow com questions 605915 unit test compile time error 我想知道是否可以编写一种单元测试来验证给
  • 与 Entity Framework Core 2.0 的一对零关系

    我正在使用 C 和 NET Framework 4 7 将 Entity Framework 6 1 3 Code First 库迁移到 Entity Framework Core 我一直在用 Google 搜索 Entity Framew
  • 值和类型的简洁双向静态 1:1 映射

    我将从我想象如何使用我想要创建的代码开始 它不必完全像这样 但它是我在标题中所说的 简洁 的一个很好的例子 就我而言 它是将类型映射到相关的枚举值 struct bar foo
  • 运行 xunit 测试时无法将输出打印到控制台窗口

    public class test2InAnotherProject private readonly ITestOutputHelper output public test2InAnotherProject ITestOutputHel
  • Emacs C++,打开相应的头文件

    我是 emacs 新手 我想知道 是否有在头文件 源文件和相应的源文件 头文件之间切换的快捷方式 是否有像通用 emacs 参考卡那样的参考卡 Thanks There s ff find other file 您可以使用以下方法将其绑定到

随机推荐

  • excel+powerpoint 如何决定重新缩放粘贴为图像的范围?

    我注意到 当您复制 Excel 中的范围 复制为图片 如屏幕所示 并将其粘贴到 PowerPoint 中时 生成的图像不会 100 缩放为原始图像 右键单击图像 转到格式设置并转到尺寸以查看比例信息 此外 这种缩放比例在不同的计算机上有所不
  • 如何使用 C API 创建 datetime64 对象的 Numpy 数组?

    我需要从 C C 代码创建 numpy datetime64 对象的数组 正如你所看到的NPY LONGLONG and NPY VOID我做到了 我需要做同样的事情NPY DATETIME type PyObject arr1 PyArr
  • 在 Javascript/jQuery 中从数组中删除多个元素

    我有两个数组 第一个数组包含一些值 而第二个数组包含应从第一个数组中删除的值的索引 例如 var valuesArr new Array v1 v2 v3 v4 v5 var removeValFromIndex new Array 0 2
  • 从头开始一个 TDD 项目

    我读了很多关于 TDD 的问答和关于 SO 的单元测试 但我没有找到任何答案 我从哪里开始 我和团队已经完成了几个项目 在这些项目中 我们对代码采用了单元测试 但先编码 然后单元测试 在开发过程的某个阶段 先编写测试然后编写代码变得很自然
  • Manatee.Trello 移动卡片

    我正在编写一个小型应用程序来管理 Trello Boards 的几个方面 例如对列表中的卡片进行排序 根据截止日期和 或标签移动 复制卡片 定期归档列表以及根据标签生成报告 因此 我一直在 Manatee Trello 库周围构建一个外观
  • Picasso 加载 AsyncTask 内生成的图像

    所以我正在尝试使用毕加索图书馆 http square github io picasso 用于图像下载和缓存 为了让 contactUri 传递给Picasso我需要向联系人查询Content Provider 由于我不想阻止主 UI 线
  • ETL 工具...它们到底做什么?请通俗地说[关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 我最近接触了一些 ETL 工具 例如 Talend 和 Apatar 我想知道这些工具通俗地说的用途 主要目标到底是什么 谁主要使用它
  • UICollectionView 断言失败

    我在执行时遇到此错误insertItemsAtIndexPaths in UICollectionView 断言失败 UICollectionViewData indexPathForItemAtGlobalIndex SourceCach
  • java.time中,月份相加的结果是如何计算的?

    在 JSR 310 中java timeJDK 8中的API 日期加月份结果的计算规则是什么 特别是 当您向 1 月 31 日这样的日期添加 1 个月时会发生什么 LocalDate initial LocalDate of 2012 1
  • UglifyJS 和 webpack v5

    我们的 React 代码一直使用 UglifyJS 和 webpack v4 但随后更新为 webpack v5 看来 UglifyJS 不适用于 webpack v5 还有其他选择吗 我们需要一些可以与 babel loader 配合使用
  • Cygwin git 将错误的路径传递给我的编辑器以获取提交消息

    我在 cygwin 下使用 git 但它没有将提交消息的正确路径传递给我的编辑器 我正在位于我的驱动器上的测试存储库中工作 d X git myproject 在 cygwin 终端中 该路径显示为 cygdrive d X git myp
  • 在 KQL azure 显示器中显示今天、昨天、一周的最佳方式

    我试图显示今天的计数 9 滚动 24 小时 与昨天 再次滚动 对比每周平均值 虽然我已经有了可以工作的代码 但我也遇到了错误 错误是 查询成功 但有警告 处理查询时出现一些错误 部分查询失败 未指定的错误 消息 分片 5eeb9282 08
  • 未找到入口点

    当我尝试运行链接到 DLL 的应用程序 我有一段时间没有更改代码 因为它工作正常 时 遇到了一个奇怪的错误 这个 DLL 曾经可以工作 但我一直在更改 DLL 的代码并使其编译正常 不幸的是 当尝试运行该应用程序时 GameTest001
  • 如何解压或获取存储在 Azure DevOps Build Piplines 变量组中的嵌套变量的值?

    是否可以在 Powershell 脚本中的变量内使用 Azure Pipelines 变量 SCENARIO Azure Pipelines 变量组中设置了两个变量 DeploymentCredentials a DeployUATApiP
  • NHibernate Envers 喜欢使用实体框架 6+ 进行审计日志

    每当更新 删除实体时 NHibernate Envers 都能很好地创建审核日志 基本上 它为每个可审计实体创建一个审计表 并将数据快照写入审计表中 例如如果客户记录保存在 CUSTOMER 表中 则客户记录的审核日志将保存在 CUSTOM
  • 如何获取包含表的列名的数组

    我需要一个包含表的列名的数组 有什么想法如何使用 Rails 3 0 0rc 做到这一点吗 假设您有一个 Post 模型 Post column names or Post columns map column column name 它将
  • 如何在执行脚本之前等待 data-main 被 Require.JS 加载?

    我有以下代码 Inside example1我正在要求一些组件的路径应该设置在scripts main 但是 路径设置不正确 根据控制台反馈判断 这使我相信 require js 尚未完成加载中引用的文件scripts main 这一切都很
  • 无效的日期时间格式:1292 不正确的日期时间值 - Laravel 5.2

    当我将这些值插入数据库表时 出现此错误 SQLSTATE 22007 Invalid datetime format 1292 Incorrect datetime value 24 06 2017 for column registrat
  • Grails 使用来自 POST 请求的 XML 填充参数

    我有一个通过 grails 控制器公开的 RestFull grails api 我需要自动绑定 POST 请求中发送的 xml 数据 我不认为使用groovybindData object params 工作作为params参考似乎没有
  • 向循环中添加代码如何使其速度更快?

    我有一个带有内部循环的简单函数 它缩放输入值 在查找表中查找输出值 并将其复制到目的地 ftol ambient是我从网上复制的一个技巧 用于将float快速转换为int for i 0 i lt iCount i iScaled ftol