跨程序集,类与结构体究竟谁快
前边的叁遍测量试验都以在同一个项目内的,既处于同三个主次集。那么,跨程序集调用会怎样呢?
上次自个儿对C#类与结构体做了二次速度评测(卡塔 尔(阿拉伯语:قطر。经过意气风发段时间考虑,发掘还能够更进一竿搜求——
原先自身直接有个疑忌——在C#中,毕竟是类(class卡塔 尔(英语:State of Qatar)比非常快,依旧结构体(struct卡塔尔不慢?
眼看并未有索求。
因为为了确定保障可维护性,我们会把一些常用操作封装到类库中去。然后实际项目支出时,援引该类库,使得应用方案中存在八个等级次序。编写翻译实现后,将会是八个根本的exe和若干个dll文件,主exe程序聚集的代码会调用dll程序集,既产生了跨程序集调用。
首先、栈变量。上次的“硬编码”,是访谈类中的静态变量的。若改为访谈函数中的栈变量,质量会不会有所升高?
其次、栈分配(stackalloc卡塔 尔(阿拉伯语:قطر。既然要测量检验栈变量,我们还足以顺便测量试验一下在栈上分配的内存块的拜访质量。
其三、60位整数。由于叁14位系统的成功,我们曾经习认为常了运用三十一个人整数(int卡塔 尔(英语:State of Qatar)。未来61位系统稳步普遍,大家得为此做好盘算。对于指针操作时平日要用到的偏移量增减运算来讲,是选取叁十二人整数,照旧使用64个人整数,或写两套代码?那需求测量检验后工夫说了算。
第四、密闭类(sealed卡塔尔国。听他们讲密闭类能增长品质,大家得以测量检验一下。有三种测验方法,一是为原来的派生类扩张sealed关键字,二是专门其余写三个密闭类。笔者决定同时利用那三种办法,分别测验其属性。
新近自己遇上贰个难点,供给将生龙活虎部分运算大的指针操作代码给封装一下。原先为了品质,那几个代码是以硬编码的款型混杂在算法逻辑之中,不但影响了算法逻辑的可读性,其自己的指针操作代码枯燥、难懂、易写错,不易维护。所以本身期望将其包装一下,简化代码编写、升高可维护性,但还要要硬着头皮地保管品质。
由于这些指针操作代码很利索,简单的卷入不可能一举成功难点,还索要动用接口(interface卡塔尔国以达成部分动态调用成效。
为了简化代码,还计划达成部分泛型方法。
道理当然是这样的还想因叁十三人指针、六18位指针的比不上而构造泛型类,缺憾开采C#不扶植将int/long作为泛型类型限制,只可以作罢。将统筹改为——分别为32人指针、60位指针编写差别的类,它们贯彻同八个接口。
解析跨程序集调用的特性,有助优化类库架构的宏图。
生机勃勃、测量试验代码
测验代码如下——
using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
namespace TryPointerCall
{
/// <summary>
/// 指针操作接口
/// </summary>
public interface IPointerCall
{
/// <summary>
/// 指针操作
/// </summary>
/// <param name="p">源指针</param>
/// <returns>修改后指针</returns>
unsafe byte* Ptr(byte* p);
}
#region 非泛型
/// <summary>
/// [非泛型] 指针操作基类
/// </summary>
public abstract class PointerCall : IPointerCall
{
public abstract unsafe byte* Ptr(byte* p);
}
/// <summary>
/// [非泛型] 指针操作派生类: 指针+偏移
/// </summary>
public sealed class PointerCallAdd : PointerCall
{
/// <summary>
/// 偏移值
/// </summary>
public int Offset = 0;
public override unsafe byte* Ptr(byte* p)
{
return unchecked(p + Offset);
}
}
/// <summary>
/// [非泛型] 指针操作密封类: 指针+偏移
/// </summary>
public sealed class SldPointerCallAdd : IPointerCall
{
/// <summary>
/// 偏移值
/// </summary>
public int Offset = 0;
public unsafe byte* Ptr(byte* p)
{
return unchecked(p + Offset);
}
}
/// <summary>
/// [非泛型] 指针操作结构体: 指针+偏移
/// </summary>
public struct SPointerCallAdd : IPointerCall
{
/// <summary>
/// 偏移值
/// </summary>
public int Offset;
public unsafe byte* Ptr(byte* p)
{
return unchecked(p + Offset);
}
}
#endregion
#region 泛型
// !!! C#不支持将整数类型作为泛型约束 !!!
//public abstract class GenPointerCall<T> : IPointerCall where T: int, long
//{
// public abstract unsafe byte* Ptr(byte* p);
// void d()
// {
// }
//}
#endregion
#region 全部测试
/// <summary>
/// 指针操作的一些常用函数
/// </summary>
public static class PointerCallTool
{
#if DEBUG
private const int CountLoop = 10000000; // 循环次数
#else
private const int CountLoop = 200000000; // 循环次数
#endif
/// <summary>
/// 调用指针操作
/// </summary>
/// <typeparam name="T">具有IPointerCall接口的类型。</typeparam>
/// <param name="ptrcall">调用者</param>
/// <param name="p">源指针</param>
/// <returns>修改后指针</returns>
public static unsafe byte* CallPtr<T>(T ptrcall, byte* p) where T : IPointerCall
{
return ptrcall.Ptr(p);
}
public static unsafe byte* CallClassPtr<T>(T ptrcall, byte* p) where T : PointerCall
{
return ptrcall.Ptr(p);
}
public static unsafe byte* CallRefPtr<T>(ref T ptrcall, byte* p) where T : IPointerCall
{
return ptrcall.Ptr(p);
}
// C#不允许将特定的结构体作为泛型约束。所以对于结构体只能采用上面那个方法,通过IPointerCall接口进行约束,可能会造成性能下降。
//public static unsafe byte* SCallPtr<T>(T ptrcall, byte* p) where T : SPointerCallAdd
//{
// return ptrcall.Ptr(p);
//}
private static int TryIt_Static_Offset;
private static unsafe byte* TryIt_Static_Ptr(byte* p)
{
return unchecked(p + TryIt_Static_Offset);
}
/// <summary>
/// 执行测试 - 静态调用
/// </summary>
/// <param name="sOut">文本输出</param>
private static unsafe void TryIt_Static(StringBuilder sOut, int CountLoop)
{
TryIt_Static_Offset = 1;
// == 性能测试 ==
byte* p = null;
Stopwatch sw = new Stopwatch();
int i;
unchecked
{
#region 测试
// 硬编码.栈变量
int iOffset = 1;
sw.Reset();
sw.Start();
for (i = 0; i < CountLoop; ++i)
{
p = p + iOffset;
}
sw.Stop();
sOut.AppendLine(string.Format("硬编码.栈变量:t{0}", sw.ElapsedMilliseconds));
// 硬编码.栈分配
int* pOffset = stackalloc int[1];
pOffset[0] = 1;
sw.Reset();
sw.Start();
for (i = 0; i < CountLoop; ++i)
{
p = p + pOffset[0];
}
sw.Stop();
sOut.AppendLine(string.Format("硬编码.栈分配:t{0}", sw.ElapsedMilliseconds));
// 硬编码.静态
sw.Reset();
sw.Start();
for (i = 0; i < CountLoop; ++i)
{
p = p + TryIt_Static_Offset;
}
sw.Stop();
sOut.AppendLine(string.Format("硬编码.静态:t{0}", sw.ElapsedMilliseconds));
// 静态调用
sw.Reset();
sw.Start();
for (i = 0; i < CountLoop; ++i)
{
p = TryIt_Static_Ptr(p);
}
sw.Stop();
sOut.AppendLine(string.Format("静态调用:t{0}", sw.ElapsedMilliseconds));
#endregion // 测试
}
}
private static long TryIt_Static64_Offset;
private static unsafe byte* TryIt_Static64_Ptr(byte* p)
{
return unchecked(p + TryIt_Static64_Offset);
}
/// <summary>
/// 执行测试 - 静态调用
/// </summary>
/// <param name="sOut">文本输出</param>
private static unsafe void TryIt_Static64(StringBuilder sOut, int CountLoop)
{
TryIt_Static64_Offset = 1;
// == 性能测试 ==
byte* p = null;
Stopwatch sw = new Stopwatch();
int i;
unchecked
{
#region 测试
// 硬编码.栈变量
long iOffset = 1;
sw.Reset();
sw.Start();
for (i = 0; i < CountLoop; ++i)
{
p = p + iOffset;
}
sw.Stop();
sOut.AppendLine(string.Format("64硬编码.栈变量:t{0}", sw.ElapsedMilliseconds));
// 硬编码.栈分配
long* pOffset = stackalloc long[1];
pOffset[0] = 1;
sw.Reset();
sw.Start();
for (i = 0; i < CountLoop; ++i)
{
p = p + pOffset[0];
}
sw.Stop();
sOut.AppendLine(string.Format("64硬编码.栈分配:t{0}", sw.ElapsedMilliseconds));
// 硬编码.静态
sw.Reset();
sw.Start();
for (i = 0; i < CountLoop; ++i)
{
p = p + TryIt_Static64_Offset;
}
sw.Stop();
sOut.AppendLine(string.Format("64硬编码.静态:t{0}", sw.ElapsedMilliseconds));
// 静态调用
sw.Reset();
sw.Start();
for (i = 0; i < CountLoop; ++i)
{
p = TryIt_Static64_Ptr(p);
}
sw.Stop();
sOut.AppendLine(string.Format("64静态调用:t{0}", sw.ElapsedMilliseconds));
#endregion // 测试
}
}
/// <summary>
/// 执行测试 - 非泛型
/// </summary>
/// <param name="sOut">文本输出</param>
private static unsafe void TryIt_NoGen(StringBuilder sOut, int CountLoop)
{
// 创建
PointerCallAdd pca = new PointerCallAdd();
SldPointerCallAdd dpca = new SldPointerCallAdd();
SPointerCallAdd spca;
pca.Offset = 1;
spca.Offset = 1;
// 转型
PointerCall pca_base = pca;
IPointerCall pca_itf = pca;
IPointerCall dpca_itf = dpca;
IPointerCall spca_itf = spca;
// == 性能测试 ==
byte* p = null;
Stopwatch sw = new Stopwatch();
int i;
unchecked
{
#region 调用
#region 直接调用
// 调用派生类
sw.Reset();
sw.Start();
for (i = 0; i < CountLoop; ++i)
{
p = pca.Ptr(p);
}
sw.Stop();
sOut.AppendLine(string.Format("调用派生类:t{0}", sw.ElapsedMilliseconds));
// 调用密封类
sw.Reset();
sw.Start();
for (i = 0; i < CountLoop; ++i)
{
p = dpca.Ptr(p);
}
sw.Stop();
sOut.AppendLine(string.Format("调用密封类:t{0}", sw.ElapsedMilliseconds));
// 调用结构体
sw.Reset();
sw.Start();
for (i = 0; i < CountLoop; ++i)
{
p = spca.Ptr(p);
}
sw.Stop();
sOut.AppendLine(string.Format("调用结构体:t{0}", sw.ElapsedMilliseconds));
#endregion // 直接调用
#region 间接调用
// 调用基类
sw.Reset();
sw.Start();
for (i = 0; i < CountLoop; ++i)
{
p = pca_base.Ptr(p);
}
sw.Stop();
sOut.AppendLine(string.Format("调用基类:t{0}", sw.ElapsedMilliseconds));
// 调用派生类的接口
sw.Reset();
sw.Start();
for (i = 0; i < CountLoop; ++i)
{
p = pca_itf.Ptr(p);
}
sw.Stop();
sOut.AppendLine(string.Format("调用派生类的接口:t{0}", sw.ElapsedMilliseconds));
// 调用密封类的接口
sw.Reset();
sw.Start();
for (i = 0; i < CountLoop; ++i)
{
p = dpca_itf.Ptr(p);
}
sw.Stop();
sOut.AppendLine(string.Format("调用密封类的接口:t{0}", sw.ElapsedMilliseconds));
// 调用结构体的接口
sw.Reset();
sw.Start();
for (i = 0; i < CountLoop; ++i)
{
p = spca_itf.Ptr(p);
}
sw.Stop();
sOut.AppendLine(string.Format("调用结构体的接口:t{0}", sw.ElapsedMilliseconds));
#endregion // 间接调用
#endregion // 调用
#region 泛型调用
#region 泛型基类约束
// 基类泛型调用派生类
sw.Reset();
sw.Start();
for (i = 0; i < CountLoop; ++i)
{
p = CallClassPtr(pca, p);
}
sw.Stop();
sOut.AppendLine(string.Format("基类泛型调用派生类:t{0}", sw.ElapsedMilliseconds));
// 基类泛型调用基类
sw.Reset();
sw.Start();
for (i = 0; i < CountLoop; ++i)
{
p = CallClassPtr(pca_base, p);
}
sw.Stop();
sOut.AppendLine(string.Format("基类泛型调用基类:t{0}", sw.ElapsedMilliseconds));
#endregion // 泛型基类约束
#region 泛型接口约束 - 直接调用
// 接口泛型调用派生类
sw.Reset();
sw.Start();
for (i = 0; i < CountLoop; ++i)
{
p = CallPtr(pca, p);
}
sw.Stop();
sOut.AppendLine(string.Format("接口泛型调用派生类:t{0}", sw.ElapsedMilliseconds));
// 接口泛型调用密封类
sw.Reset();
sw.Start();
for (i = 0; i < CountLoop; ++i)
{
p = CallPtr(dpca, p);
}
sw.Stop();
sOut.AppendLine(string.Format("接口泛型调用密封类:t{0}", sw.ElapsedMilliseconds));
// 接口泛型调用结构体
sw.Reset();
sw.Start();
for (i = 0; i < CountLoop; ++i)
{
p = CallPtr(spca, p);
}
sw.Stop();
sOut.AppendLine(string.Format("接口泛型调用结构体:t{0}", sw.ElapsedMilliseconds));
// 接口泛型调用结构体引用
sw.Reset();
sw.Start();
for (i = 0; i < CountLoop; ++i)
{
p = CallRefPtr(ref spca, p);
}
sw.Stop();
sOut.AppendLine(string.Format("接口泛型调用结构体引用:t{0}", sw.ElapsedMilliseconds));
#endregion // 直接调用
#region 间接调用
// 接口泛型调用基类
sw.Reset();
sw.Start();
for (i = 0; i < CountLoop; ++i)
{
p = CallPtr(pca_base, p);
}
sw.Stop();
sOut.AppendLine(string.Format("接口泛型调用基类:t{0}", sw.ElapsedMilliseconds));
// 接口泛型调用派生类的接口
sw.Reset();
sw.Start();
for (i = 0; i < CountLoop; ++i)
{
p = CallPtr(pca_itf, p);
}
sw.Stop();
sOut.AppendLine(string.Format("接口泛型调用派生类的接口:t{0}", sw.ElapsedMilliseconds));
// 接口泛型调用密封类的接口
sw.Reset();
sw.Start();
for (i = 0; i < CountLoop; ++i)
{
p = CallPtr(dpca_itf, p);
}
sw.Stop();
sOut.AppendLine(string.Format("接口泛型调用密封类的接口:t{0}", sw.ElapsedMilliseconds));
// 接口泛型调用结构体的接口
sw.Reset();
sw.Start();
for (i = 0; i < CountLoop; ++i)
{
p = CallPtr(spca_itf, p);
}
sw.Stop();
sOut.AppendLine(string.Format("接口泛型调用结构体的接口:t{0}", sw.ElapsedMilliseconds));
#endregion // 间接调用
#endregion // 泛型调用
}
}
/// <summary>
/// 执行测试 - 泛型
/// </summary>
/// <param name="sOut">文本输出</param>
private static unsafe void TryIt_Gen(StringBuilder sOut, int CountLoop)
{
// !!! C#不支持将整数类型作为泛型约束 !!!
}
/// <summary>
/// 执行测试
/// </summary>
public static string TryIt()
{
StringBuilder sOut = new StringBuilder();
sOut.AppendLine("== PointerCallTool.TryIt() ==");
TryIt_Static(sOut, CountLoop);
TryIt_Static64(sOut, CountLoop);
TryIt_NoGen(sOut, CountLoop);
TryIt_Gen(sOut, CountLoop);
sOut.AppendLine();
return sOut.ToString();
}
/// <summary>
/// 执行测试 - static
/// </summary>
public static string TryItStatic()
{
StringBuilder sOut = new StringBuilder();
int cnt = CountLoop * 10;
sOut.AppendLine("== PointerCallTool.TryItStatic() ==");
TryIt_Static(sOut, cnt);
TryIt_Static64(sOut, cnt);
sOut.AppendLine();
return sOut.ToString();
}
}
#endregion
}
在C#中,有两类包装手艺——
1.基于类(class卡塔 尔(阿拉伯语:قطر的卷入。在基类中定义好操作方法,然后在派生类中贯彻际操作作方法。
2.基于结构体(struct卡塔 尔(英语:State of Qatar)的卷入。在接口中定义好操作方法,然后在结构体中落到实处该接口的操作方法。
自己分别接收这两类包装技艺编写测量试验代码,然后做品质测量试验。
生机勃勃、测量试验方法
二、测量检验境遇
编译器——
VS2005:Visual Studio 2005 SP1。
VS2010:Visual Studio 2010 SP1。
行使上述编写翻译器编写翻译为Release版程序,最大速度优化。
机器A——
HP CQ42-153TX
处理器:Intel Core i5-430M(2.26GHz, Turbo 2.53GHz, 3MB L3)
内部存款和储蓄器体量:2GB (DD昂科雷3-1066)
机器B——
DELL Latitude E6320
处理器:Intel i3-2310M(2.1GHz, 3MB L3)
内部存款和储蓄器体量:4GB (DD奥德赛3-1333,双大路)
测验碰着——
A_2005:机器A,VS2005,Window 7 32位。
A_2010:机器A,VS2010,Window 7 32位。
B_2005:机器B,VS2005,Window XP SP3 32位。
B_2010:机器B,VS2010,Window XP SP3 32位。
B64_2005:机器B,VS2005,Window 7 64位(x64)。
B64_2010:机器B,VS2010,Window 7 64位(x64)。
经过每每思忖,考虑 类、结构体、接口、泛型
的整合,作者搜索了15种函数调用格局——
硬编码
静态调用
调用派生类
调用结构体
调用基类
调用派生类的接口
调用结构体的接口
基类泛型调用派生类
基类泛型调用基类
接口泛型调用派生类
接口泛型调用结构体
接口泛型调用结构体援引
接口泛型调用基类
接口泛型调用派生类的接口
接口泛型调用结构体的接口
骨子里步骤很简短——
1.重复展开VS二〇〇五。或关闭建设方案。
2.新建二个“Windows应用程序”项目。如“TryPointerCallns二零零五”。
3.增添种类,选拔上次的“TryPointerCall二零零七”。今后设计方案中就有三个类型了。
4.增选第2步时新建的品类(TryPointerCallns贰零零陆卡塔尔,增加援引,将“TryPointerCall2006”加上去。
5.调节项目性质,允许不安全的代码。
6.新建三个类(TestCall卡塔 尔(英语:State of Qatar),将测量试验代码全体Copy过来(注意只复制测量试验代码,不复制IPointerCall、PointerCall等贯彻卡塔 尔(英语:State of Qatar)。
7.更改窗口分界面,调用TestCall的测验代码。
三、硬编码与静态调用 的测验结果(栈变量、栈分配、陆拾四位整数卡塔 尔(英语:State of Qatar)
因为硬编码与静态调用很也许会被实行函数展开优化,速度显明比别的测验项目要快。所笔者其余写了多少个测验函数(TryItStatic卡塔 尔(阿拉伯语:قطر,将循环次数设为原本的10倍。
测量试验结果如下(单位:飞秒)——
模式 | A_2005 | A_2010 | B_2005 | B_2010 | B64_2005 | B64_2010 |
---|---|---|---|---|---|---|
硬编码.栈变量: | 1608 | 1623 | 957 | 966 | 960 | 959 |
硬编码.栈分配: | 1612 | 1617 | 1073 | 957 | 961 | 960 |
硬编码.静态: | 1609 | 1613 | 957 | 971 | 961 | 960 |
静态调用: | 1608 | 1611 | 1063 | 958 | 961 | 963 |
64硬编码.栈变量: | 1610 | 1617 | 967 | 957 | 959 | 1010 |
64硬编码.栈分配: | 1610 | 1619 | 1034 | 957 | 960 | 1012 |
64硬编码.静态: | 1609 | 1618 | 999 | 996 | 957 | 1010 |
64静态调用: | 1610 | 1615 | 959 | 1002 | 957 | 7696 |
结果深入分析——
先看三拾几个人与陆13个人的差异。发将来大多数景色,三十一个人与60位的速度是大器晚成律的。唯豆蔻梢头正是六12个人整数运算代码在“64人平台+VS二〇〇九”上运营时,速度比在30个人下还慢,尤其是静态调用慢了有些倍,硬编码代码的进程也具备下滑。真的很意外,既然运转的是雷同份程序,为啥六拾个人比32个人还慢,难道是.Net
4.0在x64平台上的即时编写翻译器的难点?不解。
栈变量、栈分配、静态变量的访问速度差相当少千篇大器晚成律,看来能够放心地任意动用。
看来现在写指针操作代码时,只写陆拾几个人整数版就能够了。
测量试验代码为——
四、密封类 的测量检验结果
测量试验结果如下(单位:微秒卡塔尔——
模式 A_2005 A_2010 B_2005 B_2010 B64_2005B64_2010
硬编码.栈变量: 162 162
95 95 96
95
硬编码.栈分配: 161 161
95 95 95
97
硬编码.静态: 161 165
97 95 97
95
静态调用: 161 163
95 95 96
97
64硬编码.栈变量: 161161989596
100
64硬编码.栈分配: 160162959795
100
64硬编码.静态: 162 162
95 97 95
100
64静态调用: 161 161
95 95 97
770
调用派生类: 563 568
670 668 676
580
调用密闭类: 161 162
101 103 102
767
调用结构体: 163 161
116 102 191
772
调用基类: 566 573
668 660 675
577
调用派生类的接口: 727 731
767 862 862
770
调用密闭类的接口: 721 730
957 862 870
771
调用结构体的接口: 104511341318134013441253
基类泛型调用派生类: 910795127478912561287
基类泛型调用基类: 902 785
1092 676 1346
1250
接口泛型调用派生类: 1407733163486216331633
接口泛型调用密封类: 1405808173395617431638
接口泛型调用结构体: 5661606711018641250
接口泛型调用结构体援引: 48016170098769961
接口泛型调用基类: 1409728176776416311635
接口泛型调用派生类的接口:
1410727170296617301634
接口泛型调用密封类的接口:
140280817壹玖玖贰816351637
接口泛型调用结构体的接口:
161711281859149922082117
将测量检验结果再行排版一下,特出分裂完毕格局的快慢分别——
环境 | 分类 | 基类 | 派生类 | 密封类 | 结构体 | 结构体的引用 |
---|---|---|---|---|---|---|
A_2005 | 直接调用 | 566 | 563 | 161 | 163 | |
接口调用 | 727 | 721 | 1045 | |||
基类约束泛型调用 | 902 | 910 | ||||
接口约束泛型调用 | 1407 | 1405 | 566 | 480 | ||
接口约束泛型调用接口 | 1409 | 1410 | 1402 | 1617 | ||
A_2010 | 直接调用 | 573 | 568 | 162 | 161 | |
接口调用 | 731 | 730 | 1134 | |||
基类约束泛型调用 | 785 | 795 | ||||
接口约束泛型调用 | 733 | 808 | 160 | 161 | ||
接口约束泛型调用接口 | 728 | 727 | 808 | 1128 | ||
B_2005 | 直接调用 | 668 | 670 | 101 | 116 | |
接口调用 | 767 | 957 | 1318 | |||
基类约束泛型调用 | 1092 | 1274 | ||||
接口约束泛型调用 | 1634 | 1733 | 671 | 700 | ||
接口约束泛型调用接口 | 1767 | 1702 | 1719 | 1859 | ||
B_2010 | 直接调用 | 660 | 668 | 103 | 102 | |
接口调用 | 862 | 862 | 1340 | |||
基类约束泛型调用 | 676 | 789 | ||||
接口约束泛型调用 | 862 | 956 | 101 | 98 | ||
接口约束泛型调用接口 | 764 | 966 | 958 | 1499 | ||
B64_2005 | 直接调用 | 675 | 676 | 102 | 191 | |
接口调用 | 862 | 870 | 1344 | |||
基类约束泛型调用 | 1346 | 1256 | ||||
接口约束泛型调用 | 1633 | 1743 | 864 | 769 | ||
接口约束泛型调用接口 | 1631 | 1730 | 1635 | 2208 | ||
B64_2010 | 直接调用 | 577 | 580 | 767 | 772 | |
接口调用 | 770 | 771 | 1253 | |||
基类约束泛型调用 | 1250 | 1287 | ||||
接口约束泛型调用 | 1633 | 1638 | 1250 | 961 | ||
接口约束泛型调用接口 | 1635 | 1634 | 1637 | 2117 |
综合来看,密闭类的性情最佳,在大超多测验项目中规范——
“直接调用”时能被内联(inline卡塔尔优化,与“硬编码”雷同快,快于派生类。
“接口调用”、“泛型调用接口”时与派生类质量相像,快于结构体的“接口调用”。
唯风华正茂正是在“泛型调用”时,落后于结构体,与派生类大致稍微慢一点。
再不怕诡异的“陆拾一人平台+VS二零一零”难点,密闭类、结构体在一向调用时,还不及派生类。
最终总计一下只怕会被内联优化的调用类型——
三拾位平台+VS二〇〇七:调用密闭类、调用结构体。
32人平台+VS二〇〇九:调用密封类、调用结构体、接口节制泛型调用结构体。
64个人平台+VS二〇〇六:调用封闭类、调用结构体。
64位平台+VS2010:(无)。
(完)
测量试验程序exe——
源代码下载——
目录——
C#类与结构体毕竟哪个人快——各个函数调用情势速度评测:
再探C#类与结构体终究哪个人快——酌量栈变量、栈分配、63个人整数、密闭类:
三探C#类与结构体终归什么人快——MSIL(微软个中语言卡塔 尔(阿拉伯语:قطر解读:
四探C#类与结构体终究哪个人快——跨程序集(assembly卡塔 尔(英语:State of Qatar)调用:
using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
namespace TryPointerCall
{
/// <summary>
/// 指针操作接口
/// </summary>
public interface IPointerCall
{
/// <summary>
/// 指针操作
/// </summary>
/// <param name="p">源指针</param>
/// <returns>修改后指针</returns>
unsafe byte* Ptr(byte* p);
}
#region 非泛型
/// <summary>
/// [非泛型] 指针操作基类
/// </summary>
public abstract class PointerCall : IPointerCall
{
public abstract unsafe byte* Ptr(byte* p);
}
/// <summary>
/// [非泛型] 指针操作派生类: 指针+偏移
/// </summary>
public class PointerCallAdd : PointerCall
{
/// <summary>
/// 偏移值
/// </summary>
public int Offset = 0;
public override unsafe byte* Ptr(byte* p)
{
return unchecked(p + Offset);
}
}
/// <summary>
/// [非泛型] 指针操作结构体: 指针+偏移
/// </summary>
public struct SPointerCallAdd : IPointerCall
{
/// <summary>
/// 偏移值
/// </summary>
public int Offset;
public unsafe byte* Ptr(byte* p)
{
return unchecked(p + Offset);
}
}
#endregion
#region 泛型
// !!! C#不支持将整数类型作为泛型约束 !!!
//public abstract class GenPointerCall<T> : IPointerCall where T: int, long
//{
// public abstract unsafe byte* Ptr(byte* p);
// void d()
// {
// }
//}
#endregion
#region 全部测试
/// <summary>
/// 指针操作的一些常用函数
/// </summary>
public static class PointerCallTool
{
private const int CountLoop = 200000000; // 循环次数
/// <summary>
/// 调用指针操作
/// </summary>
/// <typeparam name="T">具有IPointerCall接口的类型。</typeparam>
/// <param name="ptrcall">调用者</param>
/// <param name="p">源指针</param>
/// <returns>修改后指针</returns>
public static unsafe byte* CallPtr<T>(T ptrcall, byte* p) where T : IPointerCall
{
return ptrcall.Ptr(p);
}
public static unsafe byte* CallClassPtr<T>(T ptrcall, byte* p) where T : PointerCall
{
return ptrcall.Ptr(p);
}
public static unsafe byte* CallRefPtr<T>(ref T ptrcall, byte* p) where T : IPointerCall
{
return ptrcall.Ptr(p);
}
// C#不允许将特定的结构体作为泛型约束。所以对于结构体只能采用上面那个方法,通过IPointerCall接口进行约束,可能会造成性能下降。
//public static unsafe byte* SCallPtr<T>(T ptrcall, byte* p) where T : SPointerCallAdd
//{
// return ptrcall.Ptr(p);
//}
private static int TryIt_Static_Offset;
private static unsafe byte* TryIt_Static_Ptr(byte* p)
{
return unchecked(p + TryIt_Static_Offset);
}
/// <summary>
/// 执行测试 - 静态调用
/// </summary>
/// <param name="sOut">文本输出</param>
private static unsafe void TryIt_Static(StringBuilder sOut)
{
TryIt_Static_Offset = 1;
// == 性能测试 ==
byte* p = null;
Stopwatch sw = new Stopwatch();
int i;
unchecked
{
#region 测试
// 硬编码
sw.Reset();
sw.Start();
for (i = 0; i < CountLoop; ++i)
{
p = p + TryIt_Static_Offset;
}
sw.Stop();
sOut.AppendLine(string.Format("硬编码:t{0}", sw.ElapsedMilliseconds));
// 静态调用
sw.Reset();
sw.Start();
for (i = 0; i < CountLoop; ++i)
{
p = TryIt_Static_Ptr(p);
}
sw.Stop();
sOut.AppendLine(string.Format("静态调用:t{0}", sw.ElapsedMilliseconds));
#endregion // 测试
}
}
/// <summary>
/// 执行测试 - 非泛型
/// </summary>
/// <param name="sOut">文本输出</param>
private static unsafe void TryIt_NoGen(StringBuilder sOut)
{
// 创建
PointerCallAdd pca = new PointerCallAdd();
SPointerCallAdd spca;
pca.Offset = 1;
spca.Offset = 1;
// 转型
PointerCall pca_base = pca;
IPointerCall pca_itf = pca;
IPointerCall spca_itf = spca;
// == 性能测试 ==
byte* p = null;
Stopwatch sw = new Stopwatch();
int i;
unchecked
{
#region 调用
#region 直接调用
// 调用派生类
sw.Reset();
sw.Start();
for (i = 0; i < CountLoop; ++i)
{
p = pca.Ptr(p);
}
sw.Stop();
sOut.AppendLine(string.Format("调用派生类:t{0}", sw.ElapsedMilliseconds));
// 调用结构体
sw.Reset();
sw.Start();
for (i = 0; i < CountLoop; ++i)
{
p = spca.Ptr(p);
}
sw.Stop();
sOut.AppendLine(string.Format("调用结构体:t{0}", sw.ElapsedMilliseconds));
#endregion // 直接调用
#region 间接调用
// 调用基类
sw.Reset();
sw.Start();
for (i = 0; i < CountLoop; ++i)
{
p = pca_base.Ptr(p);
}
sw.Stop();
sOut.AppendLine(string.Format("调用基类:t{0}", sw.ElapsedMilliseconds));
// 调用派生类的接口
sw.Reset();
sw.Start();
for (i = 0; i < CountLoop; ++i)
{
p = pca_itf.Ptr(p);
}
sw.Stop();
sOut.AppendLine(string.Format("调用派生类的接口:t{0}", sw.ElapsedMilliseconds));
// 调用结构体的接口
sw.Reset();
sw.Start();
for (i = 0; i < CountLoop; ++i)
{
p = spca_itf.Ptr(p);
}
sw.Stop();
sOut.AppendLine(string.Format("调用结构体的接口:t{0}", sw.ElapsedMilliseconds));
#endregion // 间接调用
#endregion // 调用
#region 泛型调用
#region 泛型基类约束
// 基类泛型调用派生类
sw.Reset();
sw.Start();
for (i = 0; i < CountLoop; ++i)
{
p = CallClassPtr(pca, p);
}
sw.Stop();
sOut.AppendLine(string.Format("基类泛型调用派生类:t{0}", sw.ElapsedMilliseconds));
// 基类泛型调用基类
sw.Reset();
sw.Start();
for (i = 0; i < CountLoop; ++i)
{
p = CallClassPtr(pca_base, p);
}
sw.Stop();
sOut.AppendLine(string.Format("基类泛型调用基类:t{0}", sw.ElapsedMilliseconds));
#endregion // 泛型基类约束
#region 泛型接口约束 - 直接调用
// 接口泛型调用派生类
sw.Reset();
sw.Start();
for (i = 0; i < CountLoop; ++i)
{
p = CallPtr(pca, p);
}
sw.Stop();
sOut.AppendLine(string.Format("接口泛型调用派生类:t{0}", sw.ElapsedMilliseconds));
// 接口泛型调用结构体
sw.Reset();
sw.Start();
for (i = 0; i < CountLoop; ++i)
{
p = CallPtr(spca, p);
}
sw.Stop();
sOut.AppendLine(string.Format("接口泛型调用结构体:t{0}", sw.ElapsedMilliseconds));
// 接口泛型调用结构体引用
sw.Reset();
sw.Start();
for (i = 0; i < CountLoop; ++i)
{
p = CallRefPtr(ref spca, p);
}
sw.Stop();
sOut.AppendLine(string.Format("接口泛型调用结构体引用:t{0}", sw.ElapsedMilliseconds));
#endregion // 直接调用
#region 间接调用
// 接口泛型调用基类
sw.Reset();
sw.Start();
for (i = 0; i < CountLoop; ++i)
{
p = CallPtr(pca_base, p);
}
sw.Stop();
sOut.AppendLine(string.Format("接口泛型调用基类:t{0}", sw.ElapsedMilliseconds));
// 接口泛型调用派生类的接口
sw.Reset();
sw.Start();
for (i = 0; i < CountLoop; ++i)
{
p = CallPtr(pca_itf, p);
}
sw.Stop();
sOut.AppendLine(string.Format("接口泛型调用派生类的接口:t{0}", sw.ElapsedMilliseconds));
// 接口泛型调用结构体的接口
sw.Reset();
sw.Start();
for (i = 0; i < CountLoop; ++i)
{
p = CallPtr(spca_itf, p);
}
sw.Stop();
sOut.AppendLine(string.Format("接口泛型调用结构体的接口:t{0}", sw.ElapsedMilliseconds));
#endregion // 间接调用
#endregion // 泛型调用
}
}
/// <summary>
/// 执行测试 - 泛型
/// </summary>
/// <param name="sOut">文本输出</param>
private static unsafe void TryIt_Gen(StringBuilder sOut)
{
// !!! C#不支持将整数类型作为泛型约束 !!!
}
/// <summary>
/// 执行测试
/// </summary>
public static string TryIt()
{
StringBuilder sOut = new StringBuilder();
sOut.AppendLine("== PointerCallTool.TryIt() ==");
TryIt_Static(sOut);
TryIt_NoGen(sOut);
TryIt_Gen(sOut);
sOut.AppendLine();
return sOut.ToString();
}
}
#endregion
}
二、测验意况
编译器——
VS2005:Visual Studio 2005 SP1。
VS2010:Visual Studio 2010 SP1。
采取上述编写翻译器编写翻译为Release版程序,最大速度优化。
本文由88必发手机版发布于仪器仪表,转载请注明出处:跨程序集,类与结构体究竟谁快
关键词: