《Effective C#》读书笔记——条目11:理解短小方法的优势<C#语言习惯>

简介:

在应用程序开发过程中,开发者都力求写出更加高效的代码。但是当你想手工为C#编译器优化代码时,你的种种优化可能反倒会阻碍JIT进行更加高效的优化。因此,我们最好尽可能的写出最清晰的代码,将优化工作交给JIT编译器去完成。

  在.NET平台下开发程序的开发者都应该知道:.NET运行时将调用JIT编译器来将C#编译器生成的IL翻译成机器码。JIT不会在程序刚开始的时候就完全翻译所有的IL代码,CLR根据函数的粒度来逐一进行JIT编译。没有被调用的函数根本不会被JIT编译,因此将那些非常重要的逻辑分解成更多的小方法要比把所有逻辑放在一起形成大型复杂函数更有效率。例如下面的代码:

复制代码
 1     public string BuildMsg(bool takeFirstPath)
 2     {
 3         StringBuilder msg = new StringBuilder();
 4 
 5         if (takeFirstPath)
 6         {
 7             msg.Append("A problem occurred.");
 8             msg.Append("\nThis is a problem.");
 9             msg.Append("imagine much more text");
10         }
11         else
12         {
13             msg.Append("This Path is not so bad.");
14             msg.Append("\nIt is only a minor inconvenience.");
15             msg.Append("Add more detailed diagnostics here.");
16         }
17         return msg;
18     }
复制代码

 在第一次调用BuildMsg时,if-else两个分支都将被JIT编译。而实际上仅需要编译其中的一个分支就足够了,我们可以拆分这个方法,对其进行优化,下面是优化后的代码:

View Code

这时候两个方法可以根据需要再进行JIT编译,而不必在第一次调用BuildMsg方法是进行。我们可以看出:更小的函数让JIT编译器更方便的根据需要进行编译,而不是将时间浪费在不急于一时使用的代码上。对于switch语句中的每个case中的代码,这个规则的影响更明显。

 

寄存器的优化

  小而简单的方法会让JIT更容易的进行寄存器的选择工作,即选择哪个局部变量可以存放在寄存器中,而不是栈上。越少使用局部变量,也就让JIT编译器能够更方便的找到最适合放在寄存器的那一些。而越小的函数包含的局部变量也越少,也就更方便JIT对寄存器进行优化

 

内联的优化 

  内联表示把函数体替换到函数被调用的位置。由JIT编译器负责决定哪些方法应该被内联,当内联可以有效提高效率时,JIT编译器将自动执行。不过内联的标准并不是固定的,且当前的规则也不能保证将来不会发生变化,此外,是否内联完全由JIT自己决定。不过我们可以使用下面的特性选项通知JIT不要内联某个方法:

1         [MethodImpl(MethodImplOptions.NoInlining)]

   方法越简单就越适合内联。不过虚方法和包含ctry/catch代码块的方法将不会被内联。内联也改变了:代码在执行时才会被JIT编译 这一原则。所以在.NET平台下编程我们的责任应该就是尽量编写短小精悍的方法,而为你的算法生成高效的机器码是C#编译器和JIT编译器的责任。

 

小节:

将C#代码翻译为可执行的机器码有两个步骤:1.C#编译器将代码生成为IL,并放在程序集中。2.JIT再根据需要逐一为方法(或是一组方法,如果涉及内联)生成机器码。短小的方法让JIT编译器能够更好的平摊编译的代价。短小的代码也更适合内联。方法除了短小之外,简化控制流程也很重要,控制的分支越少JIT编译器也更容易选择找到最适合放在寄存器中的变量。因此,编写短小精悍的代码不但影响代码的可读性,也影响到程序运行的效率。

 

