如何使这个 C# 循环更快?

2024-01-08

执行摘要:如果您想继续使用 C#,Reed 下面的答案是最快的。如果您愿意编组到 C++(我就是),那么这是一个更快的解决方案。

我在 C# 中有两个 55mb ushort 数组。我使用以下循环将它们组合起来:

float b = (float)number / 100.0f;
for (int i = 0; i < length; i++)
{
      image.DataArray[i] = 
          (ushort)(mUIHandler.image1.DataArray[i] + 
          (ushort)(b * (float)mUIHandler.image2.DataArray[i]));
}

这段代码,根据前后添加 DateTime.Now 调用,运行时间为 3.5 秒。我怎样才能让它更快?

EDIT:我认为这是一些代码,显示了问题的根源。当以下代码在全新的 WPF 应用程序中运行时,我得到以下计时结果:

Time elapsed: 00:00:00.4749156 //arrays added directly
Time elapsed: 00:00:00.5907879 //arrays contained in another class
Time elapsed: 00:00:02.8856150 //arrays accessed via accessor methods

因此,当直接遍历数组时,时间比数组位于另一个对象或容器内部时要快得多。此代码表明,不知何故,我正在使用访问器方法,而不是直接访问数组。即便如此,我似乎能达到的最快速度是半秒。当我使用 icc 运行 C++ 中的第二个代码清单时,我得到:

Run time for pointer walk: 0.0743338

在这种情况下,C++ 的速度快了 7 倍(使用 icc,不确定使用 msvc 是否可以获得相同的性能——我不太熟悉那里的优化)。有没有办法让 C# 接近 C++ 的性能水平,或者我应该让 C# 调用我的 C++ 例程?

清单 1,C# 代码:

public class ArrayHolder
{
    int length;
    public ushort[] output;
    public ushort[] input1;
    public ushort[] input2;
    public ArrayHolder(int inLength)
    {
        length = inLength;
        output = new ushort[length];
        input1 = new ushort[length];
        input2 = new ushort[length];
    }

    public ushort[] getOutput() { return output; }
    public ushort[] getInput1() { return input1; }
    public ushort[] getInput2() { return input2; }
}


/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();


        Random random = new Random();

        int length = 55 * 1024 * 1024;
        ushort[] output = new ushort[length];
        ushort[] input1 = new ushort[length];
        ushort[] input2 = new ushort[length];

        ArrayHolder theArrayHolder = new ArrayHolder(length);

        for (int i = 0; i < length; i++)
        {
            output[i] = (ushort)random.Next(0, 16384);
            input1[i] = (ushort)random.Next(0, 16384);
            input2[i] = (ushort)random.Next(0, 16384);
            theArrayHolder.getOutput()[i] = output[i];
            theArrayHolder.getInput1()[i] = input1[i];
            theArrayHolder.getInput2()[i] = input2[i];
        }

        Stopwatch stopwatch = new Stopwatch(); 
        stopwatch.Start();
        int number = 44;
        float b = (float)number / 100.0f;
        for (int i = 0; i < length; i++)
        {
            output[i] =
                (ushort)(input1[i] +
                (ushort)(b * (float)input2[i]));
        } 
        stopwatch.Stop();

        Console.WriteLine("Time elapsed: {0}",
            stopwatch.Elapsed);
        stopwatch.Reset();

        stopwatch.Start();
        for (int i = 0; i < length; i++)
        {
            theArrayHolder.output[i] =
                (ushort)(theArrayHolder.input1[i] +
                (ushort)(b * (float)theArrayHolder.input2[i]));
        }
        stopwatch.Stop();

        Console.WriteLine("Time elapsed: {0}",
            stopwatch.Elapsed);
        stopwatch.Reset();

        stopwatch.Start();
        for (int i = 0; i < length; i++)
        {
            theArrayHolder.getOutput()[i] =
                (ushort)(theArrayHolder.getInput1()[i] +
                (ushort)(b * (float)theArrayHolder.getInput2()[i]));
        }
        stopwatch.Stop();

        Console.WriteLine("Time elapsed: {0}",
            stopwatch.Elapsed);
    }
}

清单 2,C++ 等效项: //looptiming.cpp :定义控制台应用程序的入口点。 //

