用相同的值填充多维数组 C#

2023-12-31

使用 C# 有更快的方法吗?

double[,] myArray = new double[length1, length2];

for(int i=0;i<length1;i++)
    for(int j=0;j<length2;j++)
        myArray[i,j] = double.PositiveInfinity;

我记得用过C++,有一个叫做memset()为了做这些事...


多维数组只是一大块内存,因此我们可以将其视为一个,类似于memset()作品。这需要不安全的代码。我不会说这值得做,除非really性能至关重要。不过,这是一个有趣的练习,因此以下是使用 BenchmarkDotNet 的一些基准测试:

    public class ArrayFillBenchmark
    {
        const int length1 = 1000;
        const int length2 = 1000;
        readonly double[,] _myArray = new double[length1, length2];

        [Benchmark]
        public void MultidimensionalArrayLoop()
        {
            for (int i = 0; i < length1; i++)
            for (int j = 0; j < length2; j++)
                _myArray[i, j] = double.PositiveInfinity;
        }

        [Benchmark]
        public unsafe void MultidimensionalArrayNaiveUnsafeLoop()
        {
            fixed (double* a = &_myArray[0, 0])
            {
                double* b = a;

                for (int i = 0; i < length1; i++)
                for (int j = 0; j < length2; j++)
                    *b++ = double.PositiveInfinity;
            }
        }

        [Benchmark]
        public unsafe void MultidimensionalSpanFill()
        {
            fixed (double* a = &_myArray[0, 0])
            {
                double* b = a;
                var span = new Span<double>(b, length1 * length2);
                span.Fill(double.PositiveInfinity);
            }
        }

        [Benchmark]
        public unsafe void MultidimensionalSseFill()
        {
            var vectorPositiveInfinity = Vector128.Create(double.PositiveInfinity);

            fixed (double* a = &_myArray[0, 0])
            {
                double* b = a;

                ulong i = 0;
                int size = Vector128<double>.Count;

                ulong length = length1 * length2;
                for (; i < (length & ~(ulong)15); i += 16)
                {
                    Sse2.Store(b+size*0, vectorPositiveInfinity);
                    Sse2.Store(b+size*1, vectorPositiveInfinity);
                    Sse2.Store(b+size*2, vectorPositiveInfinity);
                    Sse2.Store(b+size*3, vectorPositiveInfinity);
                    Sse2.Store(b+size*4, vectorPositiveInfinity);
                    Sse2.Store(b+size*5, vectorPositiveInfinity);
                    Sse2.Store(b+size*6, vectorPositiveInfinity);
                    Sse2.Store(b+size*7, vectorPositiveInfinity);
                    b += size*8;
                }
                for (; i < (length & ~(ulong)7); i += 8)
                {
                    Sse2.Store(b+size*0, vectorPositiveInfinity);
                    Sse2.Store(b+size*1, vectorPositiveInfinity);
                    Sse2.Store(b+size*2, vectorPositiveInfinity);
                    Sse2.Store(b+size*3, vectorPositiveInfinity);
                    b += size*4;
                }
                for (; i < (length & ~(ulong)3); i += 4)
                {
                    Sse2.Store(b+size*0, vectorPositiveInfinity);
                    Sse2.Store(b+size*1, vectorPositiveInfinity);
                    b += size*2;
                }
                for (; i < length; i++)
                {
                    *b++ = double.PositiveInfinity;
                }
            }
        }
    }

Results:

|                               Method |       Mean |     Error |    StdDev | Ratio |
|------------------------------------- |-----------:|----------:|----------:|------:|
|            MultidimensionalArrayLoop | 1,083.1 us | 11.797 us | 11.035 us |  1.00 |
| MultidimensionalArrayNaiveUnsafeLoop |   436.2 us |  8.567 us |  8.414 us |  0.40 |
|             MultidimensionalSpanFill |   321.2 us |  6.404 us | 10.875 us |  0.30 |
|              MultidimensionalSseFill |   231.9 us |  4.616 us | 11.323 us |  0.22 |

MultidimensionalArrayLoop由于边界检查,速度很慢。 JIT 在每个循环中发出代码,以确保[i, j]位于数组的边界内。 JIT 有时可以省略边界检查,我知道它适用于一维数组。我不确定它是否适用于多维。

MultidimensionalArrayNaiveUnsafeLoop本质上是相同的代码MultidimensionalArrayLoop但没有边界检查。它的速度要快得多,只需要 40% 的时间。不过,它被认为是“天真的”,因为仍然可以通过展开循环来改进循环。

MultidimensionalSpanFill也没有边界检查,并且或多或少与MultidimensionalArrayNaiveUnsafeLoop, 然而,Span.Fill在内部进行循环展开,这就是为什么它比我们的天真的不安全循环要快一些。只需要我们原来的30%的时间。

MultidimensionalSseFill通过做两件事来改进我们的第一个不安全循环:循环展开和矢量化。这需要支持 Sse2 的 CPU,但它允许我们在一条指令中写入 128 位(16 字节)。这给我们带来了额外的速度提升,将其降低至原始速度的 22%。有趣的是,使用 Avx(256 位)的相同循环始终比 Sse2 版本慢,因此此处不包含该基准测试。

但这些数字仅适用于 1000x1000 的数组。当您更改数组的大小时,结果会有所不同。例如,当我们将数组大小更改为 10000x10000 时,所有不安全基准测试的结果都非常接近。可能是因为较大的数组有更多的内存获取,因此它往往会均衡在过去三个基准测试中看到的较小的迭代改进。

那里有一个教训,但我主要只是想分享这些结果,因为这是一个非常有趣的实验。

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

