为什么 INumber<T>.CreateX(int n) 与浮点数和双精度数的隐式转换相比如此慢?

问题描述 投票:0回答:1

我正在开发一个数学库,我希望能够将其转换为使用

INumber<T>
中的新
System.Numerics
界面。这里的方法通常位于热路径上,因此如果它们能够尽可能快,同时使用起来也很愉快,那就太好了。我一直在对它们进行基准测试,并注意到一些看起来有点奇怪的事情。

在某些情况下,我们将数组值除以其索引,由于使用

System.Numerics
,这不能像定义类型时那样通过隐式转换来完成,推荐的方法似乎是使用
INumber<T>.Create{Checked|Saturating|Truncating}(int n)

我正在运行的基准测试方法相当简单,如下所示:

public static double[] DivideByImplicit(double[] values)
{
    double[] result = new double[values.Length];
    for (int i = 1; i < result.Length; i++)
    {
        result[i] = values[i] / i;
    }

    return result;
}

// Overloads for other types (int, long, float, decimal)
// ...


public static T[] DivideByChecked<T>(T[] values) where T : INumber<T>
{
    T[] result = new T[values.Length];
    for (int i = 1; i < values.Length; i++)
    {
        result[i] = values[i] / T.CreateChecked(i);
    }

    return result;
}

// Same again but for Saturating/Truncating
// ...

对于

int
long
decimal
,结果最多在几个百分点之内,但是当涉及
float
double
时,性能受到更大影响,双倍约为 25%,但浮动时间最多增加 100%:

方法 类别 意思是 错误 标准偏差 比率 比率SD
除以隐式小数 十进制 1000 29,292.733 纳秒 146.0540 纳秒 136.6190 纳秒 1.00 0.00
除以已检查的小数 十进制 1000 28,884.550 纳秒 82.0562 纳秒 76.7554 纳秒 0.99 0.00
除以饱和小数 十进制 1000 29,948.023 纳秒 61.6380 纳秒 54.6404 纳秒 1.02 0.01
除以截断小数 十进制 1000 30,367.067 纳秒 67.0287 纳秒 62.6987 纳秒 1.04 0.01
除以隐式双精度数 1000 1,067.421 纳秒 20.5178 纳秒 24.4250 纳秒 1.00 0.00
除以已检查双数 1000 1,342.562 纳秒 17.7660 纳秒 14.8354 纳秒 1.25 0.03
除以饱和双倍 1000 1,343.400 纳秒 16.4938 纳秒 13.7731 纳秒 1.25 0.04
除以截断双精度 1000 1,394.057 纳秒 27.2936 纳秒 44.0740 纳秒 1.31 0.04
除以隐式浮点数 漂浮 1000 636.576 纳秒 4.9312 纳秒 4.6127 纳秒 1.00 0.00
除以CheckedFloat 漂浮 1000 1,282.109 纳秒 13.9466 纳秒 12.3633 纳秒 2.01 0.02
除以饱和浮点数 漂浮 1000 1,288.927 纳秒 10.4365 纳秒 8.7149 纳秒 2.03 0.02
通过截断浮点除法 漂浮 1000 1,291.335 纳秒 20.6446 纳秒 17.2392 纳秒 2.03 0.04
除以隐式整数 int 1000 1,210.849 纳秒 13.0819 纳秒 12.2368 纳秒 1.00 0.00
除以CheckedInt int 1000 1,202.773 纳秒 7.3879 纳秒 6.5492 纳秒 0.99 0.01
除以饱和整数 int 1000 1,201.430 纳秒 8.0413纳秒 6.7149 纳秒 0.99 0.01
除以截断整数 int 1000 1,199.158 纳秒 9.0592 纳秒 7.0728 纳秒 0.99 0.01
除以隐式长 1000 1,712.002 纳秒 19.3905 纳秒 17.1891 纳秒 1.00 0.00
除以检查长 1000 1,716.886 纳秒 17.9844 纳秒 16.8226 纳秒 1.00 0.01
除以饱和长 1000 1,712.213 纳秒 31.2334 纳秒 29.2157 纳秒 1.00 0.02
通过截断长整除 1000 1,771.120 纳秒 34.2546 纳秒 40.7777 纳秒 1.03 0.03

为什么转换为

float
/
double
时的影响比转换为任何其他数字类型时的影响要高得多?为简洁起见,未显示,但我还对大小为 1、100 和 1_000_000 的数组运行了基准测试,并且在 1 或 100 上没有看到减速,但在 1_000_000 上类似。

c# performance .net-core casting
1个回答
0
投票
如果您想要的类型不是您要传入的类型,

CreateChecked
可能会很慢。您可以使循环使用另一个类型为
T
的循环变量,并将其用于除法。

public static T[] DivideByChecked<T>(T[] values) where T : INumber<T>
{
    T[] result = new T[values.Length];
    var tI = T.MultiplicativeIdentity;
    for (int i = 1; i < values.Length; i++)
    {
        result[i] = values[i] / tI;
        tI++;
    }

    return result;
}
© www.soinside.com 2019 - 2024. All rights reserved.