获取C#中方法的执行时间及其代码注入
在优化C#代码或对比某些API的效率时,通常需要测试某个方法的运行时间,可以通过DateTime来统计指定方法的执行时间,也可以使用命名空间System.Diagnostics中封装了高精度计时器QueryPerformanceCounter方法的Stopwatch类来统计指定方法的执行时间:
1.使用DateTime方法:
DateTime dateTime = DateTime.Now;
MyFunc();
Console.WriteLine((DateTime.Now - dateTime).TotalMilliseconds);
2.使用Stopwatch方式:
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
MyFunc();
stopwatch.Stop();
Console.WriteLine(stopwatch.ElapsedMilliseconds); //本次MyFunc()方法的运行毫秒数
//重置计时器
stopwatch.Restart(); //此处可以使用stopwatch.Reset(); stopwatch.Start();组合代替
MyFunc();
stopwatch.Stop();
Console.WriteLine(stopwatch.ElapsedMilliseconds); //本次MyFunc()方法的运行毫秒数
以上两种办法都可以达到获取方法执行时间的目的,但是在需要对整个项目中的方法都进行监测用时时,除了使用性能分析工具,我们还可以通过代码注入的方式给程序集中每一个方法加入计时器;
通过命名空间System.Reflection.Emit中的类可以动态的创建程序集、类型和成员,通常类库Mono.Cecil可以动态读取并修改已经生成的IL文件,这种在不修改源代码的情况下给程序集动态添加功能的技术称为面向切面编程(AOP);
这里给出了一个注入使用Stopwatch来检测方法执行时间的代码,这里的Mono.Cecil类库可以通过nuget进行安装:
using System;
using System.IO;
using System.Linq;
using System.Diagnostics;
using Mono.Cecil;
using Mono.Cecil.Cil;
using Mono.Collections.Generic;
static void Main(string[] args)
{
for (int i = ; i < args.Length; i++)
{
FileStream fileStream = new FileStream(args[i], FileMode.Open);
if (fileStream != null)
{
AssemblyDefinition aD = AssemblyDefinition.ReadAssembly(fileStream);
ModuleDefinition mD = aD.MainModule;
Collection<TypeDefinition> typeDefinition = mD.Types;
foreach (TypeDefinition type in typeDefinition)
{
if (type.IsClass)
{
foreach (MethodDefinition method in type.Methods)
{
if (method.IsPublic && !method.IsConstructor)
{
ILProcessor il = method.Body.GetILProcessor();
TypeReference stT = mD.ImportReference(typeof(Stopwatch));
VariableDefinition stV = new VariableDefinition(stT);
method.Body.Variables.Add(stV);
Instruction first = method.Body.Instructions.First();
il.InsertBefore(first, il.Create(OpCodes.Newobj,
mD.ImportReference(typeof(Stopwatch).GetConstructor(new Type[] { }))));
il.InsertBefore(first, il.Create(OpCodes.Stloc_S, stV));
il.InsertBefore(first, il.Create(OpCodes.Ldloc_S, stV));
il.InsertBefore(first, il.Create(OpCodes.Callvirt,
mD.ImportReference(typeof(Stopwatch).GetMethod("Start")))); Instruction @return = method.Body.Instructions.Last();
il.InsertBefore(@return, il.Create(OpCodes.Ldloc_S, stV));
il.InsertBefore(@return, il.Create(OpCodes.Callvirt,
mD.ImportReference(typeof(Stopwatch).GetMethod("Stop")))); il.InsertBefore(@return, il.Create(OpCodes.Ldstr, $"{method.FullName} run time: "));
il.InsertBefore(@return, il.Create(OpCodes.Ldloc_S, stV));
il.InsertBefore(@return, il.Create(OpCodes.Callvirt,
mD.ImportReference(typeof(Stopwatch).GetMethod("get_ElapsedMilliseconds"))));
il.InsertBefore(@return, il.Create(OpCodes.Box, mD.ImportReference(typeof(long))));
il.InsertBefore(@return, il.Create(OpCodes.Call,
mD.ImportReference(typeof(string).GetMethod("Concat", new Type[] { typeof(object), typeof(object) }))));
il.InsertBefore(@return, il.Create(OpCodes.Call,
mD.ImportReference(typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }))));
}
}
}
}
FileInfo fileInfo = new FileInfo(args[i]);
string fileName = fileInfo.Name;
int pointIndex = fileName.LastIndexOf('.');
string frontName = fileName.Substring(, pointIndex);
string backName = fileName.Substring(pointIndex, fileName.Length - pointIndex);
string writeFilePath = Path.Combine(fileInfo.Directory.FullName, frontName + "_inject" + backName);
aD.Write(writeFilePath);
Console.WriteLine($"Success! Output path: {writeFilePath}");
fileStream.Dispose();
}
}
Console.Read();
}
完整的项目传到了Github上=>InjectionStopwatchCode,下载项目后,通过dotnet build命令即可编译出可执行程序,将目标程序集文件拖入到该应用程序即可在程序集目录导出注入代码后的程序集文件,经过测试,包括方法拥有返回值和方法的参数列表中包含out和ref参数等情况都不会对运行结果产生影响;
示例:
using System; public class MyClass
{
public void MyFunc()
{
int num = ;
for (int i = ; i < int.MaxValue; i++)
{
num++;
}
}
}
public class Program
{
public static void Main(string[] args)
{
MyClass myObj = new MyClass();
myObj.MyFunc();
Console.Read();
}
}
原始IL代码:
代码注入后IL代码:
代码注入后运行结果:
如果您觉得阅读本文对您有帮助,请点一下“推荐”按钮,您的认可是我写作的最大动力!
作者:Minotauros
出处:https://www.cnblogs.com/minotauros/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
获取C#中方法的执行时间及其代码注入的更多相关文章
- 详解C#泛型(二) 获取C#中方法的执行时间及其代码注入 详解C#泛型(一) 详解C#委托和事件(二) 详解C#特性和反射(四) 记一次.net core调用SOAP接口遇到的问题 C# WebRequest.Create 锚点“#”字符问题 根据内容来产生一个二维码
详解C#泛型(二) 一.自定义泛型方法(Generic Method),将类型参数用作参数列表或返回值的类型: void MyFunc<T>() //声明具有一个类型参数的泛型方法 { ...
- Java学习笔记(七)——获取类中方法的信息,java的LinkedList
[前面的话] 在实际项目中学习知识总是最快和最有效的,既能够较好的掌握知识,又能够做出点东西,还是简单的知识总结,最近一直在总结笔记,写的东西还是比较水,希望慢慢可以写出一些干货. 学习过程中的小知识 ...
- Instruments10 分析某个类中方法的执行时间
此步骤也可用户内存分配.内存泄漏的检测 最新操作步骤参考: https://www.jianshu.com/p/e499ce63ed72
- CodeIgniter(3.1.4)框架中添加执行时间统计代码
CodeIgniter(3.1.4)框架中添加,执行时间统计代码: system/core/CodeIgniter.php最后行处. /* * ---------------------------- ...
- js 获取iframe中的元素
今天要修改编辑器插件中的元素遇到的问题 jquery 在父窗口中获取iframe中的元素 1.Js代码 格式:$("#iframe的ID").contents().find(&qu ...
- 深入理解xLua基于IL代码注入的热更新原理
目前大部分手游都会采用热更新来解决应用商店审核周期长,无法满足快节奏迭代的问题.另外热更新能够有效降低版本升级所需的资源大小,节省玩家的时间和流量,这也使其成为移动游戏的主流更新方式之一. 热更新可以 ...
- js获取输入框中当前光标位置并在此位置插入字符串的方法(angularjs+ts)
一半是参照别人代码,一半是自己代码,略笨拙,如果有更好的方法希望分享. 获取当前光标位置的方法 getCaretPosition (obj:any) { //获取输入框中当前光标的位置,obj为此输入 ...
- JS获取URL中参数值(QueryString)的4种方法分享<转>
方法一:正则法 复制代码代码如下: function getQueryString(name) { var reg = new RegExp('(^|&)' + name + '=([^ ...
- 009. C#中的WebBrowser控件的属性、方法及操作演示代码(转)
本文转自 http://www.open-open.com/code/view/1430559996802 0.常用方法 Navigate(string urlString):浏览urlString表 ...
随机推荐
- ubuntu输入法
一直都是使用搜狗的,但是感觉一直都不兼容.经常输入一串文字导致输入法崩溃 比如 ‘外显’waixian就崩了. 而且会出现fcitx 100%cpu占用的情况 pkill fcitx &&am ...
- yii框架 隐藏index.php 以及美化URL(pathinfo模式访问)
首先我们分步骤来: 安装好 yii 以后 我们看到的url地址如下所示: http://www.3w.com/MyApp/backend/web/index.php?r=site%2Flogin 我 ...
- Netsharp总体介绍
作者:秋时 日期:2014年02月05日 转载须说明出处 Netsharp交流群:338963050(请有详细的请求说明) Netsharp系列文章目录结构 Netsharp是一款免费的基于 ...
- pyhton 核心编程 正则表达式习题
方案一 import re #1. 识别下列字符串:“bat,” “bit,” “but,” “hat,” “hit,” 或 “hut” import re def test1(self): bt = ...
- 渗透测试的理论部分2——OSSTMM的详细描述
昨天休息了一天,今天我要连更两篇博客,作为补充,以下为正文 本章详细描述了OSSTMM内的RAV得分这一理论概念,对日后从事正规安全工作至关重要 OSSTMM为开源安全测试方法论,对OSSTMM不了解 ...
- centos7图形界面
安装centOS7服务器版本,系统默认是不会安装GUI的图形界面程序,这个需要手动安装CentOS7 Gnome GUI包. 安装GUI包.开机自动启动图形界面.重启 # yum groupinsta ...
- .NET Core微服务之路:基于Consul最少集群实现服务的注册与发现(二)
重温Consul最少化集群的搭建
- Android开发 - 掌握ConstraintLayout(五)偏差(Bias)
比如实现这样一个场景: "在屏幕宽度的1/4的地方放置一个View" 使用传统布局时,实现按照屏幕的宽度(高度),或者相对两个View之间距离的一个比例来进行布局,就显得非常麻烦, ...
- Error: [ng:areq] Argument ‘AppCtrl’ is not a function, got undefined
今天把用ionic做一个案例,和ionic示例项目差不多,只是用requirejs分离了controller,但是一直报错 Error: [ng:areq] Argument ‘AppCtrl’ is ...
- Linux学习笔记《六》