SSE 内在函数和循环展开

2024-01-04

我正在尝试优化一些循环并且已经成功,但我想知道我是否只部分正确地完成了它。举例来说,我有这个循环:

for(i=0;i<n;i++){
b[i] = a[i]*2;
}

将其展开 3 倍,得到:

int unroll = (n/4)*4;
for(i=0;i<unroll;i+=4)
{
b[i] = a[i]*2;
b[i+1] = a[i+1]*2;
b[i+2] = a[i+2]*2;
b[i+3] = a[i+3]*2;
}
for(;i<n;i++)
{
b[i] = a[i]*2;
} 

现在是 SSE 的等效翻译:

__m128 ai_v = _mm_loadu_ps(&a[i]);
__m128 two_v = _mm_set1_ps(2);
__m128 ai2_v = _mm_mul_ps(ai_v, two_v);
_mm_storeu_ps(&b[i], ai2_v);

或者是:

__m128 ai_v = _mm_loadu_ps(&a[i]);
__m128 two_v = _mm_set1_ps(2);
__m128 ai2_v = _mm_mul_ps(ai_v, two_v);
_mm_storeu_ps(&b[i], ai2_v);

__m128 ai1_v = _mm_loadu_ps(&a[i+1]);
__m128 two1_v = _mm_set1_ps(2);
__m128 ai_1_2_v = _mm_mul_ps(ai1_v, two1_v);
_mm_storeu_ps(&b[i+1], ai_1_2_v);

__m128 ai2_v = _mm_loadu_ps(&a[i+2]);
__m128 two2_v = _mm_set1_ps(2);
__m128 ai_2_2_v = _mm_mul_ps(ai2_v, two2_v);
_mm_storeu_ps(&b[i+2], ai_2_2_v);

__m128 ai3_v = _mm_loadu_ps(&a[i+3]);
__m128 two3_v = _mm_set1_ps(2);
__m128 ai_3_2_v = _mm_mul_ps(ai3_v, two3_v);
_mm_storeu_ps(&b[i+3], ai_3_2_v);

我对代码部分有点困惑:

for(;i<n;i++)
{
b[i] = a[i]*2;
}

这是做什么的?是否只是做额外的部分,例如,如果循环不能被您选择展开它的因子整除?谢谢。


答案是第一个块:

    __m128 ai_v = _mm_loadu_ps(&a[i]);
    __m128 two_v = _mm_set1_ps(2);
    __m128 ai2_v = _mm_mul_ps(ai_v,two_v);
    _mm_storeu_ps(&b[i],ai2_v);

它已经一次需要四个变量。

这是完整的程序,其中注释掉了等效的代码部分:

#include <iostream>

int main()
{
    int i{0};
    float a[10] ={1,2,3,4,5,6,7,8,9,10};
    float b[10] ={0,0,0,0,0,0,0,0,0,0};

    int n = 10;
    int unroll = (n/4)*4;
    for (i=0; i<unroll; i+=4) {
        //b[i] = a[i]*2;
        //b[i+1] = a[i+1]*2;
        //b[i+2] = a[i+2]*2;
        //b[i+3] = a[i+3]*2;
        __m128 ai_v = _mm_loadu_ps(&a[i]);
        __m128 two_v = _mm_set1_ps(2);
        __m128 ai2_v = _mm_mul_ps(ai_v,two_v);
        _mm_storeu_ps(&b[i],ai2_v);
    }

    for (; i<n; i++) {
        b[i] = a[i]*2;
    }

    for (auto i : a) { std::cout << i << "\t"; }
    std::cout << "\n";
    for (auto i : b) { std::cout << i << "\t"; }
    std::cout << "\n";

    return 0;
}

至于效率;看来我的系统上的程序集生成了movups说明,而手卷代码可以使用movaps这应该更快。

我使用以下程序进行了一些基准测试:

#include <iostream>
//#define NO_UNROLL
//#define UNROLL
//#define SSE_UNROLL
#define SSE_UNROLL_ALIGNED