#include "stdafx.h"
#include <stdlib.h>
#include <windows.h>
#include <stdio.h>
#include <iostream>


int _tmain(int argc, _TCHAR* argv[])
{

    int length = 55*1024*1024;
    unsigned short* output = new unsigned short[length];
    unsigned short* input1 = new unsigned short[length];
    unsigned short* input2 = new unsigned short[length];
    unsigned short* outPtr = output;
    unsigned short* in1Ptr = input1;
    unsigned short* in2Ptr = input2;
    int i;
    const int max = 16384;
    for (i = 0; i < length; ++i, ++outPtr, ++in1Ptr, ++in2Ptr){
        *outPtr = rand()%max;
        *in1Ptr = rand()%max;
        *in2Ptr = rand()%max;
    }

    LARGE_INTEGER ticksPerSecond;
    LARGE_INTEGER tick1, tick2;   // A point in time
    LARGE_INTEGER time;   // For converting tick into real time


    QueryPerformanceCounter(&tick1);

    outPtr = output;
    in1Ptr = input1;
    in2Ptr = input2;
    int number = 44;
    float b = (float)number/100.0f;


    for (i = 0; i < length; ++i, ++outPtr, ++in1Ptr, ++in2Ptr){
        *outPtr = *in1Ptr + (unsigned short)((float)*in2Ptr * b);
    }
    QueryPerformanceCounter(&tick2);
    QueryPerformanceFrequency(&ticksPerSecond);

    time.QuadPart = tick2.QuadPart - tick1.QuadPart;

    std::cout << "Run time for pointer walk: " << (double)time.QuadPart/(double)ticksPerSecond.QuadPart << std::endl;

    return 0;
}

EDIT 2:在第二个示例中启用 /QxHost 会将时间降至 0.0662714 秒。按照@Reed的建议修改第一个循环让我开始

已用时间:00:00:00.3835017

所以,对于滑块来说仍然不够快。这个时间是通过代码:

        stopwatch.Start();
        Parallel.ForEach(Partitioner.Create(0, length),
         (range) =>
         {
             for (int i = range.Item1; i < range.Item2; i++)
             {
                 output[i] =
                     (ushort)(input1[i] +
                     (ushort)(b * (float)input2[i]));
             }
         });

        stopwatch.Stop();

EDIT 3根据 @Eric Lippert 的建议,我在发布版本中重新运行 C# 代码,并且不使用附加的调试器,而是将结果打印到对话框中。他们是:

  • 简单数组:~0.273s
  • 包含的数组:~0.330s
  • 访问器数组:~0.345s
  • 并行阵列:~0.190s

(这些数字来自 5 次运行的平均值)

因此,并行解决方案肯定比我之前得到的 3.5 秒快,但仍然比使用非 icc 处理器可实现的 0.074 秒要快一些。因此,最快的解决方案似乎是在发行版中进行编译,然后编组为 icc 编译的 C++ 可执行文件,这使得此处可以使用滑块。

EDIT 4:@Eric Lippert 的另外三个建议:将 for 循环内部从 length 更改为 array.length,使用双精度数,并尝试不安全的代码。

对于这三个人来说,现在的时机是:

  • 长度:~0.274s
  • 双精度数,非浮点数:~0.290s
  • 不安全:~0.376s

到目前为止,并行解决方案是最大的赢家。虽然如果我可以通过着色器添加这些,也许我可以看到某种加速......

