如何看待 NET reactnative 性能优化,真能达到 C++ 的性能,C#的生产效率吗

9401人阅读
C# 语言学习(2)
1. C#语言方面
1.1 垃圾回收
垃圾回收解放了手工管理对象的工作,提高了程序的健壮性,但副作用就是程序代码可能对于对象创建变得随意。
1.1.1 避免不必要的对象创建
由于垃圾回收的代价较高,所以C#程序开发要遵循的一个基本原则就是避免不必要的对象创建。以下列举一些常见的情形。
1.1.1.1 避免循环创建对象 ★
如果对象并不会随每次循环而改变状态,那么在循环中反复创建对象将带来性能损耗。高效的做法是将对象提到循环外面创建。
1.1.1.2 在需要逻辑分支中创建对象
如果对象只在某些逻辑分支中才被用到,那么应只在该逻辑分支中创建对象。
1.1.1.3 使用常量避免创建对象
程序中不应出现如 new Decimal(0) 之类的代码,这会导致小对象频繁创建及回收,正确的做法是使用Decimal.Zero常量。我们有设计自己的类时,也可以学习这个设计手法,应用到类似的场景中。
1.1.1.4 使用StringBuilder做字符串连接
1.1.2 不要使用空析构函数 ★
如果类包含析构函数,由创建对象时会在 Finalize 队列中添加对象的引用,以保证当对象无法可达时,仍然可以调用到 Finalize 方法。垃圾回收器在运行期间,会启动一个低优先级的线程处理该队列。相比之下,没有析构函数的对象就没有这些消耗。如果析构函数为空,这个消耗就毫无意 义,只会导致性能降低!因此,不要使用空的析构函数。
在实际情况中,许多曾在析构函数中包含处理代码,但后来因为种种原因被注释掉或者删除掉了,只留下一个空壳,此时应注意把析构函数本身注释掉或删除掉。
1.1.3 实现 IDisposable 接口
垃圾回收事实上只支持托管内在的回收,对于其他的非托管资源,例如 Window GDI 句柄或数据库连接,在析构函数中释放这些资源有很大问题。原因是垃圾回收依赖于内在紧张的情况,虽然数据库连接可能已濒临耗尽,但如果内存还很充足的话, 垃圾回收是不会运行的。
C#的 IDisposable 接口是一种显式释放资源的机制。通过提供 using 语句,还简化了使用方式(编译器自动生成 try ... finally 块,并在 finally 块中调用 Dispose 方法)。对于申请非托管资源对象,应为其实现 IDisposable 接口,以保证资源一旦超出 using 语句范围,即得到及时释放。这对于构造健壮且性能优良的程序非常有意义!
为防止对象的 Dispose 方法不被调用的情况发生,一般还要提供析构函数,两者调用一个处理资源释放的公共方法。同时,Dispose 方法应调用 System.GC.SuppressFinalize(this),告诉垃圾回收器无需再处理 Finalize 方法了。
1.2 String 操作
1.2.1 使用 StringBuilder 做字符串连接
String 是不变类,使用 + 操作连接字符串将会导致创建一个新的字符串。如果字符串连接次数不是固定的,例如在一个循环中,则应该使用 StringBuilder 类来做字符串连接工作。因为 StringBuilder 内部有一个 StringBuffer ,连接操作不会每次分配新的字符串空间。只有当连接后的字符串超出 Buffer 大小时,才会申请新的 Buffer 空间。典型代码如下:
StringBuilder sb = new StringBuilder( 256 );
for ( int i = 0 ; i & Results.C i ++ )
sb.Append (Results[i]);
如果连接次数是固定的并且只有几次,此时应该直接用 + 号连接,保持程序简洁易读。实际上,编译器已经做了优化,会依据加号次数调用不同参数个数的 String.Concat 方法。例如:
String str = str1 + str2 + str3 + str4;
会被编译为 String.Concat(str1, str2, str3, str4)。该方法内部会计算总的 String 长度,仅分配一次,并不会如通常想象的那样分配三次。作为一个经验值,当字符串连接操作达到 10 次以上时,则应该使用 StringBuilder。
这里有一个细节应注意:StringBuilder 内部 Buffer 的缺省值为 16 ,这个值实在太小。按 StringBuilder 的使用场景,Buffer 肯定得重新分配。经验值一般用 256 作为 Buffer 的初值。当然,如果能计算出最终生成字符串长度的话,则应该按这个值来设定 Buffer 的初值。使用 new StringBuilder(256) 就将 Buffer 的初始长度设为了256。
1.2.2 避免不必要的调用 ToUpper 或 ToLower 方法
String是不变类,调用ToUpper或ToLower方法都会导致创建一个新的字符串。如果被频繁调用,将导致频繁创建字符串对象。这违背了前面讲到的“避免频繁创建对象”这一基本原则。
例如,bool.Parse方法本身已经是忽略大小写的,调用时不要调用ToLower方法。
另一个非常普遍的场景是字符串比较。高效的做法是使用 Compare 方法,这个方法可以做大小写忽略的比较,并且不会创建新字符串。
还有一种情况是使用 HashTable 的时候,有时候无法保证传递 key 的大小写是否符合预期,往往会把 key 强制转换到大写或小写方法。实际上 HashTable 有不同的构造形式,完全支持采用忽略大小写的 key: new HashTable(StringComparer.OrdinalIgnoreCase)。
1.2.3 最快的空串比较方法
将String对象的Length属性与0比较是最快的方法:if (str.Length == 0)
其次是与String.Empty常量或空串比较:if (str == String.Empty)或if (str == &&)
注:C#在编译时会将程序集中声明的所有字符串常量放到保留池中(intern pool),相同常量不会重复分配。
1.3 多线程
1.3.1 线程同步
线 程同步是编写多线程程序需要首先考虑问题。C#为同步提供了 Monitor、Mutex、AutoResetEvent 和 ManualResetEvent 对象来分别包装 Win32 的临界区、互斥对象和事件对象这几种基础的同步机制。C#还提供了一个lock语句,方便使用,编译器会自动生成适当的 Monitor.Enter 和 Monitor.Exit 调用。
1.3.1.1 同步粒度
同步粒度可以是整个方法,也可以是方法中某一段代码。为方法指定 MethodImplOptions.Synchronized 属性将标记对整个方法同步。例如:
[MethodImpl(MethodImplOptions.Synchronized)]
public static SerialManager GetInstance()
if (instance == null )
instance = new SerialManager();
通常情况下,应减小同步的范围,使系统获得更好的性能。简单将整个方法标记为同步不是一个好主意,除非能确定方法中的每个代码都需要受同步保护。
1.3.1.2 同步策略
使用 lock 进行同步,同步对象可以选择 Type、this 或为同步目的专门构造的成员变量。
避免锁定Type★
锁定Type对象会影响同一进程中所有AppDomain该类型的所有实例,这不仅可能导致严重的性能问题,还可能导致一些无法预期的行为。这是一个很不 好的习惯。即便对于一个只包含static方法的类型,也应额外构造一个static的成员变量,让此成员变量作为锁定对象。
避免锁定 this
锁定 this 会影响该实例的所有方法。假设对象 obj 有 A 和 B 两个方法,其中 A 方法使用 lock(this) 对方法中的某段代码设置同步保护。现在,因为某种原因,B 方法也开始使用 lock(this) 来设置同步保护了,并且可能为了完全不同的目的。这样,A 方法就被干扰了,其行为可能无法预知。所以,作为一种良好的习惯,建议避免使用 lock(this) 这种方式。
使用为同步目的专门构造的成员变量
这是推荐的做法。方式就是 new 一个 object 对象, 该对象仅仅用于同步目的。
如果有多个方法都需要同步,并且有不同的目的,那么就可以为些分别建立几个同步成员变量。
1.3.1.4 集合同步
C#为各种集合类型提供了两种方便的同步机制:Synchronized 包装器和 SyncRoot 属性。
// Creates and initializes a new ArrayList
ArrayList myAL = new ArrayList();
& The & );
& quick & );
& brown & );
& fox & );
// Creates a synchronized wrapper around the ArrayList
ArrayList mySyncdAL = ArrayList.Synchronized(myAL);
调用 Synchronized 方法会返回一个可保证所有操作都是线程安全的相同集合对象。考虑 mySyncdAL[0] = mySyncdAL[0] + &test& 这一语句,读和写一共要用到两个锁。一般讲,效率不高。推荐使用 SyncRoot 属性,可以做比较精细的控制。
1.3.2 使用 ThreadStatic 替代 NameDataSlot ★
存 取 NameDataSlot 的 Thread.GetData 和 Thread.SetData 方法需要线程同步,涉及两个锁:一个是 LocalDataStore.SetData 方法需要在 AppDomain 一级加锁,另一个是 ThreadNative.GetDomainLocalStore 方法需要在 Process 一级加锁。如果一些底层的基础服务使用了 NameDataSlot,将导致系统出现严重的伸缩性问题。
规避这个问题的方法是使用 ThreadStatic 变量。示例如下:
public sealed class InvokeContext
[ThreadStatic]
private static InvokeC
private Hashtable maps = new Hashtable();
1.3.3 多线程编程技巧
1.3.3.1 使用 Double Check 技术创建对象
internal IDictionary KeyTable
if ( this ._keyTable == null )
lock ( base ._lock)
if ( this ._keyTable == null )
this ._keyTable = new Hashtable();
return this ._keyT
创建单例对象是很常见的一种编程情况。一般在 lock 语句后就会直接创建对象了,但这不够安全。因为在 lock 锁定对象之前,可能已经有多个线程进入到了第一个 if 语句中。如果不加第二个 if 语句,则单例对象会被重复创建,新的实例替代掉旧的实例。如果单例对象中已有数据不允许被破坏或者别的什么原因,则应考虑使用 Double Check 技术。
1.4 类型系统
1.4.1 避免无意义的变量初始化动作
CLR保证所有对象在访问前已初始化,其做法是将分配的内存清零。因此,不需要将变量重新初始化为0、false或null。
需要注意的是:方法中的局部变量不是从堆而是从栈上分配,所以C#不会做清零工作。如果使用了未赋值的局部变量,编译期间即会报警。不要因为有这个印象而对所有类的成员变量也做赋值动作,两者的机理完全不同!
1.4.2 ValueType 和 ReferenceType
1.4.2.1 以引用方式传递值类型参数
值类型从调用栈分配,引用类型从托管堆分配。当值类型用作方法参数时,默认会进行参数值复制,这抵消了值类型分配效率上的优势。作为一项基本技巧,以引用方式传递值类型参数可以提高性能。
1.4.2.2 为 ValueType 提供 Equals 方法
.net 默认实现的 ValueType.Equals 方法使用了反射技术,依靠反射来获得所有成员变量值做比较,这个效率极低。如果我们编写的值对象其 Equals 方法要被用到(例如将值对象放到 HashTable 中),那么就应该重载 Equals 方法。
public struct Rectangle
public double L
public double B
public override bool Equals ( object ob)
if (ob is Rectangle)
return Equels ((Rectangle)ob))
private bool Equals (Rectangle rect)
return this .Length == rect.Length && this .Breadth == rect.B
1.4.2.3 避免装箱和拆箱
C#可以在值类型和引用类型之间自动转换,方法是装箱和拆箱。装箱需要从堆上分配对象并拷贝值,有一定性能消耗。如果这一过程发生在循环中或是作为底层方法被频繁调用,则应该警惕累计的效应。
一种经常的情形出现在使用集合类型时。例如:
ArrayList al = new ArrayList();
for ( int i = 0 ; i & 1000 ; i ++ )
al.Add(i); // Implicitly boxed because Add() takes an object
int f = ( int )al[ 0 ]; // The element is unboxed
1.5 异常处理
异常也是现代语言的典型特征。与传统检查错误码的方式相比,异常是强制性的(不依赖于是否忘记了编写检查错误码的代码)、强类型的、并带有丰富的异常信息(例如调用栈)。
1.5.1 不要吃掉异常★
关于异常处理的最重要原则就是:不要吃掉异常。这个问题与性能无关,但对于编写健壮和易于排错的程序非常重要。这个原则换一种说法,就是不要捕获那些你不能处理的异常。
吃掉异常是极不好的习惯,因为你消除了解决问题的线索。一旦出现错误,定位问题将非常困难。除了这种完全吃掉异常的方式外,只将异常信息写入日志文件但并不做更多处理的做法也同样不妥。
1.5.2 不要吃掉异常信息★
有些代码虽然抛出了异常,但却把异常信息吃掉了。
为异常披露详尽的信息是程序员的职责所在。如果不能在保留原始异常信息含义的前提下附加更丰富和更人性化的内容,那么让原始的异常信息直接展示也要强得多。千万不要吃掉异常。
1.5.3 避免不必要的抛出异常
抛出异常和捕获异常属于消耗比较大的操作,在可能的情况下,应通过完善程序逻辑避免抛出不必要不必要的异常。与此相关的一个倾向是利用异常来控制处理逻辑。尽管对于极少数的情况,这可能获得更为优雅的解决方案,但通常而言应该避免。
1.5.4 避免不必要的重新抛出异常
如果是为了包装异常的目的(即加入更多信息后包装成新异常),那么是合理的。但是有不少代码,捕获异常没有做任何处理就再次抛出,这将无谓地增加一次捕获异常和抛出异常的消耗,对性能有伤害。
反射是一项很基础的技术,它将编译期间的静态绑定转换为延迟到运行期间的动态绑定。在很多场景下(特别是类框架的设计),可以获得灵活易于扩展的架构。但带来的问题是与静态绑定相比,动态绑定会对性能造成较大的伤害。
1.6.1 反射分类
type comparison :类型判断,主要包括 is 和 typeof 两个操作符及对象实例上的 GetType 调用。这是最轻型的消耗,可以无需考虑优化问题。注意 typeof 运算符比对象实例上的 GetType 方法要快,只要可能则优先使用 typeof 运算符。
member enumeration : 成员枚举,用于访问反射相关的元数据信息,例如Assembly.GetModule、Module.GetType、Type对象上的 IsInterface、IsPublic、GetMethod、GetMethods、GetProperty、GetProperties、 GetConstructor调用等。尽管元数据都会被CLR缓存,但部分方法的调用消耗仍非常大,不过这类方法调用频度不会很高,所以总体看性能损失程 度中等。
member invocation:成员调用,包括动态创建对象及动态调用对象方法,主要有Activator.CreateInstance、Type.InvokeMember等。
1.6.2 动态创建对象
C#主要支持 5 种动态创建对象的方式:
1. Type.InvokeMember
2. ContructorInfo.Invoke
3. Activator.CreateInstance(Type)
4. Activator.CreateInstance(assemblyName, typeName)
5. Assembly.CreateInstance(typeName)
最快的是方式 3 ,与 Direct Create 的差异在一个数量级之内,约慢 7 倍的水平。其他方式,至少在 40 倍以上,最慢的是方式 4 ,要慢三个数量级。
1.6.3 动态方法调用
方法调用分为编译期的早期绑定和运行期的动态绑定两种,称为Early-Bound Invocation和Late-Bound Invocation。Early-Bound Invocation可细分为Direct-call、Interface-call和Delegate-call。Late-Bound Invocation主要有Type.InvokeMember和MethodBase.Invoke,还可以通过使用LCG(Lightweight Code Generation)技术生成IL代码来实现动态调用。
从测试结果看,相比Direct Call,Type.InvokeMember要接近慢三个数量级;MethodBase.Invoke虽然比Type.InvokeMember要快三 倍,但比Direct Call仍慢270倍左右。可见动态方法调用的性能是非常低下的。我们的建议是:除非要满足特定的需求,否则不要使用!
1.6.4 推荐的使用原则
1. 如果可能,则避免使用反射和动态绑定
2. 使用接口调用方式将动态绑定改造为早期绑定
3. 使用Activator.CreateInstance(Type)方式动态创建对象
4. 使用typeof操作符代替GetType调用
1. 在已获得Type的情况下,却使用Assembly.CreateInstance(type.FullName)
1.7 基本代码技巧
这里描述一些应用场景下,可以提高性能的基本代码技巧。对处于关键路径的代码,进行这类的优化还是很有意义的。普通代码可以不做要求,但养成一种好的习惯也是有意义的。
1.7.1 循环写法
可以把循环的判断条件用局部变量记录下来。局部变量往往被编译器优化为直接使用寄存器,相对于普通从堆或栈中分配的变量速度快。如果访问的是复杂计算属性 的话,提升效果将更明显。for (int i = 0, j = collection.GetIndexOf(item); i & i++)
需要说明的是:这种写法对于CLR集合类的Count属性没有意义,原因是编译器已经按这种方式做了特别的优化。
1.7.2 拼装字符串
拼装好之后再删除是很低效的写法。有些方法其循环长度在大部分情况下为1,这种写法的低效就更为明显了:
public static string ToString(MetadataKey entityKey)
string str = && ;
object [] vals = entityKey.
for ( int i = 0 ; i & vals.L i ++ )
str += & , & + vals[i].ToString();
return str == && ? && : str.Remove( 0 , 1 );
推荐下面的写法:
if (str.Length == 0 )
str = vals[i].ToString();
str += & , & + vals[i].ToString();
其实这种写法非常自然,而且效率很高,完全不需要用个Remove方法绕来绕去。
1.7.3 避免两次检索集合元素
获取集合元素时,有时需要检查元素是否存在。通常的做法是先调用ContainsKey(或Contains)方法,然后再获取集合元素。这种写法非常符合逻辑。
但如果考虑效率,可以先直接获取对象,然后判断对象是否为null来确定元素是否存在。对于Hashtable,这可以节省一次GetHashCode调用和n次Equals比较。
如下面的示例:
public IData GetItemByID(Guid id)
IData data1 =
if ( this .idTable.ContainsKey(id.ToString())
data1 = this .idTable[id.ToString()] as ID
return data1;
其实完全可用一行代码完成:return this.idTable[id] as ID
1.7.4 避免两次类型转换
考虑如下示例,其中包含了两处类型转换:
if (obj is SomeType)
SomeType st = (SomeType)
st.SomeTypeMethod();
效率更高的做法如下:
SomeType st = obj as SomeT
if (st != null )
st.SomeTypeMethod();
1.8 Hashtable
Hashtable是一种使用非常频繁的基础集合类型。需要理解影响Hashtable的效率有两个因素:一是散列码(GetHashCode方法),二 是等值比较(Equals方法)。Hashtable首先使用键的散列码将对象分布到不同的存储桶中,随后在该特定的存储桶中使用键的Equals方法进 行查找。
良好的散列码是第一位的因素,最理想的情况是每个不同的键都有不同的散列码。Equals方法也很重要,因为散列只需要做一次,而存储桶中查找键可能需要做多次。从实际经验看,使用Hashtable时,Equals方法的消耗一般会占到一半以上。
System.Object类提供了默认的GetHashCode实现,使用对象在内存中的地址作为散列码。我们遇到过一个用Hashtable来缓存对 象的例子,每次根据传递的OQL表达式构造出一个ExpressionList对象,再调用QueryCompiler的方法编译得到 CompiledQuery对象。以ExpressionList对象和CompiledQuery对象作为键值对存储到Hashtable中。 ExpressionList对象没有重载GetHashCode实现,其超类ArrayList也没有,这样最后用的就是System.Object类
的GetHashCode实现。由于ExpressionList对象会每次构造,因此它的HashCode每次都不同,所以这个 CompiledQueryCache根本就没有起到预想的作用。这个小小的疏漏带来了重大的性能问题,由于解析OQL表达式频繁发生,导致 CompiledQueryCache不断增长,造成服务器内存泄漏!解决这个问题的最简单方法就是提供一个常量实现,例如让散列码为常量0。虽然这会导 致所有对象汇聚到同一个存储桶中,效率不高,但至少可以解决掉内存泄漏问题。当然,最终还是会实现一个高效的GetHashCode方法的。
以上介绍这些Hashtable机理,主要是希望大家理解:如果使用Hashtable,你应该检查一下对象是否提供了适当的GetHashCode和Equals方法实现。否则,有可能出现效率不高或者与预期行为不符的情况。
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:53130次
排名:千里之外
原创:25篇
(1)(2)(5)(11)(6)(1)(4)(2)C#的性能到底有多差?
C#的性能到底有多差?
[摘要:那篇文章领导人人去看看c#的机能题目。固然了,做为比拟的选脚是c/c++。 起首道道测试情况: 操纵体系:win7 旗舰版 内存:2GB 硬盘:160GB 处置惩罚器:Intel Pentium Dual CPU T2330 @ 1.60GHZ 本文挨]
这篇文章带领大家来看看c#的性能问题。当然了,作为比较的选手是c/c++。
首先说说测试环境:
操作系统:win7 旗舰版
硬盘:160GB
处理器:Intel Pentium Dual CPU T2330 @ 1.60GHZ
本文打算基于以下几个方面讨论:
1.CPU使用情况;
2.内存使用情况;
3.基本类型的四则运算能力;
4.数学函数运算能力;
5.I/O操作能力;
6.数组运算能力;
7.异常处理能力;
8.STL vs FCL;
9.算法HeapSort;
10.矩阵乘;
11.嵌套for循环;
12.字符串连接。
接下来,我们一个一个详细地说:(注,测试程序都是直接运行编译后的exe)
1.CPU使用情况
在程序的整个运行过程中,两者的CPU占用情况都在50%上下浮动,算是不分上下。
2.内存使用情况
组程序执行过程中我记录的数据:
c/c++:216kb,832kb,904kb,944kb,1336kb。
c#:1972kb,1980kb,2000kb,2372kb,3024kb,5156kb。
通过这组数据,不难发现,在内存使用方面c#可算是一败涂地。注:
希望有人能解释下c#为什么会出现这种一路飙升的情况呢?!
3.基本类型的四则运算能力
费话不多说了,上代码:(注,所有的图中时间单位均为毫秒ms)
1 double intArithmetic("&int intMax) 2 { 3
double elapsedT 4
clock_t stopT 5
int intResult = 1; 6
int i = 1; 7
clock_t startTime = clock(); 9
while (i & intMax)10
intResult -= i++;12
intResult += i++;13
intResult *= i++;14
intResult /= i++;15
stopTime = clock();17 18
elapsedTime = (stopTime - startTime) / (CLOCKS_PER_SEC / (double) 1000.0);19
printf("Int arithmetic elapsed time: %1.0f ms with intMax of %ldn", elapsedTime, intMax);20
printf(" i: %dn", i);21
printf(" intResult: %dn", intResult);22
return elapsedT23 }
1 static long intArithmetic(int intMax) 2
long elapsedM 4
int intResult = 1; 5
int i = 0; 6
stopwatch.Start(); 8
while (i & intMax) 9
intResult -= i++;11
intResult += i++;12
intResult *= i++;13
intResult /= i++;14
elapsedMilliseconds = stopwatch.ElapsedM16
stopwatch.Reset();17 18
Console.WriteLine("Int arithmetic elapsed time: " + elapsedMilliseconds + 19
" ms with max of " + intMax);20
Console.WriteLine(" i: " + i);21
Console.WriteLine(" intResult: " + intResult);22
return elapsedM23
对比结果见下图:
针对double的
1 double doubleArithmetic(double doubleMin, double doubleMax) 2 { 3
double elapsedT 4
clock_t stopT 5
clock_t startTime = clock(); 6
double doubleResult = doubleM 8
double i = doubleM 9
while (i & doubleMax)10
doubleResult -= i++;12
doubleResult += i++;13
doubleResult *= i++;14
doubleResult /= i++;15
stopTime = clock();18
elapsedTime = (stopTime - startTime) / (CLOCKS_PER_SEC / (double) 1000.0);19
printf("Double arithmetic elapsed time: %1.0f ms with doubleMin %.15f, doubleMax %.15fn", elapsedTime, doubleMin, doubleMax);20
printf(" i: %fn", i);21
printf(" doubleResult: %.15fn", doubleResult);22
return elapsedT23 }
针对double的
1 static long doubleArithmetic(double doubleMin, double doubleMax) 2
long elapsedM 4
double doubleResult = doubleM 5
double i = doubleM 6
stopwatch.Start(); 8
while (i & doubleMax) 9
doubleResult -= i++;11
doubleResult += i++;12
doubleResult *= i++;13
doubleResult /= i++;14
elapsedMilliseconds = stopwatch.ElapsedM16
stopwatch.Reset();17
Console.WriteLine("Double arithmetic elapsed time: " + elapsedMilliseconds + 19
" ms with min of " + doubleMin + ", max of " + doubleMax);20
Console.WriteLine(" i: " + i);21
Console.WriteLine(" doubleResult: " + doubleResult);22
return elapsedM23
对比结果见下图:
针对long的
1 double longArithmetic(long long longMin, long long longMax) 2 { 3
double elapsedT 4
clock_t stopT 5
clock_t startTime = clock(); 6
long long longResult = longM 8
long long i = longM 9
while (i & longMax)10
longResult -= i++;12
longResult += i++;13
longResult *= i++;14
longResult /= i++;15
stopTime = clock();18
elapsedTime = (stopTime - startTime) / (CLOCKS_PER_SEC / (double) 1000.0);19
printf("Long arithmetic elapsed time: %1.0f ms with longMax %I64dn", elapsedTime, longMax);20
printf(" i: %I64dn", i);21
printf(" longResult: %I64dn", longResult);22
return elapsedT23 }
针对long的
1 static long longArithmetic(long intMin, long intMax) 2
long elapsedM 4
long intResult = intM 5
long i = intM 6
stopwatch.Start(); 8
while (i & intMax) 9
intResult -= i++;11
intResult += i++;12
intResult *= i++;13
intResult /= i++;14
elapsedMilliseconds = stopwatch.ElapsedM16
stopwatch.Reset();17
Console.WriteLine("long arithmetic elapsed time: " + elapsedMilliseconds + 19
" ms with min of " + intMin + ", max of " + intMax);20
Console.WriteLine(" i: " + i);21
Console.WriteLine(" intResult: " + intResult);22
return elapsedM23
对比结果见下图:
通过以上数据,发现两者对基本数据类型的操作性能差不多,属于同一个数量级。
同时,注意到,c#针对int型的性能要优于c/c++。
4.数学函数的运算能力
常用数学函数
1 double trig(double trigMax) 2 { 3
double elapsedT 4
clock_t stopT 5
clock_t startTime = clock(); 6
double squareR12 13
double i = 0.0;14
while (i & trigMax)15
sine = sin(i);17
cosine = cos(i);18
tangent = tan(i);19
logarithm = log10(i);20
squareRoot = sqrt(i);21
stopTime = clock();25
elapsedTime = (stopTime - startTime) / (CLOCKS_PER_SEC / (double) 1000.0);26
printf("Trig elapsed time: %1.0f ms with max of %1.0fn", elapsedTime, trigMax);27
printf(" i: %fn", i);28
printf(" sine: %.15fn", sine);29
printf(" cosine: %.15fn", cosine);30
printf(" tangent: %.15fn", tangent);31
printf(" logarithm: %.15fn", logarithm);32
printf(" squareRoot: %.15fn", squareRoot);33
return elapsedT34 }
常用数学函数
1 static long trig(double trigMax) 2
long elapsedM 4
double sine = 0.0D; 6
double cosine = 0.0D; 7
double tangent = 0.0D; 8
double logarithm = 0.0D; 9
double squareRoot = 0.0D;
double i = 0.0D;11 12
stopwatch.Start();13
while(i & trigMax)14
sine = Math.Sin(i);16
cosine = Math.Cos(i);17
tangent = Math.Tan(i);18
logarithm = Math.Log10(i);19
squareRoot = Math.Sqrt(i);20
elapsedMilliseconds = stopwatch.ElapsedM23
stopwatch.Reset();24
Console.WriteLine("Trig elapsed time: " + elapsedMilliseconds + 26
" ms with max of " + trigMax);27
Console.WriteLine(" i: " + i);28
Console.WriteLine(" sine: " + sine);29
Console.WriteLine(" cosine: " + cosine);30
Console.WriteLine(" tangent: " + tangent);31
Console.WriteLine(" logarithm: " + logarithm);32
Console.WriteLine(" squareRoot: " + squareRoot);33
return elapsedM34
对比结果如下图:
通过数据,我们发现c#在计算sin、cos、tan、log、sqrt等数学函数方面明显
优于c/c++。大家以后注意了哦&&
5.I/O操作能力
写文件、读文件
1 double io(int ioMax) 2 { 3
double elapsedT 4
clock_t stopT 5
clock_t startTime = clock(); 6
stream = fopen("F:\TestC.txt", "w"); 9
int i = 0;10
while (i++ & ioMax)11
fputs("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghn", stream);13
fclose(stream);15 16
char readLine[100];17
stream = fopen("F:\TestC.txt", "r");18
while (i++ & ioMax)20
fgets(readLine, 100, stream);22
fclose(stream);24 25
stopTime = clock();26
elapsedTime = (stopTime - startTime) / (CLOCKS_PER_SEC / (double) 1000.0);27
printf("I/O elapsed time: %1.0f ms with max of %ldn", elapsedTime, ioMax);28
printf(" i: %dn", i);29
printf(" readLine: %sn", readLine);30 31
return elapsedT32 }
写文件、读文件
1 static long io(int ioMax) 2
long elapsedM 4
String fileName = "F:\TestCSharp.txt"; 6
String textLine = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefgh"; 7
int i = 0; 8
String myLine = ""; 9 10
stopwatch.Start();11
StreamWriter streamWriter = new StreamWriter(fileName);14
while (i++ & ioMax)15
streamWriter.WriteLine(textLine);17
streamWriter.Close();19 20
StreamReader streamReader = new StreamReader(fileName);22
while (i++ & ioMax) 23
myLine = streamReader.ReadLine();25
catch (IOException e)28
System.Console.Write(e.Message);30
elapsedMilliseconds = stopwatch.ElapsedM34
stopwatch.Reset();35
Console.WriteLine("IO elapsed time: " + elapsedMilliseconds + 37
" ms with max of " + ioMax);38
Console.WriteLine(" i: " + i);39
Console.WriteLine(" myLine: " + myLine);40
return elapsedM41
对比结果见下图:
通过数据,我们发现c#的性能略优于c/c++。
6.数组运算能力
数组基本操作
1 double array(int n) 2 { 3
int i, k, *x, *y; 4
double elapsedT 6
clock_t stopT 7
clock_t startTime = clock(); 8
x = new int[n]; 9
y = new int[n];10 11
for (i = 0; i & i++) {12
x[i] = i + 1;13
y[i] = 0;14
for (k=0; k&1000; k++) {16
for (i = n-1; i &= 0; i--) {17
y[i] += x[i];18
stopTime = clock();22
elapsedTime = (stopTime - startTime) / (CLOCKS_PER_SEC / (double) 1000.0);23
printf("array elapsed time: %1.0f ms - %ld %ldn", elapsedTime, y[0], y[n-1]);24 25
delete[]26
delete[]27 28
return elapsedT29 }
数组基本操作
1 static long array(int n) 2
long elapsedM 4
int i, j, 5
stopwatch.Start(); 9
if(n & 1) n = 1;10
x = new int[n];12
y = new int[n];13
for (i = 0; i & i++)15
x[i] = i + 1;17
y[i] = 0;18
for (k = 0; k & 1000; k++ )20
for (j = n-1; j &= 0; j--)21
y[j] += x[j];22 23
elapsedMilliseconds = stopwatch.ElapsedM24
stopwatch.Reset();25
Console.WriteLine("Array elapsed time: " + elapsedMilliseconds + " ms - "+ y[0].ToString() + " " + y[n-1].ToString());27
return elapsedM28
对比结果见下图:
通过数据发现,在数组基本操作方面c#较弱(大家都能知道为什么!)。
7.异常处理能力
由于这部分代码稍多,所以就不在这贴了,一多后台就崩溃了(在写这篇文章其间博客园后台不知崩溃了n次,对此很无语&&)。
后面会给出所有源码的下载地址。
对比结果见下图:
通过数据,发现c/c++在这方面远胜于c#,可是大家不要就此止步,再想想为什么。注:
可参考Jeffrey Richter的CLR via C#中有关异常的讨论。
8.STL vs FCL
郁闷啊!不知道为什么,一提交网站不停地崩溃,我写的东西不停地全没。没有办法,代码就不贴了。直接上结果。
这里,通过map&T1,T2&和Dictionary&T1,T2&的两组对比和一组Vector&T&和List&T&的对比。
第一组对比结果见下图:
这里涉及到的基本操作包括,插入和测试key是否存在。通过数据,发现c#的Dictionary&T1,T2&
的性能远远优于c/c++(10倍有余)。
第二组对比结果见下图:
通过数据发现c#版的字典性能远优于c/c++中的。
Vector&T&和List&T&的对比结果见下图:
通过数据发现两者性能不相上下。
9.算法HeapSort
对比结果见下图:
通过数据发现,c/c++要优于c#(看代码发现罪魁祸首在于数组操作)。
对比结果见下图:
通过数据发现,c/c++的性能几乎是c#的两倍。截止到目前,我们应该知道
c#在数组、矩阵操作方面是个弱项(为什么?)。大家在以后的工作学习中要学会
扬长避短哦&&
11.嵌套for循环
对比结果见下图:
通过数据发现,c#略差于c/c++。
12.字符串连接
大家都知道,字符串连接是个耗时的操作,现在让我们来看看两者的表现,对比结果见下图:
令人意外的是,c#在这方面的性能要优于c/c++(几乎2倍有余)。
13.总和对比
这里的总和指的是前面所有项的时间加和,对比结果见下图:
通过数据发现,在一般的应用中c#的性能能达到c/c++的70%-80%。
作为托管代码来说这已经很不错了,你说呢?
&& &写了这么多了,也许有人要问我了,你到底想说什么呢?其实,作为语言,我们要懂得什么情况下用哪个。我的观点是,
精通一门,熟悉或了解另一门。怎么说呢?如果您已经工作了,那么很容易做决定,您工作中不停要用到的语言自然是您
应该精通的了。如果您和我一样还是学生的话,那也很好办,凭自己的喜好呗,但不要太过执着于语言本身。比如说我,
花了大量时间在c#上,今年暑假要去公司实习(大半会用到c++),所以你也要了解c++,不是吗?
&& &现在的社会,一切都讲求速度。特别是在软件行业更是如此。软件不仅要求运行速度,很多时候更多的是开发速度,而且是第一个
遇到的问题。不是有哪位大牛说过嘛,先让它run起来,再让它run地更快。结合上面的形式,一个项目中即需要懂c#的也需要懂c++的。
下面的建议摘自别人的博客原文:
Except for writing time-critical blocks of code, prefer C#. Write all your algorithmic code in C++ (not VC++ .NET), compile it into a dll and call that using a Dll Interop through C#. This should balance the performance. This technique is not new or not invented by me or anyone. It's similar the old age C programming vs Assembly, where people on one camp fight assembly programming is faster and the other camp stating C is easier to develop and then people started using assembly embedded within a C program for time-critical applications using an asm block.
History repeats...!
在一个真正的软件项目中我们要做到开发速度与运行速度的折中。另外,建议大家看一下《Framework Design Guidelines, Conventions,Idioms,and Patterns
for Reusable .NET Libraries》,这本书深刻阐述了.NET的设计过程,其认真程度(包括一个命名)令人折服。
http://www.tommti-systems.de/go.html?http://www.tommti-systems.de/main-Dateien/reviews/languages/benchmarks.html
/KB/cs/CSharpVsCPP.aspx
/en-us/library/ms173196(VS.80).aspx
http://dotnet./node/46342
http://www.techgalaxy.net/Docs/Dev/5ways.htm
&& &注:本文基本上基于参考1写成。有兴趣的读者点击这里下载源代码。
感谢关注 Ithao123C#频道,是专门为互联网人打造的学习交流平台,全面满足互联网人工作与学习需求,更多互联网资讯尽在 IThao123!
Laravel是一套简洁、优雅的PHP Web开发框架(PHP Web Framework)。它可以让你从面条一样杂乱的代码中解脱出来;它可以帮你构建一个完美的网络APP,而且每行代码都可以简洁、富于表达力。
Hadoop是一个由Apache基金会所开发的分布式系统基础架构。
用户可以在不了解分布式底层细节的情况下,开发分布式程序。充分利用集群的威力进行高速运算和存储。
Hadoop实现了一个分布式文件系统(Hadoop Distributed File System),简称HDFS。HDFS有高容错性的特点,并且设计用来部署在低廉的(low-cost)硬件上;而且它提供高吞吐量(high throughput)来访问应用程序的数据,适合那些有着超大数据集(large data set)的应用程序。HDFS放宽了(relax)POSIX的要求,可以以流的形式访问(streaming access)文件系统中的数据。
Hadoop的框架最核心的设计就是:HDFS和MapReduce。HDFS为海量的数据提供了存储,则MapReduce为海量的数据提供了计算。
产品设计是互联网产品经理的核心能力,一个好的产品经理一定在产品设计方面有扎实的功底,本专题将从互联网产品设计的几个方面谈谈产品设计
随着国内互联网的发展,产品经理岗位需求大幅增加,在国内,从事产品工作的大部分岗位为产品经理,其实现实中,很多从事产品工作的岗位是不能称为产品经理,主要原因是对产品经理的职责不明确,那产品经理的职责有哪些,本专题将详细介绍产品经理的主要职责
IThao123周刊

我要回帖

更多关于 喷气发动机效率能达到 的文章

 

随机推荐