标签:arch linux
01: /// <summary> 02: /// 使用字节数组中的值初始 ZipInteger 结构的新实例 03: /// 注意:本构造函数会破坏传入的 bits 参数的值。 04: /// </summary> 05: /// <param name="bits">顺序为 big-endian 的字节值的数组</param> 06: public ZipInteger(byte[] bits) 07: { 08: if (bits == null) throw new ArgumentNullException("bits"); 09: if (bits.Length < 1 || bits.Length > 9) throw new ArgumentException("Invalid length", "bits"); 10: byte[] mask = { 0x7F, 0x3F, 0x1F, 0x0F, 0x07, 0x03, 0x01, 0x00 }; 11: if (bits.Length > 1 && bits.Length < 9) bits[0] &= mask[bits.Length - 1]; 12: Array.Reverse(bits); 13: Array.Resize(ref bits, 8); 14: if (!BitConverter.IsLittleEndian) Array.Reverse(bits); 15: data = Decode(BitConverter.ToInt64(bits, 0)); 16: }
注意上述源程序第 10 行的 mask 数组其实只需要初始化一次就行了,而不需要每次调用该构造函数时都进行初始化。也就是说,应该将 mask 变量声明为 static 的,如下所示:
static readonly byte[] mask = { 0x7F, 0x3F, 0x1F, 0x0F, 0x07, 0x03, 0x01, 0x00 };
愿望是美好的,现实是残酷的,加上 static 关键字后,再编译时 C# 编译器会报告以下错误:
也就是说,C# 语言不允许使用 static 修饰符来声明方法内部的变量。但是在 C/C++ 语言中是允许这么做的。
如果把该构造函数内部的 mask 变量提升到构造函数外部,成为 ZipInteger 结构的字段成员,就可以声明为 static 的。但是这样一样,读这段代码的人就不容易弄清楚 mask 字段只在这个构造函数内部使用,成为代码的“坏味道”,一点也不优雅了。
好了,让我们写一小段程序来测试一下加上 static 后对运行效率的影响:
01: using System; 02: using System.IO; 03: using System.Diagnostics; 04: using Skyiv.Numerics; 05: 06: namespace Skyiv.Tester 07: { 08: sealed class StaticTester 09: { 10: static void Main() 11: { 12: try 13: { 14: new StaticTester().Run(100000000); 15: } 16: catch (Exception ex) 17: { 18: Console.WriteLine(ex); 19: } 20: } 21: 22: void Run(int count) 23: { 24: Console.WriteLine(" OS Version: " + Environment.OSVersion); 25: Console.WriteLine(" CLR Version: " + Environment.Version); 26: using (var reader = new MemoryStream(GetBytes(count))) 27: { 28: var watch = Stopwatch.StartNew(); 29: var n = 0; 30: for (var i = 0; i < 10; i++) 31: { 32: reader.Seek(0, SeekOrigin.Begin); 33: while (ZipInteger.Read(reader).HasValue) n++; 34: } 35: watch.Stop(); 36: Console.WriteLine(" Count: " + n.ToString("N0")); 37: Console.WriteLine("Milliseconds: " + watch.ElapsedMilliseconds.ToString("N0")); 38: } 39: } 40: 41: byte[] GetBytes(int count) 42: { 43: var bits = new byte[count]; 44: var rand = new Random(123456); 45: rand.NextBytes(bits); 46: return bits; 47: } 48: } 49: }
上述程序中第 44 行使用固定的种子初始化 Random 类的新实例,从而产生相同的随机数序列,以便用相同的测试用例来进行测试,使测试结果具有可比性。注意,在上述程序中如果 count 值和随机数的种子选取得不好,执行到第 33 行的 ZipInteger.Read 方法时是有可能抛出 EndOfStreamException 异常的。
这个测试程序在 Windows Vista 操作系统的 .NET Framework 4 环境下的运行结果如下所示:
将这个测试程序对 mask 变量是非静态的和静态的两种情况分别运行五次,得到的 Milliseconds 值如下表所示:
序号 | 非静态 | 静态 |
---|---|---|
1 | 181,886 | 167,528 |
2 | 179,074 | 166,847 |
3 | 180,309 | 166,526 |
4 | 183,542 | 166,399 |
5 | 179,469 | 167,365 |
平均 | 180,856 | 166,933 |
经过简单的计算得知,静态情况下比非静态情况下的平均运行效率提高了 7.7%,还是很可观的。
这个测试程序在 Ubuntu 10.10 操作系统的 mono 2.6.7 环境的运行结果如下所示:
非静态 | 静态 |
---|---|
OS Version: Unix 2.6.35.24 CLR Version: 2.0.50727.1433 Count: 500,992,030 Milliseconds: 660,254 |
OS Version: Unix 2.6.35.24 CLR Version: 2.0.50727.1433 Count: 500,992,030 Milliseconds: 583,387 |
经过简单的计算得知,静态情况下比非静态情况下的运行效率提高了 11.6%,更是有了较大的改进。
我们来看看这两种情况下 C# 编译器生成的 IL 代码,在非静态的情况下的该实例构造函数(.ctor)的 IL 代码:
001: .method public hidebysig specialname rtspecialname 002: instance void .ctor(uint8[] bits) cil managed 003: { 004: // 代码大小 181 (0xb5) 005: .maxstack 5 006: .locals init (uint8[] V_0, 007: bool V_1) 008: IL_0000: nop 009: IL_0001: ldarg.1 010: IL_0002: ldnull 011: IL_0003: ceq 012: IL_0005: ldc.i4.0 013: IL_0006: ceq 014: IL_0008: stloc.1 015: IL_0009: ldloc.1 016: IL_000a: brtrue.s IL_0017 017: IL_000c: ldstr "bits" 018: IL_0011: newobj instance void [mscorlib]System.ArgumentNullException::.ctor(string) 019: IL_0016: throw 020: IL_0017: ldarg.1 021: IL_0018: ldlen 022: IL_0019: conv.i4 023: IL_001a: ldc.i4.1 024: IL_001b: blt.s IL_0029 025: IL_001d: ldarg.1 026: IL_001e: ldlen 027: IL_001f: conv.i4 028: IL_0020: ldc.i4.s 9 029: IL_0022: cgt 030: IL_0024: ldc.i4.0 031: IL_0025: ceq 032: IL_0027: br.s IL_002a 033: IL_0029: ldc.i4.0 034: IL_002a: stloc.1 035: IL_002b: ldloc.1 036: IL_002c: brtrue.s IL_003e 037: IL_002e: ldstr "Invalid length" 038: IL_0033: ldstr "bits" 039: IL_0038: newobj instance void [mscorlib]System.ArgumentException::.ctor(string, 040: string) 041: IL_003d: throw 042: IL_003e: ldc.i4.8 043: IL_003f: newarr [mscorlib]System.Byte 044: IL_0044: dup 045: IL_0045: ldtoken field int64 ‘<PrivateImplementationDetails> 046: {78063CDC-E5EE-4C6B-A62D-FD0F919F6111}‘::‘$$method0x6000006-1‘ 047: IL_004a: call void [mscorlib]System.Runtime.CompilerServices.RuntimeHelpers:: 048: InitializeArray(class [mscorlib]System.Array, 049: valuetype [mscorlib]System.RuntimeFieldHandle) 050: IL_004f: stloc.0 051: IL_0050: ldarg.1 052: IL_0051: ldlen 053: IL_0052: conv.i4 054: IL_0053: ldc.i4.1 055: IL_0054: ble.s IL_0062 056: IL_0056: ldarg.1 057: IL_0057: ldlen 058: IL_0058: conv.i4 059: IL_0059: ldc.i4.s 9 060: IL_005b: clt 061: IL_005d: ldc.i4.0 062: IL_005e: ceq 063: IL_0060: br.s IL_0063 064: IL_0062: ldc.i4.1 065: IL_0063: stloc.1 066: IL_0064: ldloc.1 067: IL_0065: brtrue.s IL_0082 068: IL_0067: ldarg.1 069: IL_0068: ldc.i4.0 070: IL_0069: ldelema [mscorlib]System.Byte 071: IL_006e: dup 072: IL_006f: ldobj [mscorlib]System.Byte 073: IL_0074: ldloc.0 074: IL_0075: ldarg.1 075: IL_0076: ldlen 076: IL_0077: conv.i4 077: IL_0078: ldc.i4.1 078: IL_0079: sub 079: IL_007a: ldelem.u1 080: IL_007b: and 081: IL_007c: conv.u1 082: IL_007d: stobj [mscorlib]System.Byte 083: IL_0082: ldarg.1 084: IL_0083: call void [mscorlib]System.Array::Reverse(class [mscorlib]System.Array) 085: IL_0088: nop 086: IL_0089: ldarga.s bits 087: IL_008b: ldc.i4.8 088: IL_008c: call void [mscorlib]System.Array::Resize<uint8>(!!0[]&, 089: int32) 090: IL_0091: nop 091: IL_0092: ldsfld bool [mscorlib]System.BitConverter::IsLittleEndian 092: IL_0097: stloc.1 093: IL_0098: ldloc.1 094: IL_0099: brtrue.s IL_00a2 095: IL_009b: ldarg.1 096: IL_009c: call void [mscorlib]System.Array::Reverse(class [mscorlib]System.Array) 097: IL_00a1: nop 098: IL_00a2: ldarg.0 099: IL_00a3: ldarg.1 100: IL_00a4: ldc.i4.0 101: IL_00a5: call int64 [mscorlib]System.BitConverter::ToInt64(uint8[], 102: int32) 103: IL_00aa: call int64 Skyiv.Numerics.ZipInteger::Decode(int64) 104: IL_00af: stfld int64 Skyiv.Numerics.ZipInteger::data 105: IL_00b4: ret 106: } // end of method ZipInteger::.ctor
上述 IL 代码的第 42 行到第 49 行对 mask 数组进行初始化,紧接着在第 50 行用“stloc.0”指令保存起来,然后在第 73 行用“ldloc.0”指令引用。
再看看静态的情况下该实例构造函数(.ctor)的 IL 代码:
01: .method public hidebysig specialname rtspecialname 02: instance void .ctor(uint8[] bits) cil managed 03: { 04: // 代码大小 167 (0xa7) 05: .maxstack 5 06: .locals init (bool V_0) 07: IL_0000: nop 08: IL_0001: ldarg.1 09: IL_0002: ldnull 10: IL_0003: ceq 11: IL_0005: ldc.i4.0 12: IL_0006: ceq 13: IL_0008: stloc.0 14: IL_0009: ldloc.0 15: IL_000a: brtrue.s IL_0017 16: IL_000c: ldstr "bits" 17: IL_0011: newobj instance void [mscorlib]System.ArgumentNullException::.ctor(string) 18: IL_0016: throw 19: IL_0017: ldarg.1 20: IL_0018: ldlen 21: IL_0019: conv.i4 22: IL_001a: ldc.i4.1 23: IL_001b: blt.s IL_0029 24: IL_001d: ldarg.1 25: IL_001e: ldlen 26: IL_001f: conv.i4 27: IL_0020: ldc.i4.s 9 28: IL_0022: cgt 29: IL_0024: ldc.i4.0 30: IL_0025: ceq 31: IL_0027: br.s IL_002a 32: IL_0029: ldc.i4.0 33: IL_002a: stloc.0 34: IL_002b: ldloc.0 35: IL_002c: brtrue.s IL_003e 36: IL_002e: ldstr "Invalid length" 37: IL_0033: ldstr "bits" 38: IL_0038: newobj instance void [mscorlib]System.ArgumentException::.ctor(string, 39: string) 40: IL_003d: throw 41: IL_003e: ldarg.1 42: IL_003f: ldlen 43: IL_0040: conv.i4 44: IL_0041: ldc.i4.1 45: IL_0042: ble.s IL_0050 46: IL_0044: ldarg.1 47: IL_0045: ldlen 48: IL_0046: conv.i4 49: IL_0047: ldc.i4.s 9 50: IL_0049: clt 51: IL_004b: ldc.i4.0 52: IL_004c: ceq 53: IL_004e: br.s IL_0051 54: IL_0050: ldc.i4.1 55: IL_0051: stloc.0 56: IL_0052: ldloc.0 57: IL_0053: brtrue.s IL_0074 58: IL_0055: ldarg.1 59: IL_0056: ldc.i4.0 60: IL_0057: ldelema [mscorlib]System.Byte 61: IL_005c: dup 62: IL_005d: ldobj [mscorlib]System.Byte 63: IL_0062: ldsfld uint8[] Skyiv.Numerics.ZipInteger::mask 64: IL_0067: ldarg.1 65: IL_0068: ldlen 66: IL_0069: conv.i4 67: IL_006a: ldc.i4.1 68: IL_006b: sub 69: IL_006c: ldelem.u1 70: IL_006d: and 71: IL_006e: conv.u1 72: IL_006f: stobj [mscorlib]System.Byte 73: IL_0074: ldarg.1 74: IL_0075: call void [mscorlib]System.Array::Reverse(class [mscorlib]System.Array) 75: IL_007a: nop 76: IL_007b: ldarga.s bits 77: IL_007d: ldc.i4.8 78: IL_007e: call void [mscorlib]System.Array::Resize<uint8>(!!0[]&, 79: int32) 80: IL_0083: nop 81: IL_0084: ldsfld bool [mscorlib]System.BitConverter::IsLittleEndian 82: IL_0089: stloc.0 83: IL_008a: ldloc.0 84: IL_008b: brtrue.s IL_0094 85: IL_008d: ldarg.1 86: IL_008e: call void [mscorlib]System.Array::Reverse(class [mscorlib]System.Array) 87: IL_0093: nop 88: IL_0094: ldarg.0 89: IL_0095: ldarg.1 90: IL_0096: ldc.i4.0 91: IL_0097: call int64 [mscorlib]System.BitConverter::ToInt64(uint8[], 92: int32) 93: IL_009c: call int64 Skyiv.Numerics.ZipInteger::Decode(int64) 94: IL_00a1: stfld int64 Skyiv.Numerics.ZipInteger::data 95: IL_00a6: ret 96: } // end of method ZipInteger::.ctor
上述 IL 代码中就没有对 mask 数组进行初始化,而是在第 63 行用“ldsfld”指令直接引用 ZipInteger 结构的静态字段,即已经初始化好了的 mask 数组。这个 mask 数组是在 ZipInteger 结构的静态构造函数(.cctor)中初始化的,如下所示:
01: .method private hidebysig specialname rtspecialname static 02: void .cctor() cil managed 03: { 04: // 代码大小 61 (0x3d) 05: .maxstack 8 06: IL_0000: ldc.i8 0x8000000000000000 07: IL_0009: call valuetype Skyiv.Numerics.ZipInteger Skyiv.Numerics.ZipInteger::op_Implicit(int64) 08: IL_000e: stsfld valuetype Skyiv.Numerics.ZipInteger Skyiv.Numerics.ZipInteger::MinValue 09: IL_0013: ldc.i8 0x7fffffffffffffff 10: IL_001c: call valuetype Skyiv.Numerics.ZipInteger Skyiv.Numerics.ZipInteger::op_Implicit(int64) 11: IL_0021: stsfld valuetype Skyiv.Numerics.ZipInteger Skyiv.Numerics.ZipInteger::MaxValue 12: IL_0026: ldc.i4.8 13: IL_0027: newarr [mscorlib]System.Byte 14: IL_002c: dup 15: IL_002d: ldtoken field int64 ‘<PrivateImplementationDetails> 16: {CDCDEB38-994E-4730-8D14-55B1DBDE4B1B}‘::‘$$method0x6000016-1‘ 17: IL_0032: call void [mscorlib]System.Runtime.CompilerServices.RuntimeHelpers:: 18: InitializeArray(class [mscorlib]System.Array, 19: valuetype [mscorlib]System.RuntimeFieldHandle) 20: IL_0037: stsfld uint8[] Skyiv.Numerics.ZipInteger::mask 21: IL_003c: ret 22: } // end of method ZipInteger::.cctor
可以看出,在上述 IL 代码中的第 12 到第 20 行对静态的 mask 数组进行了初始化。
相对应的非静态情况下的 ZipInteger 结构的静态构造函数(.cctor)就没有对 mask 数组进行初始化的语句:
01: .method private hidebysig specialname rtspecialname static 02: void .cctor() cil managed 03: { 04: // 代码大小 39 (0x27) 05: .maxstack 8 06: IL_0000: ldc.i8 0x8000000000000000 07: IL_0009: call valuetype Skyiv.Numerics.ZipInteger Skyiv.Numerics.ZipInteger::op_Implicit(int64) 08: IL_000e: stsfld valuetype Skyiv.Numerics.ZipInteger Skyiv.Numerics.ZipInteger::MinValue 09: IL_0013: ldc.i8 0x7fffffffffffffff 10: IL_001c: call valuetype Skyiv.Numerics.ZipInteger Skyiv.Numerics.ZipInteger::op_Implicit(int64) 11: IL_0021: stsfld valuetype Skyiv.Numerics.ZipInteger Skyiv.Numerics.ZipInteger::MaxValue 12: IL_0026: ret 13: } // end of method ZipInteger::.cctor
我们知道,静态构造函数总共只执行一次,而实例构造函数在每次实例化时都要执行一行。也就是说,在我们的测试用例中,mask 数组在静态的情况下只要初始化一次,而在非静态的情况下要初始化 500,990,730 次。所以运行效率出现这么大的差别就很好理解了。
除此之外,将方法内部的变量声明为 static 的,还可以起着方法内置状态的作用,这也是很有好处的。具体例子请参见“隐约有歌”大大的博文“技术面试题:f(f(n)) == -n”。
综上所述,我强烈要求在 C# 5.0 中增加支持将方法内部的变量声明为 static 的。这在 C/C++ 语言中早就已经支持了,我想这应该不难实现,因为 Microsoft 的 C/C++ 也支持这一特性。
版权声明:本文为博主http://www.zuiniusn.com 原创文章,未经博主允许不得转载。
标签:arch linux
原文地址:http://blog.csdn.net/u013141940/article/details/47121839