本文转自gyzhao博客园博客,原文链接:http://www.cnblogs.com/IPrograming/archive/2012/10/08/Effective_CSharp_11.html ,如需转载请自行联系原作者
相关文章
|
4月前
|
Java C#
C# 面向对象编程解析:优势、类和对象、类成员详解
OOP代表面向对象编程。 过程式编程涉及编写执行数据操作的过程或方法,而面向对象编程涉及创建包含数据和方法的对象。 面向对象编程相对于过程式编程具有几个优势: OOP执行速度更快,更容易执行 OOP为程序提供了清晰的结构 OOP有助于保持C#代码DRY("不要重复自己"),并使代码更易于维护、修改和调试 OOP使得能够创建完全可重用的应用程序,编写更少的代码并减少开发时间 提示:"不要重复自己"(DRY)原则是有关减少代码重复的原则。应该提取出应用程序中常见的代码,并将其放置在单一位置并重复使用,而不是重复编写。
52 0
|
2月前
|
开发框架 Java .NET
C#编程语言的优势与C++对比
C#编程语言的优势与C++对比
|
9月前
|
C#
《More Effective C# 》读书笔记 第一章
《More Effective C# 》读书笔记 第一章
|
Java 数据挖掘 数据库连接
简单讲一下 python,Java,C++,C#,Go,Ruby 语言的优势和前景
python,Java,C++,C#,Go,Ruby 语言的优势和前景
简单讲一下 python,Java,C++,C#,Go,Ruby 语言的优势和前景
|
C++ 安全 C#
读书笔记 effective c++ Item 9 绝不要在构造函数或者析构函数中调用虚函数
1.关于构造函数的一个违反直觉的行为 我会以重复标题开始:你不应该在构造或者析构的过程中调用虚函数,因为这些调用的结果会和你想的不一样。如果你同时是一个java或者c#程序员,那么请着重注意这个条款,因为这是c++同它们不一样的地方。
914 0
|
C++ 容器 数据库连接
读书笔记 effective c++ Item 8 不要让异常(exceptions)离开析构函数
1.为什么c++不喜欢析构函数抛出异常 C++并没有禁止析构函数出现异常,但是它肯定不鼓励这么做。这是有原因的,考虑下面的代码: 1 class Widget { 2 3 public: 4 5 .
804 0
|
C++ 编译器 安全
读书笔记 effective c++ Item 6 如果你不想使用编译器自动生成的函数,你需要明确拒绝
问题描述-阻止对象的拷贝   现实生活中的房产中介卖房子,一个服务于这个中介的软件系统很自然的会有一个表示要被销售的房屋的类: 1 class HomeForSale { ... };   每个房产中介会立刻指出来,要销售房屋的每个属性都是唯一的,没有两个完全一样的房屋。
812 0
|
C++ 容器 C语言
读书笔记 effective c++ Item 7 在多态基类中将析构函数声明为虚析构函数
1. 继承体系中关于对象释放遇到的问题描述 1.1 手动释放 关于时间记录有很多种方法,因此为不同的计时方法创建一个TimeKeeper基类和一些派生类就再合理不过了: 1 class TimeKeeper { 2 3 public: 4 5 TimeKeeper(); 6 7 ~TimeKeeper(); 8 9 .
1116 0
|
C++ 编译器
读书笔记 effective c++ Item 5 了解c++默认生成并调用的函数
1 编译器会默认生成哪些函数  什么时候空类不再是一个空类?答案是用c++处理的空类。如果你自己不声明,编译器会为你声明它们自己版本的拷贝构造函数,拷贝赋值运算符和析构函数,如果你一个构造函数都没有声明,编译器同样会为你声明一个默认拷贝构造函数。
943 0
|
安全 程序员 C++
读书笔记 effective c++ Item 3 在任何可能的时候使用 const
Const可以修饰什么?   Const 关键字是万能的,在类外部,你可以用它修饰全局的或者命名空间范围内的常量,也可以用它来修饰文件,函数和块作用域的静态常量。在类内部,你可以使用它来声明静态或者非静态的数据成员。
844 0