Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/289.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/performance/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/jquery/77.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C#基本操作时间如何随数字大小而变化?_C#_Performance_Optimization_Multiplication_Micro Optimization - Fatal编程技术网

C#基本操作时间如何随数字大小而变化?

C#基本操作时间如何随数字大小而变化?,c#,performance,optimization,multiplication,micro-optimization,C#,Performance,Optimization,Multiplication,Micro Optimization,它的上下文是一个函数,每个帧几乎需要运行一次,因此在性能方面非常关键。此函数包含一个循环及其内部的操作 private int MyFunction(int number) { // Code for (int i = 0; i <= 10000; i++) { var value = i * number var valuePow2 = value * value; // Some code which uses

它的上下文是一个函数,每个帧几乎需要运行一次,因此在性能方面非常关键。此函数包含一个循环及其内部的操作

private int MyFunction(int number)
{
    // Code
    for (int i = 0; i <= 10000; i++)
    {
        var value = i * number
        var valuePow2 = value * value;

        // Some code which uses valuePow2 several times
    }
    return 0; // Not actual line
}
private int MyFunction(int number)
{
//代码

对于(int i=0;i对于典型的处理器,将两个32位整数相乘将花费相同的周期量,而不管这些整数中的数据如何

我确实注意到两个代码中都有一个问题。当你将两个int相乘时,它返回一个int类型。var类型将把该类型设置为返回值。这意味着valuePow2将是一个int。 由于循环增加到10000,若数字为5或更大,则会溢出valuePow2

如果不想使int溢出,可以将代码更改为

private int MyFunction(int number)
{
    // Code
    for (int i = 0; i <= 10000; i++)
    {
        long value = i * number;        //64bit multiplication          
        long valuePow2 = value * value; //64bit multiplication

        // Some code which uses valuePow2 several times
    }
    return 0; // Not actual line
}
private int MyFunction(int number)
{
//代码
对于(int i=0;i
例如,5*5的执行速度会比5000*5000快吗

对于编译时常量,
5*x
5000*x
便宜,因为前者可以通过
leaeax、[rdi+rdi*4]
实现

但对于运行时变量,唯一具有数据相关性能的整数指令是除法。这适用于任何主流CPU:流水线非常重要,即使某些情况下可以以较低的延迟运行,它们通常也不会,因为这会使调度更加困难。(不能让同一个执行单元在同一个周期内产生两个结果;相反,CPU只想知道,在一个周期内输入数据肯定会在3个周期后得到答案。)

(对于FP,同样只有division和sqrt在正常CPU上具有数据相关的性能。)

如果分支方向不同,则使用整数或具有任何数据相关分支的FP的代码可能会慢得多。(例如,对于二进制搜索,分支预测在一个跳转序列上“训练”;使用另一个键搜索会慢得多,因为它至少会预测失误一次。)

作为记录,使用
Math.Pow
而不是integer
*
的建议是愚蠢的。简单地将整数转换为
double
并返回比用整数乘法自身相乘要慢


Adam的答案链接了一个在一个大数组上循环的基准测试,可以实现自动矢量化。SSE/AVX2只有32位整数乘法。 64位占用更多的内存带宽。这也是为什么它显示16位和8位整数的加速。因此它发现在Haswell CPU上以半速度运行的
c=a*b
,但这不适用于循环情况

在标量代码中,
imul r64、r64
在英特尔主流CPU(至少是Nehalem)和Ryzen()上的性能与
imul r32、r32
相同。均为1 uop、3周期延迟、1/时钟吞吐量

这是唯一的AMD推土机系列,AMD Atom和Silvermont,其中64位标量乘法较慢。(当然,假设64位模式!在32位模式下,使用64位整数较慢。)


优化你的循环 对于固定值
number
,编译器可以并将其优化为
inum+=number
,而不是重新计算
i*number
。这称为,因为加法运算比乘法运算“弱”(稍微便宜)

for(...) {
    var value = i * number
    var valuePow2 = value * value;
}
可以编译成asm,其功能如下

var value = 0;
for(...) {
    var valuePow2 = value * value;

    ...

    value += number;
}
您可以尝试用这种方式手工编写,以防编译器不为您编写

但是整数乘法非常便宜,尤其是在现代CPU上完全流水线。它的延迟比add稍高,并且可以在更少的端口上运行(通常每个时钟吞吐量只有1个,而add只有4个),但您说您正在使用
valuePow2
进行重要的工作。这应该会让无序执行隐藏延迟


如果您检查asm,并且编译器正在使用一个单独的循环计数器递增1,那么您也可以尝试手持编译器来优化循环,以使用
value
作为循环计数器


var maxval = number * 10000;
for (var value = 0; i <= maxval; value += number) {
    var valuePow2 = value * value;

    ...
}

var maxval=数量*10000;

对于(var value=0;i您不能基于直觉、猜测、评论或文档进行优化。请使用探查器来衡量性能。为什么不使用
Math.Pow
?这是最快的速度。基于基本操作的数字类型中包含的数字的大小,性能应该不会有任何差异。FundamentallY,你的两个循环是相同的:它们每个都有两个乘法。考虑使用LIQPAD来计时小的代码段,但是实现精确的时间不是那么简单。而且,没有分析,你可能正在优化错误的代码。@ ZEL0,因为从我所看到的,数学。当Pow知道的时候,它比直接乘法慢得多。n和是2在你的例子中,乘法和
Math.Pow
是不等价的,因为你没有使用
double
数学。Pow
只对double实现。你使用的是
int
。要使用
Math.Pow
,你必须先转换成double,然后在完成后再转换回来。这是两个或者三个不需要直接乘法的转换操作。这是您提出的一个非常有趣的观点,有可能将64位乘法转换为32位。不幸的是,尽管它适用于我问题中的代码,但它已经被大大简化(和更改)对于这个问题,这在我的实际案例中是不可能的。不过,我会记住这些信息,因为我可能会在将来遇到它起作用的案例,或者能够修改一个以使其起作用。@KaitoKid:Adam链接的基准是在一个大数组上循环的,可以自动矢量化。(并且SSE/AVX2只有32位整数乘法。64位占用更多的内存带宽)。在标量码中,
imul r64,r64
具有相同的性能
var value = 0;
for(...) {
    var valuePow2 = value * value;

    ...

    value += number;
}

var maxval = number * 10000;
for (var value = 0; i <= maxval; value += number) {
    var valuePow2 = value * value;

    ...
}