int main()
{
    const size_t array_size = 100003;
#ifdef SSE_UNROLL_ALIGNED
    __declspec(align(16)) int i{0};
    __declspec(align(16)) float a[array_size] ={1,2,3,4,5,6,7,8,9,10};
    __declspec(align(16)) float b[array_size] ={0,0,0,0,0,0,0,0,0,0};
#endif
#ifndef SSE_UNROLL_ALIGNED
    int i{0};
    float a[array_size] ={1,2,3,4,5,6,7,8,9,10};
    float b[array_size] ={0,0,0,0,0,0,0,0,0,0};
#endif

    int n = array_size;
    int unroll = (n/4)*4;


    for (size_t j{0}; j < 100000; ++j) {
#ifdef NO_UNROLL
        for (i=0; i<n; i++) {
            b[i] = a[i]*2;
        }
#endif
#ifdef UNROLL
        for (i=0; i<unroll; i+=4) {
            b[i] = a[i]*2;
            b[i+1] = a[i+1]*2;
            b[i+2] = a[i+2]*2;
            b[i+3] = a[i+3]*2;
        }
#endif
#ifdef SSE_UNROLL
        for (i=0; i<unroll; i+=4) {
            __m128 ai_v = _mm_loadu_ps(&a[i]);
            __m128 two_v = _mm_set1_ps(2);
            __m128 ai2_v = _mm_mul_ps(ai_v,two_v);
            _mm_storeu_ps(&b[i],ai2_v);
        }
#endif
#ifdef SSE_UNROLL_ALIGNED
        for (i=0; i<unroll; i+=4) {
            __m128 ai_v = _mm_load_ps(&a[i]);
            __m128 two_v = _mm_set1_ps(2);
            __m128 ai2_v = _mm_mul_ps(ai_v,two_v);
            _mm_store_ps(&b[i],ai2_v);
        }
#endif
#ifndef NO_UNROLL
        for (; i<n; i++) {
            b[i] = a[i]*2;
        }
#endif
    }

    //for (auto i : a) { std::cout << i << "\t"; }
    //std::cout << "\n";
    //for (auto i : b) { std::cout << i << "\t"; }
    //std::cout << "\n";

    return 0;
}

我得到以下结果(x86):

  • NO_UNROLL: 0.994秒,编译器没有选择 SSE
  • UNROLL3.511秒,使用movups
  • SSE_UNROLL: 3.315秒,使用movups
  • SSE_UNROLL_ALIGNED: 3.276秒,使用movaps

因此很明显,在这种情况下展开循环并没有帮助。甚至确保我们使用更高效的movaps没有多大帮助。

但当编译为 64 位 (x64) 时,我得到了一个更奇怪的结果:

  • NO_UNROLL: 1.138秒,编译器没有选择 SSE
  • UNROLL1.409秒,编译器没有选择 SSE
  • SSE_UNROLL1.420秒,编译器仍然没有选择 SSE!
  • SSE_UNROLL_ALIGNED: 1.476秒,编译器仍然没有选择 SSE!

看来 MSVC 已经看穿了该提案并生成了更好的装配,尽管仍然比我们根本没有尝试任何手动优化的情况慢。

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

SSE 内在函数和循环展开 的相关文章

