Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/kotlin/3.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
Performance F#已检查计算的性能影响?_Performance_F#_Integer Overflow - Fatal编程技术网

Performance F#已检查计算的性能影响?

Performance F#已检查计算的性能影响?,performance,f#,integer-overflow,Performance,F#,Integer Overflow,使用选中的模块是否会影响性能?我用int类型的序列进行了测试,没有发现明显的差异。有时检查的版本更快,有时未检查的版本更快,但通常不会太快 Seq.initInfinite (fun x-> x) |> Seq.item 1000000000;; 基本上,我是想弄清楚总是开支票是否会有任何负面影响。(我遇到了一个不是很明显的溢出,所以我现在扮演一个被抛弃的恋人,他不想再让我伤心。)我能想到的不总是使用Checked的唯一非人为原因是如果有一些性能上的问题,但是我还没有看到过。当你衡

使用选中的模块是否会影响性能?我用int类型的序列进行了测试,没有发现明显的差异。有时检查的版本更快,有时未检查的版本更快,但通常不会太快

Seq.initInfinite (fun x-> x) |> Seq.item 1000000000;;

基本上,我是想弄清楚总是开支票是否会有任何负面影响。(我遇到了一个不是很明显的溢出,所以我现在扮演一个被抛弃的恋人,他不想再让我伤心。)我能想到的不总是使用Checked的唯一非人为原因是如果有一些性能上的问题,但是我还没有看到过。

当你衡量性能时,通常不应该把
Seq
包括在内,因为
Seq
会增加很多开销(至少与int操作相比),所以你可能会把大部分时间花在
Seq
上,而不是花在你喜欢测试的代码上

我为
(+)
编写了一个小测试程序:

因此,使用Checked似乎增加了一些开销。int add的成本应该小于循环开销,因此选中的
开销高于
2x
可能更接近
4x

出于好奇,我们可以使用诸如ILSpy之类的工具检查IL代码:

未选中:

    IL_0000: nop
    IL_0001: ldarg.2
    IL_0002: ldarg.0
    IL_0003: bge.s IL_0014

    IL_0005: ldarg.0
    IL_0006: ldarg.1
    IL_0007: ldc.i4.1
    IL_0008: add
    IL_0009: ldarg.2
    IL_000a: ldc.i4.1
    IL_000b: add
    IL_000c: starg.s i
    IL_000e: starg.s a
    IL_0010: starg.s c
    IL_0012: br.s IL_0000
; if i < c then
00007FF926A611B3  cmp         esi,ebx  
00007FF926A611B5  jge         00007FF926A611BD  
; i + 1
00007FF926A611B7  inc         esi  
; a + 1
00007FF926A611B9  inc         edi  
; loop (a + 1) (i + 1)
00007FF926A611BB  jmp         00007FF926A611B3
检查:

    IL_0000: nop
    IL_0001: ldarg.2
    IL_0002: ldarg.0
    IL_0003: bge.s IL_0014

    IL_0005: ldarg.0
    IL_0006: ldarg.1
    IL_0007: ldc.i4.1
    IL_0008: add.ovf
    IL_0009: ldarg.2
    IL_000a: ldc.i4.1
    IL_000b: add.ovf
    IL_000c: starg.s i
    IL_000e: starg.s a
    IL_0010: starg.s c
    IL_0012: br.s IL_0000
; if i < c then
00007FF926A62613  cmp         esi,ebx  
00007FF926A62615  jge         00007FF926A62623  
; a + 1
00007FF926A62617  add         edi,1  
; Overflow?
00007FF926A6261A  jo          00007FF926A6262D  
; i + 1
00007FF926A6261C  add         esi,1  
; Overflow?
00007FF926A6261F  jo          00007FF926A6262D  
; loop (a + 1) (i + 1)
00007FF926A62621  jmp         00007FF926A62613
唯一的区别是,未选中使用
add
,选中使用
add.ovf
add.ovf
是通过溢出检查添加的

通过查看jitted
x86_64
代码,我们可以更深入地挖掘

