Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/55.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和OCaml中的模运算_C#_C_Ocaml_Complexity Theory_Modulo - Fatal编程技术网

C#,C和OCaml中的模运算

C#,C和OCaml中的模运算,c#,c,ocaml,complexity-theory,modulo,C#,C,Ocaml,Complexity Theory,Modulo,我想确认模运算是一个昂贵的运算,所以我测试了这段代码,检查给定的数字是否为偶数: bool is_even(int n) { return (n & 1) == 0; } 那么这个, bool is_even_bis(int n) { return (n % 2) == 0; } 我一开始使用C#,事实上,使用逻辑&的代码比另一个更快,有时甚至快三倍。使用ILSpy,我发现编译到MSIL时没有进行优化,代码完全相同 然而,正如我的一位朋友在C中发现的,使用gcc-O3

我想确认模运算是一个昂贵的运算,所以我测试了这段代码,检查给定的数字是否为偶数:

bool is_even(int n) {
    return (n & 1) == 0;
}
那么这个,

bool is_even_bis(int n) {
    return (n % 2) == 0;
}
我一开始使用C#,事实上,使用逻辑
&
的代码比另一个更快,有时甚至快三倍。使用ILSpy,我发现编译到MSIL时没有进行优化,代码完全相同

然而,正如我的一位朋友在C中发现的,使用
gcc-O3
将代码编译为:

is_even:
    mov     eax, DWORD PTR [esp+4]  # tmp63, n
    and     eax, 1  # tmp63,
    xor     eax, 1  # tmp63,
    ret
以及:

所以基本上是一样的。即使使用
-O0
优化,操作也不会出现:

is_even:
    push    ebp     #
    mov     ebp, esp        #,
    mov     eax, DWORD PTR [ebp+8]  # tmp63, n
    and     eax, 1  # D.1837,
    test    eax, eax        # D.1837
    sete    al      #, D.1838
    movzx   eax, al # D.1836, D.1838
    pop     ebp     #
    ret
不用说,编译后的代码在
-O0
中的
是偶数
是偶数
之间也是相同的

更有趣的是,我的另一位朋友使用OCaml尝试了同样的方法:

let is_even x = ((x land 1) == 0)

let _ = 
  let i = ref 100000000 in
  while !i > 0 do
    ignore (is_even !i);
    decr i
  done
以及:

而且,在运行字节码时,模版本的速度似乎更快,但在本机代码中则更慢!也许有人能解释这个谜

然后我开始思考为什么它的行为与C#中的行为不同(这两个函数之间的性能存在明显差距),以及为什么JIT编译器没有应用与
gcc
相同的优化。我不知道是否有办法截取JIT编译器的输出,也许这有助于理解


附加问题:我猜模是基于除法的,因为除法是在O(n²)时间内完成的(n是位数),我们能说模具有二次时间复杂度吗?

首先,从可移植的意义上讲,这些运算没有速度的概念。您的断言对于您的系统可能是正确的,但对于所有系统都是无效的。出于这个原因,对微观优化的推测是毫无意义的。通过生成一个解决有意义问题的程序,对其进行分析以找到占用执行时间最多的代码部分,并在这些时间引入更快的算法,您可以找到更重要的优化。所谓更快的算法,我指的是更好的数据结构(或更少的操作),而不是不同的运算符。停止关注微观优化


你的
的C版本甚至没有很好的定义。它可能会产生负零或陷阱表示,尤其是负数。使用陷阱表示是未定义的行为

似乎您可能看到的差异可能是由您的系统上的错误引起的。考虑如果-1用补语<代码> 11111111…1111111×0 < /代码>来表示。您希望
-1%2
的结果是-1,而不是0,不是吗?(编辑:…但是如果将-1表示为
11111111…11111110
,您希望
-1&1
会产生什么结果?)对于使用一个补码作为有符号整数表示的实现,需要一些开销来处理此问题

也许您的C编译器已经注意到您使用的
%
表达式和您使用的
&
表达式在您的系统上是等效的,因此进行了优化,但无论出于何种原因,C或OCaml编译器都没有执行优化

附加问题:我猜模是基于除法的,因为 除法是在O(n²)时间内完成的(n为位数),我们可以吗 假设模具有二次时间复杂度


考虑这两个基本操作的时间复杂性是没有意义的,因为它们会因系统而异。我在第一段中谈到了这一点。

我们可以说模具有二次时间复杂度吗?不,你肯定是在安排发布时间,是吗?@MatthewWatson:我不是,我只是好奇:)@HenkHolterman你能详细说明一下吗?“
的C版本甚至没有很好的定义。它可能会产生负零或陷阱表示,”不,一的补码机上的负值是错误的,但仅此而已。@DanielFischer“如果实现不支持负零,则&、|、^、~、运算符与操作数的行为将产生这样一个值是未定义的。”是的,但
x&1
不会产生负零
x&1
只能产生(无符号/非负)零或1。@DanielFischer标准状态是否在
&
运算符对符号位进行操作的任何位置?“二进制和运算符的结果是按位和操作数的结果(也就是说,当且仅当转换后的操作数中的每个对应位都已设置时,才会设置结果中的每个位)。”不是说“值位”;我猜它如何处理填充位取决于实现,与算术运算相反,它不能明确保证逐位运算无法生成陷阱表示(或者我还没有找到保证)。但是如果除了算术运算之外的所有东西都被允许随意生成陷阱表示,那就太糟糕了。
let is_even x = ((x land 1) == 0)

let _ = 
  let i = ref 100000000 in
  while !i > 0 do
    ignore (is_even !i);
    decr i
  done
let is_even_bis x = ((x mod 2) == 0)

let _ = 
  let i = ref 100000000 in
  while !i > 0 do
    ignore (is_even_bis !i);
    decr i
  done