这是附加代码:

        stopwatch.Reset();

        stopwatch.Start();

        double b2 = ((double)number) / 100.0;
        for (int i = 0; i < output.Length; ++i)
        {
            output[i] =
                (ushort)(input1[i] +
                (ushort)(b2 * (double)input2[i]));
        }

        stopwatch.Stop();
        DoubleArrayLabel.Content += "\t" + stopwatch.Elapsed.Seconds + "." + stopwatch.Elapsed.Milliseconds;
        stopwatch.Reset();

        stopwatch.Start();

        for (int i = 0; i < output.Length; ++i)
        {
            output[i] =
                (ushort)(input1[i] +
                (ushort)(b * input2[i]));
        }

        stopwatch.Stop();
        LengthArrayLabel.Content += "\t" + stopwatch.Elapsed.Seconds + "." + stopwatch.Elapsed.Milliseconds;
        Console.WriteLine("Time elapsed: {0}",
            stopwatch.Elapsed);
        stopwatch.Reset();

        stopwatch.Start();
        unsafe
        {
            fixed (ushort* outPtr = output, in1Ptr = input1, in2Ptr = input2){
                ushort* outP = outPtr;
                ushort* in1P = in1Ptr;
                ushort* in2P = in2Ptr;
                for (int i = 0; i < output.Length; ++i, ++outP, ++in1P, ++in2P)
                {
                    *outP = (ushort)(*in1P + b * (float)*in2P);
                }
            }
        }

        stopwatch.Stop();
        UnsafeArrayLabel.Content += "\t" + stopwatch.Elapsed.Seconds + "." + stopwatch.Elapsed.Milliseconds;
        Console.WriteLine("Time elapsed: {0}",
            stopwatch.Elapsed);

这应该是完全可并行的。然而,考虑到每个元素完成的工作量很小,您需要格外小心地处理这个问题。

执行此操作的正确方法(在 .NET 4 中)是使用Parallel.ForEach与分区器结合使用:

float b = (float)number / 100.0f;
Parallel.ForEach(Partitioner.Create(0, length), 
(range) =>
{
   for (int i = range.Item1; i < range.Item2; i++)
   {
      image.DataArray[i] = 
          (ushort)(mUIHandler.image1.DataArray[i] + 
          (ushort)(b * (float)mUIHandler.image2.DataArray[i]));
   }
});

这将有效地将工作划分到系统中的可用处理核心上,并且如果您有多个核心,应该会提供相当大的加速。

话虽这么说,这最多只能根据系统中的核心数量来加速此操作。如果您需要加快速度,您可能需要恢复到并行化和不安全代码的混合。到那时,可能值得考虑尝试实时呈现这一点的替代方案。

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

如何使这个 C# 循环更快? 的相关文章

  • 从另一个 FORM 中取回隐藏的 FORM

    我有两种形式Form1 and Form2 我正在打开Form2 from Form1 on button Click Form2 obj2 new Form2 this Visible false obj2 Show 然后我想回来Form
  • 如何使用 C# 以编程方式编辑 Power BI Desktop 文档参数或数据源?

    我有一个在 Power BI Desktop 中内置的报告模板 并保存为 pbix 或 pbit 文件 该模板使用DirectQuery SQL数据库作为数据源 而服务器地址和数据库名称被提取到参数中 还有一个参数包含一个ReportId
  • 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
  • 为什么在 C++ 中声明枚举时使用 typedef?

    我已经很多年没有写过任何 C 了 现在我正试图重新开始 然后我遇到了这个并考虑放弃 typedef enum TokenType blah1 0x00000000 blah2 0X01000000 blah3 0X02000000 Toke
  • C# Outlook 从收件人获取 CompanyName 属性

    我目前正在使用 C 编写 Outlook 2010 AddIn 我想要的是从我从 AppointmentItem 中提取的 Recipient 对象中获取 CompanyName 属性 因此 有了 AppointmentItem 的收件人
  • 如何调整 Windows 窗体以适应任何屏幕分辨率?

    我知道这是重复的问题 但我检查了所有其他相关问题 他们的答案没有帮助 结果仍然与屏幕截图 2 中所示相同 我是 C Windows 窗体新手 如截图1所示 我有Form1有一些控件 每组控件都放在一个面板中 我在 PC1 中设计了应用程序
  • C++中的类要具备什么条件才能成为容器?

    我是 C 编程新手 偶然发现了这个术语containers举例如下vector deque map etc 一个企业的最低要求应该是什么class应该满足被称为container in C 我将从 范围 这个概念开始 Range 只有两个方
  • 具有多个谓词的 C++11 算法

    功能如std find if来自algorithmheader 确实很有用 但对我来说 一个严重的限制是我只能为每次调用使用 1 个谓词count if 例如给定一个像这样的容器std vector我想同时应用相同的迭代find if 多个
  • 虚拟并行端口模拟器

    在我的计算机网络课程中 我们应该通过使用本机寄存器 例如使用 outportb 等命令 来学习并行端口编程 我没有并行端口 因为我住在 2011 年 但想练习这些程序 我使用 dosbox 安装了旧的 Turboc 3 IDE 有没有一个程
  • 名称查找、实例化点 (POI) 和基本类型

    以下代码针对 X 进行编译 但不适用于 double struct X void foo double void foo X namespace NN struct A void foo A foo double error foo not
  • 编写具有多种类型的泛型扩展方法时的类型推断问题

    我正在为 IEnumerable 编写一个通用扩展方法 用于将对象列表映射到另一个映射对象列表 这就是我希望该方法的工作方式 IList
  • 如何在新窗口中打开图像或pdf文件?

    我有一个 gridview 它包含文件名和文件路径 图像和 pdf 格式文件 其中我使用了模板字段 在该字段下放置了 1 个图像按钮 单击该图像按钮 即 查看 按钮 时 我想在新窗口中打开所选文件 这是我的代码 protected void
  • 将 2 个字节转换为整数

    我收到一个 2 个字节的端口号 最低有效字节在前 我想将其转换为整数 以便我可以使用它 我做了这个 char buf 2 Where the received bytes are char port 2 port 0 buf 1 port
  • C 与 C++ 中的 JNI 调用不同?

    所以我有以下使用 Java 本机接口的 C 代码 但是我想将其转换为 C 但不知道如何转换 include
  • 如何对STL向量进行排序?

    我想排序一个vector vector
  • WinForms - 加载表单时如何使用 PaintEventArgs 运行函数?

    我试图理解图形 在 Graphics FromImage 文档中 它有这样的示例 private void FromImageImage PaintEventArgs e Create image Image imageFile Image
  • 当我使用可变参数而不是常量参数时,为什么我的内联表 UDF 慢得多?

    我有一个表值内联 UDF 我想过滤该 UDF 的结果以获得一个特定值 当我使用常量参数指定过滤器时 一切都很好 并且性能几乎是瞬时的 当我使用可变参数指定过滤器时 它会花费明显更大的时间块 大约是逻辑读取的 500 倍和持续时间的 20 倍
  • 在 mvc4 中创建通用 mvc 视图

    我以前也提过类似的问题 没有得到答案 如何创建一个通用的 mvc4 视图 该视图可以显示传递给它的模型列表或单个模型 模型可以是个人 组织或团体 无论传递给它的是什么 如果您正在寻找类似的东西 model MyViewModel
  • 使用 Unity 在 C# 中发送 http 请求

    如何使用 Unity 在 C 中发送 HTTP GET 和 POST 请求 我想要的是 在post请求中发送json数据 我使用Unity序列化器 所以不需要 新的 我只想在发布数据中传递一个字符串并且能够 将 ContentType 设置
  • Emacs C++,打开相应的头文件

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