用相同的值填充多维数组 C# 的相关文章

  • 是否有与 posix_memalign 对应的 C++ 版本?

    当我打电话时posix memalign http man7 org linux man pages man3 posix memalign 3 html为类型的对象分配对齐的内存Foo在我的 C 代码中 我需要做一个reinterpret
  • 为什么在连接两个字符串时 Python 比 C 更快?

    目前我想比较 Python 和 C 用来处理字符串的速度 我认为 C 应该比 Python 提供更好的性能 然而 我得到了完全相反的结果 这是 C 程序 include
  • 如何在C(Linux)中的while循环中准确地睡眠?

    在 C 代码 Linux 操作系统 中 我需要在 while 循环内准确地休眠 比如说 10000 微秒 1000 次 我尝试过usleep nanosleep select pselect和其他一些方法 但没有成功 一旦大约 50 次 它
  • JNI 将 Char* 2D 数组传递给 JAVA 代码

    我想从 C 代码通过 JNI 层传递以下指针数组 char result MAXTEST MAXRESPONSE 12 12 8 3 29 70 5 2 42 42 在java代码中我写了以下声明 public static native
  • 查看 NuGet 包依赖关系层次结构

    有没有一种方法 文本或图形 来查看 NuGet 包之间的依赖关系层次结构 如果您使用的是新的 csproj 您可以在此处获取所有依赖项 在项目构建后 项目目录 obj project assets json
  • Visual Studio 在构建后显示假错误

    我使用的是 Visual Studio 2017 构建后 sln在调试模式下 我收到错误 但是 当我通过双击错误列表选项卡中的错误来访问错误时 错误会从页面中消失 并且错误数量也会减少 我不太确定这种行为以及为什么会发生这种情况 有超过 2
  • 告诉 Nancy 将枚举序列化为字符串

    Nancy 默认情况下在生成 JSON 响应时将枚举序列化为整数 我需要将枚举序列化为字符串 有一种方法可以通过创建来自定义 Nancy 的 JSON 序列化JavaScript 原始转换器 https github com NancyFx
  • 将 Long 转换为 DateTime 从 C# 日期到 Java 日期

    我一直尝试用Java读取二进制文件 而二进制文件是用C 编写的 其中一些数据包含日期时间数据 当 DateTime 数据写入文件 以二进制形式 时 它使用DateTime ToBinary on C 为了读取 DateTime 数据 它将首
  • 为什么可以通过ref参数修改readonly字段?

    考虑 class Foo private readonly string value public Foo Bar ref value private void Bar ref string value value hello world
  • C# 存档中的文件列表

    我正在创建一个 FileFinder 类 您可以在其中进行如下搜索 var fileFinder new FileFinder new string C MyFolder1 C MyFolder2 new string
  • 启动时的 Excel 加载项

    我正在使用 Visual C 创建 Microsoft Excel 的加载项 当我第一次创建解决方案时 它包含一个名为 ThisAddIn Startup 的函数 我在这个函数中添加了以下代码 private void ThisAddIn
  • 识别 Visual Studio 中的重载运算符 (c++)

    有没有办法使用 Visual Studio 快速直观地识别 C 中的重载运算符 在我看来 C 中的一大问题是不知道您正在使用的运算符是否已重载 Visual Studio 或某些第三方工具中是否有某些功能可以自动突出显示重载运算符或对重载运
  • 等待 IAsyncResult 函数直至完成

    我需要创建等待 IAsyncResult 方法完成的机制 我怎样才能做到这一点 IAsyncResult result contactGroupServices BeginDeleteContact contactToRemove Uri
  • 检测到严重错误 c0000374 - C++ dll 将已分配内存的指针返回到 C#

    我有一个 c dll 它为我的主 c 应用程序提供一些功能 在这里 我尝试读取一个文件 将其加载到内存 然后返回一些信息 例如加载数据的指针和内存块的计数到 c Dll 成功将文件读取到内存 但在返回主应用程序时 程序由于堆损坏而崩溃 检测
  • String.Empty 与 "" [重复]

    这个问题在这里已经有答案了 可能的重复 String Empty 和 有什么区别 https stackoverflow com questions 151472 what is the difference between string
  • OpenGL:仅获取模板缓冲区而没有深度缓冲区?

    我想获取一个模板缓冲区 但如果可能的话 不要承受附加深度缓冲区的开销 因为我不会使用它 我发现的大多数资源表明 虽然模板缓冲区是可选的 例如 排除它以利于获得更高的深度缓冲区精度 但我还没有看到任何请求并成功获取仅 8 位模板缓冲区的代码
  • 实体框架中的“it”是什么

    如果以前有人问过这个问题 请原谅我 但我的任何搜索中都没有出现 它 我有两个数据库表 Person 和 Employee 对每个类型的表进行建模 例如 Employee is a Person 在我的 edmx 设计器中 我定义了一个实体
  • 如何减少具有多个单元的 PdfPTable 的内存消耗

    我正在使用 ITextSharp 创建一个 PDF 它由单个 PdfTable 组成 不幸的是 对于特定的数据集 由于创建了大量 PdfPCell 我遇到了内存不足异常 我已经分析了内存使用情况 我有近百万个单元格的 1 2 在这种情况下有
  • 如何使用 C++11 using 语法键入定义函数指针?

    我想写这个 typedef void FunctionPtr using using 我该怎么做呢 它具有类似的语法 只不过您从指针中删除了标识符 using FunctionPtr void 这是一个Example http ideone
  • 如何将十六进制字符串转换为无符号长整型?

    我有以下十六进制值 CString str str T FFF000 如何将其转换为unsigned long 您可以使用strtol作用于常规 C 字符串的函数 它使用指定的基数将字符串转换为 long long l strtol str

随机推荐