未选中:

    IL_0000: nop
    IL_0001: ldarg.2
    IL_0002: ldarg.0
    IL_0003: bge.s IL_0014

    IL_0005: ldarg.0
    IL_0006: ldarg.1
    IL_0007: ldc.i4.1
    IL_0008: add
    IL_0009: ldarg.2
    IL_000a: ldc.i4.1
    IL_000b: add
    IL_000c: starg.s i
    IL_000e: starg.s a
    IL_0010: starg.s c
    IL_0012: br.s IL_0000
; if i < c then
00007FF926A611B3  cmp         esi,ebx  
00007FF926A611B5  jge         00007FF926A611BD  
; i + 1
00007FF926A611B7  inc         esi  
; a + 1
00007FF926A611B9  inc         edi  
; loop (a + 1) (i + 1)
00007FF926A611BB  jmp         00007FF926A611B3
;如果我
检查:

    IL_0000: nop
    IL_0001: ldarg.2
    IL_0002: ldarg.0
    IL_0003: bge.s IL_0014

    IL_0005: ldarg.0
    IL_0006: ldarg.1
    IL_0007: ldc.i4.1
    IL_0008: add.ovf
    IL_0009: ldarg.2
    IL_000a: ldc.i4.1
    IL_000b: add.ovf
    IL_000c: starg.s i
    IL_000e: starg.s a
    IL_0010: starg.s c
    IL_0012: br.s IL_0000
; if i < c then
00007FF926A62613  cmp         esi,ebx  
00007FF926A62615  jge         00007FF926A62623  
; a + 1
00007FF926A62617  add         edi,1  
; Overflow?
00007FF926A6261A  jo          00007FF926A6262D  
; i + 1
00007FF926A6261C  add         esi,1  
; Overflow?
00007FF926A6261F  jo          00007FF926A6262D  
; loop (a + 1) (i + 1)
00007FF926A62621  jmp         00007FF926A62613
;如果我
现在,检查
开销的原因显而易见。在每次操作之后,抖动插入条件指令
jo
,如果设置了溢出标志,该指令将跳转到引发
溢出异常的代码

这表明整数加法的开销小于1个时钟周期。它小于1个时钟周期的原因是现代CPU可以并行执行某些指令

图表还显示,CPU正确预测的分支大约需要1-2个时钟周期

因此,假设吞吐量至少为2,在未检查的示例中,两个整数相加的代价应该是1个时钟周期

在选中的示例中,我们执行
add,jo,add,jo
。在这种情况下,CPU很可能无法并行化,其成本应该在4-6个时钟周期左右

另一个有趣的区别是加法的顺序改变了。对于选中的添加,操作的顺序很重要,但是对于未选中的抖动(和CPU),移动操作具有更大的灵活性,可能会提高性能

长话短说;对于像
(+)
这样的廉价操作,与
未选中的
相比,选中的
的开销应该在
4x-6x
左右


这假定没有溢出异常。.NET异常的成本可能是整数加法的10万倍。

请注意,您的示例中似乎没有使用任何算术运算符,因此这可能不是对其效率的良好测试…可能是这样,但是整数类型在FSharp.Core.Operators.Checked中被重载,所以问题的一部分是确定当溢出可能不是一个问题时是否会影响int类型的正常使用。这些不是您看到的类型,而是用于转换为数值类型的函数。这些类型完全相同,因此您当前的测试不会显示任何内容。如果你用checked做一些算术运算,将会有一些额外的IL指令,所以它看起来会稍微慢一点,但很少有足够的担心。谢谢你的澄清。
; if i < c then
00007FF926A62613  cmp         esi,ebx  
00007FF926A62615  jge         00007FF926A62623  
; a + 1
00007FF926A62617  add         edi,1  
; Overflow?
00007FF926A6261A  jo          00007FF926A6262D  
; i + 1
00007FF926A6261C  add         esi,1  
; Overflow?
00007FF926A6261F  jo          00007FF926A6262D  
; loop (a + 1) (i + 1)
00007FF926A62621  jmp         00007FF926A62613