C# 为什么在这个例子中使用浮点数会使我比使用双倍点数慢2倍?

C# 为什么在这个例子中使用浮点数会使我比使用双倍点数慢2倍?,c#,.net,c++,optimization,floating-point,C#,.net,C++,Optimization,Floating Point,我最近一直在做一些分析,我遇到了一个让我发疯的案例。下面是一段不安全的C#代码,它基本上以不同的采样率将源样本缓冲区复制到目标缓冲区。现在,它每帧占用约0.17%的总处理时间。我不明白的是,如果我使用浮动而不是双精度,处理时间将提高到0.38%。有人能解释一下这是怎么回事吗 快速版本(~17%) double rateIncr=。。。 双读偏移量=。。。 双偏移量CR=。。。 浮点v=…//体积 //源缓冲区和目标缓冲区。 浮点*src=。。。 浮点*tgt=。。。 对于(var c=0;c

我最近一直在做一些分析,我遇到了一个让我发疯的案例。下面是一段不安全的C#代码,它基本上以不同的采样率将源样本缓冲区复制到目标缓冲区。现在,它每帧占用约0.17%的总处理时间。我不明白的是,如果我使用浮动而不是双精度,处理时间将提高到0.38%。有人能解释一下这是怎么回事吗

快速版本(~17%)

double rateIncr=。。。
双读偏移量=。。。
双偏移量CR=。。。
浮点v=…//体积
//源缓冲区和目标缓冲区。
浮点*src=。。。
浮点*tgt=。。。
对于(var c=0;c
慢版本(~38%)

float rateIncr=。。。
浮点读偏移量=。。。
浮动偏移量CR=。。。
浮点v=…//体积
//源缓冲区和目标缓冲区。
浮点*src=。。。
浮点*tgt=。。。
对于(var c=0;c
奇数版本(~22%)

float rateIncr=。。。
浮点读偏移量=。。。
浮动偏移量CR=。。。
浮点v=…//体积
//源缓冲区和目标缓冲区。
浮点*src=。。。
浮点*tgt=。。。
对于(var c=0;c

到目前为止,我所知道的是我什么都不知道,也许在某个地方发生了一系列双浮转换,占用了CPU时间。你能用IL反汇编程序查看输出,看看它实际在做什么吗?

你是在64位还是32位处理器上运行这个程序?我的经验是,在某些边缘情况下,如果对象的大小与寄存器的大小相匹配,CPU可以通过这样的低级别功能进行优化(即使您可能假设两个浮点数可以整齐地装入64位寄存器,您仍可能失去优化的好处)。如果在32位系统上运行,您可能会发现情况正好相反

<> P>快速搜索,我能做的最好的引用是在C++游戏开发论坛上的几篇文章(我在游戏DVI的一年里我自己注意到了,但是那是我唯一的时间来描述这个级别)。从C++方法中得到一些有趣的拆卸结果,它可能适用于非常低的水平。
另一个想法是:

从MSDN开始,我们讨论了在.NET中使用浮点的许多内部细节,主要是为了解决浮点比较的问题。其中有一个有趣的段落总结了处理浮点值的CLR规范:

这个规范显然考虑到了x87 FPU。规范基本上是这样说的 允许使用CLR实现 内部代表(在我们的 大小写,x87 80位表示) 只要没有明确的 存储到强制位置(类 或valuet类型字段),强制 缩小。而且,在任何时候,IL 流可能有conv.r4和conv.r8 指令,这将强制 正在缩小范围


因此,在对浮点数执行操作时,浮点数实际上可能不是浮点数,而是x87 FPU上的80位数字或编译器认为是优化或计算精度所需的任何其他数字。如果不查看IL,您将无法确定,但是当您使用双打时,可能会有许多昂贵的浮动无法命中。遗憾的是,你不能通过C++中的FP开关来定义C语言中浮点运算所需的精度,因为这会阻止编译器在操作之前把所有东西都放进一个较大的容器。

< P>双转换到浮点转换可能在以下方面慢下来:

(float)readOffset

也可以尝试使readOffset浮动。

您的计算可能会导致浮点值进入“非规范”状态,这在大多数x86处理器上是非常低效的。非规范值非常小,以至于它们位于最小可能浮点值的边缘。相比之下,这样的值可以很好地适应双重范围,因此在这种情况下,计算是有效的

我不能确定这是否适用于你,但它确实解释了你所看到的行为


理解正在发生的事情的一种方法是在代码的这一点上进入调试器,并查看正在执行的实际x86指令。在不知道你的C#翻译成机器代码的情况下,很多可能的原因只是猜测。即使是看《每日电讯报》也不会告诉你太多

如果你这样做了,你可能想成为明星
float rateIncr = ...
float readOffset = ...
float offsetIncr = ...

float v = ... // volume

// Source and target buffers.
float* src = ...
float* tgt = ...

for( var c = 0; c < chunkCount; ++c)
{
    for( var s = 0; s < chunkSampleSize; ++s )
    {
        var iReadOffset = (int)readOffset;

        // The cast to float is removed
        var k = readOffset - iReadOffset;

        *tgt++ += (src[ iReadOffset ] * (1f - k) + src[ iReadOffset + 1 ] * k) * v;
        readOffset += offsetIncr;
    }
    offsetIncr += rateIncr;
}
float rateIncr = ...
float readOffset = ...
float offsetIncr = ...

float v = ... // volume

// Source and target buffers.
float* src = ...
float* tgt = ...

for( var c = 0; c < chunkCount; ++c)
{
    for( var s = 0; s < chunkSampleSize; ++s )
    {
        var iReadOffset = (int)readOffset;
        var k = readOffset - iReadOffset;

        // By just placing this test it goes down from 38% to 22%,
        // and the condition is NEVER met.
        if( (k != 0) && Math.Abs( k ) < 1e-38 )
        {
           Console.WriteLine( "Denormalized float?" );
        }

        *tgt++ += (src[ iReadOffset ] * (1f - k) + src[ iReadOffset + 1 ] * k) * v;
        readOffset += offsetIncr;
    }
    offsetIncr += rateIncr;
}
(float)readOffset
// NOTE: runnable - copy in paste into your own project
class Program
    {
        static int endVal = 32768;
        static int runCount = 100;
        static void Main(string[] args)
        {
            Stopwatch doublesw = Stopwatch.StartNew();
            for (int i = 0; i < runCount; ++i)
                doubleTest();
            doublesw.Stop();
            Console.WriteLine("Double: " + doublesw.ElapsedMilliseconds);
            Stopwatch floatsw = Stopwatch.StartNew();
            for (int i = 0; i < runCount; ++i)
                floatTest();
            floatsw.Stop();
            Console.WriteLine("Float: " + floatsw.ElapsedMilliseconds);
            Console.ReadLine();
        }

        static void doubleTest()
        {
            double value = 0;
            double incr = 0.001D;

            while (value < endVal)
            {
                value += incr;
            }
        }

        static void floatTest()
        {
            float value = 0;
            float incr = 0.001f;

            while (value < endVal)
            {
                value += incr;
            }
        }
    }
}
Double: 12897
Float: 10059