偶然在 MSDN 上看到 Math.BigMul 方法:
我就想,为什么 .NET Base Class Library 要提供这么一个方法?她的功能不就是等价于 (long)a * b 吗?
那么,首先想到的就是用 .NET Reflector 这个 .NET 程序员必备的工具看看 Math.BigMul 的源程序代码:
2 {
3 return (a * b);
4 }
咦,上面的代码好像有点不对。第 3 行应该是
return (long)a * b;
才对呀。于是,再看 IL 代码:
2 {
3 .maxstack 8
4 L_0000: ldarg.0
5 L_0001: conv.i8
6 L_0002: ldarg.1
7 L_0003: conv.i8
8 L_0004: mul
9 L_0005: ret
10 }
这下对了,第 5 行和第 7 行的 conv.i8 指令将两个 int 参数转换为 long 类型,然后再调用 mul 指令(第 8 行)进行乘法运算。
不过,这样一来,Math.BigMul 方法不就完全没有存在的必要吗?调用她还不如我们自己写 (long)a * b 语句来代替她,还省了一次方法调用的开销。我原来以为 IL 语言中有什么指令可以直接将两个 int 类型的操作数相乘得到 long 类型的结果呢,而 Math.BigMul 方法就直接调用该指令。
于是,我就写了以下的程序来比较 Math.BigMul 方法和 (long)a * b 的效率:
2 using System.Diagnostics;
3
4 namespace Skyiv.Ben
5 {
6 sealed class TestBigMul
7 {
8 static void Main()
9 {
10 for (int i = 0; i < 5; i++) Console.WriteLine(BigMultiply(i) == LongMultiply(i));
11 }
12
13 static long BigMultiply(int n)
14 {
15 long k = 0;
16 var watch = Stopwatch.StartNew();
17 for (int i = int.MaxValue; i > 0; i--) k += Math.BigMul(i, n - i);
18 watch.Stop();
19 Console.WriteLine("Math.BigMul(): " + watch.Elapsed);
20 return k;
21 }
22
23 static long LongMultiply(int n)
24 {
25 long k = 0;
26 var watch = Stopwatch.StartNew();
27 for (int i = int.MaxValue; i > 0; i--) k += (long)i * (n - i);
28 watch.Stop();
29 Console.WriteLine("long multiply: " + watch.Elapsed);
30 return k;
31 }
32 }
33 }
这个程序关键部分的 IL 代码如下:
BigMultiply | LongMultiply |
---|---|
IL_0008: stloc.1 IL_0009: ldc.i4 0x7fffffff IL_000e: stloc.2 IL_000f: br.s IL_0021 IL_0011: ldloc.0 IL_0012: ldloc.2 IL_0013: ldarg.0 IL_0014: ldloc.2 IL_0015: sub IL_0016: call int64 [mscorlib] System.Math::BigMul (int32, int32) IL_001b: add IL_001c: stloc.0 IL_001d: ldloc.2 IL_001e: ldc.i4.1 IL_001f: sub IL_0020: stloc.2 IL_0021: ldloc.2 IL_0022: ldc.i4.0 IL_0023: bgt.s IL_0011 IL_0025: ldloc.1 |
IL_0008: stloc.1 IL_0009: ldc.i4 0x7fffffff IL_000e: stloc.2 IL_000f: br.s IL_001f IL_0011: ldloc.0 IL_0012: ldloc.2 IL_0013: conv.i8 IL_0014: ldarg.0 IL_0015: ldloc.2 IL_0016: sub IL_0017: conv.i8 IL_0018: mul IL_0019: add IL_001a: stloc.0 IL_001b: ldloc.2 IL_001c: ldc.i4.1 IL_001d: sub IL_001e: stloc.2 IL_001f: ldloc.2 IL_0020: ldc.i4.0 IL_0021: bgt.s IL_0011 IL_0023: ldloc.1 |
上表中的 IL 代码和我们预计的一样。在 BigMultiply 方法中就是直接调用 Math.BigMul 方法,而在 LongMultiply 方法中先用两个 conv.i8 指令将两个 int 类型的操作数转换为 long 类型,然后再使用 mul 指令进行乘法运算。而 Math.BigMul 方法内部也是这么做的。那么,这是不是说 BigMultiply 方法就一定比 LongMultiply 方法慢呢?还是让我们来看看这个程序的实际运行情况吧:
可以看出,这两种方法的运行时间是一样的。
这样看来,在 C# 语言中,方法调用的时间几乎可以忽略不计。因为在 BigMultiply 方法中比 LongMultiply 方法中多了二十多亿次(准确地说,是 2,147,483,647次) Math.BigMul 方法调用。但她们的运行时间差不多。
在 MSDN 论坛上有一篇关于 Math.BigMul 的有趣的帖子:
这个帖子中说到 Math.BigMul 方法是为了防止程序员在做乘法时忘记了把 int 类型转换为 long 类型。我想,作为一个熟练的程序员,应该不会犯这个错误。相反,不知道有 Math.BigMul 方法的程序员可能会更多一些。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· [AI/GPT/综述] AI Agent的设计模式综述