银河

SKYIV STUDIO

  博客园 :: 首页 :: 博问 :: 闪存 :: :: :: 订阅 订阅 :: 管理 ::
  268 随笔 :: 2 文章 :: 2606 评论 :: 48 引用

公告

我在2009年4月19日写的一篇随笔“Timus 1037. Memory management”中,使用了如下的一个结构(Structs)来表示“内存块”:

struct Block
{
  public int Id { get; private set; }
  public int Time { get; set; }
  public Block(int id, int time) : this() { Id = id; Time = time; }
}

在这个结构中,Id 表示“内存块”的编号,Time 表示该“内存块”到期时间,它们都是自动实现的属性(Auto-Implemented Properties)。

下面,就是我们这次的主角 Block.cs 源程序文件:

using System;

namespace Skyiv.Ben.Test
{
  struct Block
  {
    public int Id { get; private set; }
    public int Time { get; set; }
    public Block(int id) : this() { Id = id; }
  }
  
  sealed class Test
  {
    static void Main()
    {
      Console.WriteLine(new Block(37).Time);
    }
  }
}

我们将分别在 Windows 和 Linux 操作系统下编译这个 C# 源文件。

Windows 操作系统的版本如下所示:

编译器是:

E:\work> csc –out:block.windows.exe block.cs
Microsoft (R) Visual C# 2008 Compiler version 3.5.30729.1
for Microsoft (R) .NET Framework version 3.5
Copyright (C) Microsoft Corporation. All rights reserved.

E:\work>

Linux 操作系统和编译器如下所示:

ben@linux-cod2:~/work> cat /etc/issue
Welcome to openSUSE 11.1 - Kernel \r (\l).

ben@linux-cod2:~/work> uname -srvm
Linux 2.6.27.21-0.1-default #1 SMP 2009-03-31 14:50:44 +0200 x86_64
ben@linux-cod2:~/work> mono --version
Mono JIT compiler version 2.4 (tarball Fri Mar 13 15:52:25 UTC 2009)
Copyright (C) 2002-2008 Novell, Inc and Contributors. www.mono-project.com
	TLS:           __thread
	GC:            Included Boehm (with typed GC)
	SIGSEGV:       altstack
	Notifications: epoll
	Architecture:  amd64
	Disabled:      none
ben@linux-cod2:~/work> gmcs --version
Mono C# compiler version 2.4.0.0
ben@linux-cod2:~/work> gmcs -out:block.mono.exe block.cs
ben@linux-cod2:~/work> 

下面就是在这两个操作系统下分别编译后的结果:

E:\work>dir
 Volume in drive E is Data2
 Volume Serial Number is 16BB-989E

 Directory of E:\work

2009/05/08  21:21    <dir>          .
2009/05/08  21:21    <dir>          ..
2009/05/08  20:02               318 block.cs
2009/05/08  20:10             3,584 block.mono.exe
2009/05/08  21:21             4,096 block.windows.exe
               3 File(s)          7,998 bytes
               2 Dir(s)  61,416,194,048 bytes free

E:\work>

在 Windows 操作系统上编译后的程序可以在 Linux 操作系统下运行,反之亦然。

在 Windows 操作系统下运行:

E:\work> block.windows.exe
0
E:\work> block.mono.exe
0
E:\work>

在 Linux 操作系统下运行:

ben@linux-cod2:~/work> mono block.windows.exe
0
ben@linux-cod2:~/work> mono block.mono.exe
0
ben@linux-cod2:~/work> 

下面,我们用 ildasm 来反汇编这两个 .exe 文件。

从上图中可以看出,这两个 .exe 文件中的内容几乎是一样的,除了 Block 结构的 Id 和 Time 属性用 Microsoft C# 编译器比用 mono C# 编译器多了个 instance 修饰符。

下面就是 Block 结构的 Id 属性(总是先 Microsoft 后 mono,下同):


下面就是 Block 结构的 Id 属性的 get 方法:


从上图中可以看出,Microsoft C# 编译器生成的代码有很多不必要的 IL 代码,不好。注意,上述代码是直接用 csc.exe 编译的,在编译时没有加上 /debug+ 参数,而不是在 Visual Studio 2008 IDE 中编译的。

而 mono C# 编译器生成的代码就非常好,没有多余的 IL 代码。

下面就是 Block 结构的 Id 属性的 set 方法:


这下,Micorsoft 和 mono 生成的代码又完全一样,奇怪。

下面就是 Block 结构的构造函数:


从上图中可以看出,Microsoft 生成的代码除了有多余的 nop 以外,还多了以下一行:

IL_0001:  initobj    Skyiv.Ben.Test.Block

这一行代码,是用来调用 Block 结构的默认(无参的)构造函数,对应下面 C# 源程序代码:

public Block(int id) : this() { Id = id; }

中的“ : this() ” 。

如果删除这个“ : this() ” ,用 Microsoft C# 编译器编译时就会出错,如下所示:

E:\work2> csc block.cs
适用于 Microsoft(R) .NET Framework 3.5 版的 Microsoft(R) Visual C# 2008 编译器 3.5.30729.1 版
版权所有(C) Microsoft Corporation。保留所有权利。

block.cs(9,28): error CS0188: 在给“this”对象的所有字段赋值之前,无法使用该对象
block.cs(9,12): error CS0843:
        必须对自动实现的属性“Skyiv.Ben.Test.Block.Id”的支持字段完全赋值,才能
        将控制返回给调用方。请考虑从构造函数初始值设定项中调用默认构造函数。
block.cs(9,12): error CS0843:
        必须对自动实现的属性“Skyiv.Ben.Test.Block.Time”的支持字段完全赋值,才
        能将控制返回给调用方。请考虑从构造函数初始值设定项中调用默认构造函数。

E:\work2> 

但是,如果用 mono C# 编译器编译就可以顺利通过。实际上,即便加上这个“ : this() ” ,mono C# 编译器也完全无视它,也就是说,即使在有“ : this() ” 的情况下,mono C# 编译器也不会生成调用  Block 结构的默认构造函数的 IL 代码,它直接忽略了这个“ : this() ”。而且,这样做也没有造成什么不良后果,block.mono.exe 在 Windows 和 Linux 操作系统下都运行良好。

最后,block.cs、block.windows.exe 和 block.mono.exe 这三个文件可以在这里下载。

实际上,之所以会写这篇文章,是因为我在做“Timus 1037. Memory management”这道 ACM 题的时候,是在 Ubuntu 9.04 Linux 下使用 MonoDevelop 2.0 写程序的,如下所示:

从上图中可以看出,在 Block 结构的构造函数中没有“ : this() ” ,这在 Linux 下运行得很好。但是,提交到 ACM 网站后,由于该网站是使用 Microsoft Visual C# 2008 версии 3.5.30729.1 编译器,导致编译出错。

这就引起了我比较 Microsoft C# 编译器和 mono C# 编译器的兴趣,于是就产生了这篇文章。

 

总结一下,我认为目前的 mono C# 编译器生成的代码比较高效,而 Microsoft C# 编译器生成的代码有很多不必要的垃圾。

以上观点如有不妥之处,欢迎各位大侠指正。

posted on 2009-05-09 00:25 银河 阅读(...) 评论(...) 编辑 收藏