随机推荐

  • 使用 jquery 将项目添加到列表框

    如何使用 jquery 将项目添加到列表框 例如在下面的列表框中
  • 列出 CalendarApp.Color 中的枚举键

    尝试使用以下代码在 Google 脚本中获取 Google CalendarApp Color 枚举中的键列表 var colors CalendarApp Color for var x in colors Logger log Colo
  • 代码合同。如何抑制生成代码的警告?

    如何关闭对 Linq2Sql 代码的静态检查 您可以通过将有问题的类标记为来抑制对静态代码的检查 ContractVerification false 如果您生成的类是partial您可以创建另一个文件 其中包含另一个部分 ial 并将其添
  • 报表查看器中的重叠表格

    我有一个报告查看器控件 我向其中提供 SSRS 报告 生成的报告相当大 无法适应单一视图 因此 当我们在 Firefox 11 或 chrome 15 上的 ReportViewer 中看到该报告时 呈现的表格是重叠的 但它在 IE 中运行
  • 我可以将网页的图像捆绑到一个文件中吗?

    我有一个包含大约 70 张图像的网页 我正在寻找一种将这些图像捆绑到资源文件中的方法 这并不是为了提高客户端性能 因为缓存等会解决这个问题 更多的是通过我们的 cms 在服务器端进行资产管理 我希望能够将单个资源部署到 cms 中 而不必创
  • Pandas 和 Numpy 中有关多线程的奇怪错误

    Numpy 的大部分功能都会默认启用多线程 例如 我在 8 核 intel cpu 工作站上工作 如果我运行脚本 import numpy as np x np random random 1000000 for i in range 10
  • 使用 DataTrigger 动态设置事件处理程序

    我有几个项目控件 我需要为其附加一个事件处理程序 PreviewMouseLeftButtonDown 仅在满足特定条件时发生 我用数据触发器为我的控件设计了一种样式 我检查了它的绑定并用常规财产设置者对于 BorderThickness
  • 哪种 Objective-C 类型适合处理金钱?

    哪种 Objective C 类型适合处理金钱 我需要一些与核心数据兼容的东西 有两种解决方案 Use an int 并始终跟踪以美分为单位的货币价值 或您所使用的任何货币的最小可能划分 仅使用整数计算 Use NSDecimalNumbe
  • C# 中的重载解析、扩展方法和泛型

    我的 C 源代码中有以下场景 class A class Dispatch
  • Python:用 re.sub 替换列表中的多个特定单词

    我有以下字符串并列出 changewords 我想将 word from list n 替换为 word from list 我不想替换 n 的所有实例 string Foo n value of something n Bar n Ano
  • TaskFactory 新 UI 创建

    如何使用 TaskFactory 创建新的 UI 元素 当我尝试时出现以下错误 调用线程必须是 STA 因为许多 UI 组件都需要它 示例代码 Dim txtBoxList as new List Of TextBox Sub StartT
  • WiFiCameraApp 中的蓝牙视频流

    今天 我看到很多人有兴趣寻找使用移动设备通过蓝牙进行视频流的解决方案 但我还没有找到任何方便的意见来描述应该做什么来实现此类功能 一方面 我有相当抽象的信息 为了成功地进行视频流传输 两个设备都应该符合视频分发配置文件 VDP 这是蓝牙配置
  • 从多个子进程进行非阻塞读取 (Python)

    我目前有以下代码 受到答案的启发python 中 subprocess PIPE 的非阻塞读取 https stackoverflow com questions 375427 non blocking read on a subproce
  • 将内容控件的内容导出到另一个word文档中

    我有一个包含许多内容控件的Word文档 如何将内容控件的内容导出到另一个word文档中 我正在使用 C 并打开 xml sdk 问候 这里有两篇很棒的文章将向您展示如何做到这一点 使用 LINQ to XML 进行检索 Word 2007
  • 如何捕获 MonoTouch 应用程序中的 iOS 崩溃

    我们已经记录了 iPad MonoTouch 应用程序中发生的任何 Net Framework 异常 并且可以将这些异常返回给我们的支持部门以帮助分析和解决问题 但是 如果应用程序由于 iOS 错误 即 sigsev 而崩溃 我们不确定如何
  • 如何使用axios获取本地文件?

    我正在尝试获取本地文件 结果 js componentWillMount axios get config db json then function response alert ok catch function error alert
  • 如何使用c#在stackexchange redis客户端上执行flush命令

    我们如何使用 stackexchange redis 客户端执行 FlushAll 和 FlushDB redis 命令 The 项目主页 https github com StackExchange StackExchange Redis
  • 更新 ListView 行中与数据库行对应的复选框

    我已经设置了android focusable false 在我的自定义布局中的复选框上 我的后端 SQLite 数据库取决于 CheckBox 是否被选中 我的每一行ListView对应于我的数据库中的一行 所以我的问题是 我应该在哪里放
  • 如何模拟返回最终类的静态方法?

    我想模拟下一行 Bigquery bigquery Transport newBigQueryClient options build 这里的问题是 newBigQueryClient 方法返回 Bulder 类 这是最终的 这意味着我不能
  • SSE 内在函数和循环展开

    我正在尝试优化一些循环并且已经成功 但我想知道我是否只部分正确地完成了它 举例来说 我有这个循环 for i 0 i