随机推荐

  • 弱化 GADT 类型约束以处理不可预测的数据

    我试图利用 GADT 来获得良好的约束类型 但某些依赖项在编译期间无法处理 例如用户输入 让我们考虑以下 AVL 树定义 data Zero data S a data AVL depth where Nil AVL Zero LNode
  • OS X 10.9 升级后 Android Studio 项目出现问题,cacerts 错误

    我在加载在 Mountain Lion 10 8 中启动的 Android Studio 项目时遇到问题 现在尝试在 Mavericks 10 9 中运行 但是当我打开项目时 Gradle 失败并出现以下错误 Gradle SimpleTi
  • 选择数据框中按组第一次出现的所有行(包括第一次出现)

    我一直在摸不着头脑不知道该怎么做 我正在重新组织一些不平衡的面板数据 堆叠 长格式 我需要按组 id 保留所有行 包括变量 indc D 值的第一次出现 并且还保留尚未发生这种情况的组的行 我唯一希望丢弃的行是每组中存在第二个或更多指示变量
  • Java库解析mysql异常消息

    java中是否有任何库可以解析mySQL异常并返回行 列或表信息 这样我就可以使用行或列名称来获取并显示更合适的消息 我想包含列或行信息的自定义异常消息 目前 这就是我提取信息的方式 通过使用处理一些异常情况mysql错误代码 https
  • Pandas 中滚动最大值的 Numpy 版本

    TL DR 我的问题是如何改进我的函数以超越 pandas 自己的移动最大函数 背景资料 因此 我正在使用大量移动平均线 移动最大值和移动最小值等 到目前为止 我发现的唯一类似移动窗口的功能是pandas rolling 方法 https
  • Asp.Net MVC 5 身份创建数据库管理界面

    我正在为一家小公司开发一个新的 asp net mvc 5 应用程序 并试图弄清楚如何使用新的 asp net 身份系统创建会员数据库 我认为可能有一个管理界面 我可以使用它来创建数据库并允许我添加一些用户 角色 是否有用于此任务的管理界面
  • 如何在MySQL中使用JPA自动生成区分大小写的列

    如何命令 JPA 在创建时默认将文本内容的 MySQL 数据库列设置为区分大小写 The Column http download oracle com docs cd E17410 01 javaee 6 api javax persis
  • Io 语言:异常:对象不响应“URL”

    今天我正在练习 七周七种语言 的 Io 示例 示例代码 futureResult URL with http google com fetch writeln Do something immediately while fetch goe
  • 无法使用 OpenOCD 找到脚本文件

    我正在尝试按照本教程将 OpenOCD 与我的 ST 发现板一起使用 https japaric github io discovery README html https japaric github io discovery READM
  • 使用 dll 的接口安全吗

    当我想导出 DLL 中的类时 从接口派生它并通过导出函数返回该接口是否是正确的方法 exported dll function which is used in the exe function MyClass Create IMyClas
  • 占用 Vaadin Gridlayout 中的可用空间,但考虑换行

    我使用 Vaadin 的 GridLayout 来可视化一些标题和值标签 GridLayout 有 2 列和几行 标题标签位于左侧 其关联的值标签位于右侧 我希望第一列消耗尽可能少的空间 第二列应该占据浏览器窗口的所有重新挖掘空间 如果值标
  • 使用 IF 条件和存储在变量中的比较运算符

    我有一套涉及比较运算符的规则 我想根据规则中存储的比较运算符的值执行一些任务 我正在按照以下方式进行操作 但它不起作用 检查以下代码 if benRules i amountCriteria Greater than comparison
  • XDocument.Save() 时出现内存异常

    我正在尝试将 XDcoument 保存到没有足够可用内存空间的拇指驱动器 这是应用程序的特殊测试条件 虽然应用程序给出了如下所示的异常 但我无法在 XDocument Save filePath 周围的 try catch 块中得到该异常
  • 如何在 AngularJS 中从我的应用程序配置中设置 $httpProvider 默认标头?

    我正在尝试设置我的 httpProvider defaults headers common X CSRF Token cookie auth token 在我的应用程序的 config 部分中 但我似乎还无法访问文档 cookie 有没有
  • 相当于Windows窗体中的canvas

    我正在创建一个简单的应用程序来显示多个图像 一个在另一个下面 在 WPF 中 我使用相当于图像数量的画布数量 并将这些画布添加到主画布中 并使用Image在每个画布上进行控制 我上传了图像 看起来不错 现在 我正在尝试在 Windows 窗
  • PHP连接Hotmail发送邮件?

    目前我正在尝试使用 PHPmailer 发送电子邮件 这是下面的代码
  • 如何在达到一定长度后分割字符串? [复制]

    这个问题在这里已经有答案了 我想在一定长度后分割字符串 假设我们有一串 消息 Who Framed Roger Rabbit 像这样分割 Who Framed Roger Rab bit 我想在 message 变量超过 10 时进行拆分
  • 如何将嵌套字典的所有值转换为字符串?

    我正在编写一个 python 应用程序 其中有一个可以嵌套到任何级别的变量字典 任何级别中的键可以是 int 或 string 但我想将所有级别的所有键和值转换为字符串 字典的嵌套方式是可变的 这使得它有点复杂 col1 0 0 1 8 2
  • “body {background-color}”适用于 HTML,但不适用于 CSS

    能够在内联中设置 HTML 正文的背景颜色属性
  • 如何使这个 C# 循环更快?

    执行摘要 如果您想继续使用 C Reed 下面的答案是最快的 如果您愿意编组到 C 我就是 那么这是一个更快的解决方案 我在 C 中有两个 55mb ushort 数组 我使用以下循环将它们组合起来 float b float number