【问题标题】:How many cycles to multiply a float in C#在 C# 中乘以浮点数需要多少个周期
【发布时间】:2015-03-23 23:29:43
【问题描述】:

我有一个数字密集型应用程序,在互联网上寻找 GFLOPS 后,我决定做自己的小基准测试。我只是做了数千次单线程矩阵乘法以获得大约一秒钟的执行时间。这是内部循环。完整的

for (int i = 0; i < SIZEA; i++)
    for (int j = 0; j < SIZEB; j++)
        vector_out[i] = vector_out[i] + vector[j] * matrix[i, j];

我处理 FLOPS 已经有好几年了,所以我预计每个 FLOP 会得到大约 3 到 6 个周期。但是我得到了 30(100 MFLOPS),当然如果我并行化这个我会得到更多,但我只是没想到会这样。这可能是 dot NET 的问题吗?还是这真的是 CPU 性能?

这是带有完整基准代码的fiddle

编辑:即使在发布模式下,Visual Studio 也需要更长的时间来运行,可执行文件本身在每个 FLOP (250 MFLOPS) 中运行 12 个周期。仍然有任何 VM 影响吗?

【问题讨论】:

  • 鉴于 C# 编译为 IL,最终将转换为 x86、x64 或各种 ARM(仅举三例)架构,对此没有单一的答案。如果性能至关重要,那么 C# 不是(这部分)工作的正确工具。
  • @Damien_The_Unbeliever 的评论是正确的(应该是 IMO 的答案)。这将取决于目标架构。
  • @Damien 和 mura,这就是问题所在,架构有多少,点网有多少。在这样一个简单的代码中,我们可能正在以与熨斗相同的速度运行。
  • 您令人失望的结果更多来自vector_out[i]i &lt; SIZEA 保护。学习正确使用 C#,这是(转换后的)C 代码。

标签: c# benchmarking flops


【解决方案1】:

您的基准测试并不能真正衡量 FLOPS,它会在 C# 中进行一些浮点运算和循环。

但是,如果您可以将代码隔离为仅重复浮点操作,您仍然会遇到一些问题。

您的代码应包含一些“预循环”以允许“抖动预热”,因此您无需测量编译时间。

那么,即使你这样做了,

您需要在发布模式下编译并进行优化,并在已知一致的平台上从命令行执行您的测试。


Fiddle here

这是我的替代基准,

using System;
using System.Linq;
using System.Diagnostics;

class Program
{
    static void Main()
    {
        const int Flops = 10000000;
        var random = new Random();
        var output = Enumerable.Range(0, Flops)
                         .Select(i => random.NextDouble())
                         .ToArray();
        var left = Enumerable.Range(0, Flops)
                         .Select(i => random.NextDouble())
                         .ToArray();
        var right = Enumerable.Range(0, Flops)
                         .Select(i => random.NextDouble())
                         .ToArray();

        var timer = Stopwatch.StartNew();
        for (var i = 0; i < Flops - 1; i++)
        {
            unchecked
            {
                output[i] += left[i] * right[i];
            }
        }

        timer.Stop();
        for (var i = 0; i < Flops - 1; i++)
        {
            output[i] = random.NextDouble();
        }

        timer = Stopwatch.StartNew();
        for (var i = 0; i < Flops - 1; i++)
        {
            unchecked
            {
                output[i] += left[i] * right[i];
            }
        }

        timer.Stop();

        Console.WriteLine("ms: {0}", timer.ElapsedMilliseconds);
        Console.WriteLine(
            "MFLOPS: {0}",
            (double)Flops / timer.ElapsedMilliseconds / 1000.0);
    }
}

在我的虚拟机上,我得到类似的结果

ms: 73
MFLOPS: 136.986301...

注意,我必须大幅增加操作次数才能超过 1 毫秒。

【讨论】:

  • 非常感谢您的回答。我只是想澄清一下,我和你一样只对内部循环计时。
  • @ArturoHernandez 在您的链接示例中调用Stopwatch.StartNew(),它会立即构造并启动一个计时器,随后的_timer.Start() 不会重置计时器。你可以在这里查看备注msdn.microsoft.com/en-us/library/…
  • true 但仅在 100000 次迭代中出现问题。纠正后得到相同的答案。我仍然需要运行你的代码。谢!!!
  • 因为你使用毫秒,所以你得到了更像 136 mflops。就像我在 Visual Studio 中运行的一样。
  • @ArturoHernandez,你是对的,我已经进行了相应的修改。
猜你喜欢
  • 1970-01-01
  • 2016-11-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-16
  • 2023-03-12
  • 2021-06-24
  • 1970-01-01
相关资源
最近更新 更多