在上司面前硬不起来?教你如何快速将字符串转换为可执行代码
???老是因為活不好被上司欺凌?
? ? 在上司面前很沒面子?
? ? 在上司面前硬不起來??
? ??是時候分享一個可以快速將字符串轉換為可執行代碼的項目給你了 -?YACEP?!
? ? 不過,這不是一篇專門對YACEP?做詳細介紹的隨筆,想知道更詳細的的YACEP?細節,請去GitHub上觀摩。?//這特么都啥離奇思路???
? ? 這篇隨筆的核心是介紹一下YACEP所用到的一些技術,工具,服務和技巧,鑒于篇幅原因,不可能面面俱到,只能點到為止,目錄如下:
?? ?
? ? 目錄:
? ? ? ? ? ??1.?YACEP簡介
? ? ? ? ? ? 2.?技術篇
? ? ? ? ? ? ? ?2.1?利用優先爬山算法解決運算符優先級的問題
? ? ? ? ? ? ? ?2.2?利用ReadOnlySpan加速字符串解析
? ? ? ? ? ? ? ?2.3?利用表達式樹和Emit生成表達式執行代理
? ? ? ? ? ? ? ?3.?工具篇(上)
? ? ? ? ? ? ? ? 3.1?測試覆蓋率工具 - Coverlet?
? ? ? ? ? ? ? ? 3.2?覆蓋率報表轉換工具 -?ReportGenerator?
? ? ? ? ? ? ? ? 3.3?基準測試工具 - BenchmarkDotNet
? ? ? ? ? ?4.?服務篇(下)
? ? ? ? ? ? ? ? 4.1??測試覆蓋率服務 -?Codecov
? ? ? ? ? ? ? ? 4.2?代碼質量分析服務 -?SonarCloud
? ? ? ? ? ? ? ? 4.3 持續集成服務1 -?Travis CI
? ? ? ? ? ? ? ??4.4 持續集成服務2 -?AppVeyor
? ? ? ? ? ? ? ? 4.5?持續集成服務3 -?Azure DevOps
? ? ? ? ? ?5. 技巧篇(下)
? ? ? ? ? ? ? ?5.1 利用props文件抽離csproj的公共配置
? ? ? ? ? ? ? ?5.2 利用WSL跨平臺測試代碼
? ? ? ? ? ? ? ?5.3 利用持續集成服務檢查PR
? ? ? ? ? ? ? ?5.4 更易寫的文檔格式 - AsciiDoc
? ? ? ? ? ? ? ?5.5 如何給你的項目添加更多的徽章
? ? ? ? ? ? ? ?5.6 利用git?message自動發布NuGet包
?
1.?YACEP??簡介
? ? ?YACEP?: yet another csharp expression parser,是一款基于netstandard2.0構建的輕量級高性能表達式解析器。能夠將一段有效的字符串并轉換成一棵抽象語法樹,同時可以把抽象語法樹轉換成一段可以執行的代碼。
? ? ? 項目使用MIT開源協議,代碼托管在GitHub上,更多更詳細的信息可以去看官方文檔,隨便給個star什么的就再好不過了:)
? ? ??YACEP??的核心是一個輕量級的表達式解析器,其解析代碼不到500行,算上輔助的一些操作,整個解析器代碼不到600行。解析器內部使用了??ReadOnlySpan?來處理字符串,所以在做長串處理時,內存消耗可以做到很低,處理起來也非常快。
? ? ?YACEP??還附加實現了一個簡單編譯器,可以將抽象語法樹轉換成可執行代碼。編譯器的接口申明了兩個方法,一個不帶泛型參數,一個帶了泛型參數。所以在?YACEP??的內部編譯器的實現有兩個,第一個是不做運行時類型綁定的實現,一個是限定運行時類型綁定的實現。前者有更好的靈活性,有類似Python這種語言的動態能力,所以無法在編譯完成時生成更優化的IL指令,性能一般。而后者是在編譯時就已經限定了具體的類型,所以能夠生成更短的IL指令,性能相對于第一種有非常大的提升。
1 ?public interface ICompiler
2 {3 ///不做運行時類型綁定
4 IEvaluator Compile(EvaluableExpression expression);
5 ///做運行時類型綁定
6 IEvaluator<TState> Compile<TState>(EvaluableExpression expression);
7 }
? ? ??YACEP??具體的其它特性可以去?GitHub?上查看,這里就直接從官方復制過來,如下:
開箱即用,內置了的字面值, 一元及二元操作符以及統計類與時間類函數可滿足大部分使用場景
跨平臺,基于netstandard2.0標準構建
輕量級,只有500多行代碼實現的輕量級詞法分析器
低消耗,詞法分析器使用?ReadOnlySpan?解析字符串
高性能,使用EMIT技術生成IL來構建可執行對象(查看基準測試報告)
支持條件表達式
支持索引器
支持 in 表達式
支持自定義字面量
支持自定義一元操作符
支持自定義二元操作符
支持自定義函數
?
2.技術篇
2.1?利用優先爬山算法解決運算符優先級的問題
是一種深度優先的算法,它采用啟發式的方式進行局部擇優。現在的很多人工智能技術也有用到此算法。(后簡稱爬山算法)
? ? ? ? ? 更詳細的介紹請點擊此鏈接:https://en.wikibooks.org/wiki/Algorithms/Hill_Climbing
? ? ? ? ? 當前有很多種算法可以解決運算符優先級的問題,比如調度場算法,遞歸下降算法,移進歸約算法等。
? ? ? ? ? 那為什么YACEP?會選用優先爬山算法呢?
? ? ? ? ? 因為爬山算法是我看過眾多的算法中,理解起來是最快的。下面講解一下這個算法的大致思路。
? ? ? ? ? 在開始之前我需要先回憶一下小學學到的數學知識,四則運算法則和結合律。
? ? ? ? ? 四則運算法則告訴我們當一個式子有加減乘除的時候,先算乘除后算加減,這個是一個原則,很好理解,照著做就成。
? ? ? ? ??結合律就稍復雜一點了,結合律是說在一個包含有二個以上的可結合運算子的表示式,只要算子的位置沒有改變,其運算的順序就不會對運算出來的值有影響。看不懂是吧,換一種說法就是:
( a + b ) + c = a + ( b + c )? ?? ? ? ?a, b的和 再與c求和的值 一定 等于 a與? b, c的和? 求和的值。請務必按照這個斷句讀,否則有點拗口。
? ? ? ?如果讀完還不知道啥意思,可以回小學去捶一頓你們的體育老師了。
? ? ? ?好了,有了上面的知識儲備,我們開始研究如下數學表達式:
a + b * c * e + d? ? ? ? ? ??按照四則運算法則: 先乘除后加減,對于上述表達式,我們標記一下運算的優先級
? ? ? ? ?再按照結合律,對于更高優先級的乘法,在求 a*b*c 的值的時候,我們無所謂先算 a*b 再用這個結果去乘c,還是先算b*c再去乘a。按照當前主流的文字閱讀規則,我們選擇從左往右,即先算b*c,然后拿到這個結果再乘d。我們將b*c的結果存儲為m,于是上述表達式可以簡化為:
// m = b * ca + m * d + e
? ? ? ?再繼續,我們將m*d的結果存儲為n,于是上述表達式再次簡化為:
// m = b * c// n = m * d
a + n + e
? ? ? ? ?再次按照結合律,我們將a+n的結果存儲為x,?于是上述表達式還可簡化為:
// m = b * c// n = m * d
// x = a + n
x + e
? ? ? ??到這一步只要求x+e的結果就好了,把前面的步驟合并在一起。
-----
m
---------
n
---------------
x
----------------------
? ? ? 去掉字符,我們把下面的虛線連接起來,大概是這樣:
? ? ? 山頂
/ \
/ --------- \
/ \
/ ------------- \
/ \
/ ----------------- \
? ? ??這就是傳說中的爬山算法!!!
? ? ?是不是超好懂!!!
? ? ?10秒鐘讀懂了爬山算法!!!? ? ?
? ? ? 其實怎么說呢,理解到這一步基本就離理解完整的爬山算法差的沒幾百步了。總共也就幾百步,你再走幾百步就完全理解了,加油,我們繼續。
? ? ? 按照上述的步驟,我們現在開始理解算法的流程。我們將表達式中的元素分為兩類,一類我們叫原子值,比如上面表達式中的a、b、c、d、e,另一類我們叫他們為運算符,比如+和*。對于帶括號的表達式,我們可以稱其為子表達式,也是一種表達式,所以我們總是可以將任何一個表達式拆解為只包含原子值或運算符的表達式(對于一個不包含運算符的表達式,直接拿值就完了)。
? ? ? 基于上述的分類,我們開始描述一下爬山算法的運算步驟:
? ? ? ? ? ? ? ?1. 讀取當前一個原子值和它最鄰近的運算符
? ? ? ? ? ? ? ?2.?讀取下一個原子值和它最鄰近的運算符
? ? ? ? ? ? ? ?3. 如果步驟1中的運算符的優先級大于步驟2中的優先級,則算法返回當前原子值,當前原子值最鄰近運算符與下一個原子值的子表達式
? ? ? ? ? ? ? ?4. 否則,從步驟2開始構建新的子表達式繼續按照上述步驟處理
? ? ? 回到表達式:
a + b * c * d + e? ? ? 現在按照爬山算法開始處理這個表達式:
?讀到原子值a以及優先級為1的運算符+,?a +?
?讀取下一個原子值b以及優先級為2的運算符*,?b *?
?優先級2大于1,所以我們開始從b開始構建子表達式?
?讀取下一個原子值c以及優先級為2的運算符*,?c *?
?當前的子表達式的??b *?優先級是2(,新讀到???c *?的優先級也是2,得到新表達式?b * c *?, 優先級是2。上面的步驟的值??m *?
?讀取下一個表達式??d +?, +的優先級是1,所以整個子表達式返回值為??m * d?,這兩步需要理解結合律。上面的值??n +??
?子表達式已經處理完成,退出子表達式的處理流程,當前的表達式為的優先級為步驟1中,?a +
?下一個表達式為?n +??,兩者的優先級都是1,返回子串??a + n?上面的值?x +?
?下一個表達式為??e??,直接返回
?得到最終表達式??x + e?
? ? ? ?回到之前我們得到的那座山,再看看這個步驟,你會發現這個流程還真的就是在爬這座山 。
? ? ? 山頂
/ \
/ --------- \
/ \
/ ------------- \
/ \
/ ----------------- \
2.2?利用ReadOnlySpan加速字符串解析
? ? ? ? ?C#中String對象是一個只讀對象,一旦創建將不可更改。所以C#中對String對象做更改的方法底層都會創建一個新的String對象。比如在如下代碼中:
1 unsafe2 {
3 var random = new Random();
4 var str = "";
5 for (int i = 0; i <= 5; i++)
6 {
7 str += Convert.ToChar(random.Next('A', 'Z'));
8 fixed (char* p = str)
9 Console.WriteLine((int)p);
10 }
11 Console.WriteLine(str);
12 }
? ? ? ??上述的代碼是在做字符串修改,你可能會覺得這種修改返回一個新值沒問題。
? ? ? 但是下面的這種情況對于解析器來說就是一種致命傷了。在截取字符串時,你會發現每一次值都是不一樣的,縱使你截取的位置是相同的,Substring始終如一的返回一個新對象給你。
1 unsafe2 {
3 var str = "123456";
4 fixed (char* p = str)
5 Console.WriteLine((int)p);
6 fixed (char* p = str.Substring(1, 2))
7 Console.WriteLine((int)p);
8 fixed (char* p = str.Substring(1, 2))
9 Console.WriteLine((int)p);
10 Console.WriteLine(str);
11 }
? ? ? ? ??對解析過程而言,可能會有頻繁截串的場景,比如隨時都可能要將表達式中的一段數字轉換為一個數值。這種情況,每次都返回一個新的字符串對象,無論性能還是內存都是難以接受的。
? ? ? ?你可能有想到C#中的StringBuilder對象,它確實是維護一個緩沖區,可以在做字符串修改的時候保證始終如一的使用同一塊地址,但是這玩意是用來構建字符串的,讀取字符串這貨不行的,所以你看官方連個Substring都不給你。
? ? ? 難道必須使用非托管代碼了么?為了保證更快的內存讀取以及更低的內存消耗,難道我要去PInvoke???
? ? ? ? ? ?
? ? ? 這種問題,微軟的碼農肯定已經意識到了,不然這部分的隨筆。。。我怎么寫下去。
? ? ? 微軟提供了System.Memory程序集用來幫助我們更方便也更安全的操作內存。
? ? ? 我們可以使用ReadOnlySpan來解決上述問題。
? ? ??ReadOnlySpan在程序集System.Memory中,是Span的只讀表示。將字符串轉換為一個ReadOnlySpan對象,接著使用ReadOnlySpan來處理字符串,那么上述的問題都可以被解決。
? ? ? 然后大致說一下Span,Span可以用于表達任何一段連續內存空間,無論是數組,非托管指針,可獲取到指針值的托管內存等等等等(是不是回憶起當初被指針支配的恐懼感),其實在它內部的實現就是一個指針。相對于C/C++里面的指針需要各種小心翼翼,不敢有一絲怠慢忘記釋放,或訪問到離奇的地址,或因為各種原因變成野指針。Span會在內部維護這個指針的地址,在做指針運算時,會做邊界檢查,在更新引用時,垃圾回收器也能判斷出該如何回收內存。
? ? ? 對于Span的解讀,推薦閱讀下面這個系列,作者的解讀非常贊。現在愿意寫博文講清 What、How 和 Why的博主不多了,且讀且珍惜。
? ? ??https://www.cnblogs.com/justmine/p/10006621.html
2.3?利用表達式樹和Emit生成表達式執行代理
? ? ?動態生成代理是一個古老的話題。最開始是因為大家都覺得.NET自帶的那個反射操作太慢,怎么說呢,其實對于大部分場景是夠用的,某知名大佬說:
每每看人在談論代碼時,都說那反射操作是極慢的,萬萬不可取。
在我自己,卻認為反射之慢不過毫秒。
用不正當的思路寫出的代碼才會引起真正的慢。
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?-- 樹人Groot
? ? ? ? ?
? ? ? 之所以會成為一個古老的話題,是因為動態生成代理會出現在太多的業務場景中。
? ? ? 最常見的就是快速獲取一個對象指定名稱的成員值,你會看到各色愛寫庫愛造輪子的大佬非常熱衷去搞的個快速對象訪問器什么的。
? ? ? 多年前博客園大佬趙姐夫還參與過此事寫過一個庫,地址如下:
? ? ? ? ? ??http://blog.zhaojie.me/2009/02/fast-reflection-library.html
? ? ??注:雖然老趙已經很久沒有更新博客了,但是他的博客還是非常推薦去閱讀一下,內容很豐富,干貨特別多。
? ? ? ? ? 博客地址:http://blog.zhaojie.me
? ? ? 好了,回過頭來。現在對使用動態生成代理的場景做一個匯總,常出現的場景如下:
?對象序列化反序列化,這種場景多出現于RPC中,代理要把stream轉換為object
?實現ORM,代理要把reader轉換為object(這種其實也是rpc)
?實現AOP,代理要為具體類生成一個包含一系列切面函數的類
?綁定求值,比如模板渲染或YACEP這種對編譯結果調用執行獲取結果的過程
? ? ? ? 動態生成代理實現方式有如下四種:
?利用Expression構造代理方法
?DynamicMethod生成動態函數構造代理方法
?DynamicAssembly構建代理類
?利用CodeDom動態生成程序集,生成代理
? ? ? ?下面大致對上面的四種方式做一個比較
| Expression | DynamicMethod | DynamicAssembly | CodeDom | |
| 優點 | 實現的代碼簡單易讀 | 原生支持繞過CLR訪問修飾符檢查 | 支持生成類型 | 支持生成類型 |
| 缺點 | 不支持生成類型 | 需要對IL指令有一定了解 | 需要對IL指令有一定了解 | 代碼臃腫 |
| 適用場景 | 邏輯稍簡單的代理 | 邏輯稍簡單的代理 | 功能更完備的代理 | 處理模板化的代碼 |
? ? ? ? ???YACEP在定義可執行對象時,?沒有使用.NET內置的委托,而是定義了兩個接口。
public interface IEvaluator{
object Evaluate(object state);
}
public interface IEvaluator<in TState>
{
object Evaluate(TState state);
}?
? ? ? ? ? 那為什么使用接口而不是用委托來定義可執行對象,委托才更符合對可執行對象表述的直覺啊 ?
? ? ? ? ? 這是因為YACEP需要支持自定義字面量,自定義函數。
? ? ? ? ? 如果生成的是委托,最佳的做法是生成閉包函數,在閉包中保存這些自定義字面量和函數,用EMIT生成一個返回閉包函數的函數。
? ? ? ? ? 這種做法確實可以做到,問題是寫出來的EMIT代碼會更多,更難調試和定位錯誤,如何知道是函數的閉包問題還是函數自身問題呢。
? ? ? ? ? 那如果不用EMIT的方式生成委托,還可以使用表達式樹。是的,表達式樹在這個方面處理起來比EMIT更有優勢,代碼可讀性更好。而且使用表達式樹還有個巨大的優勢,YACEP編譯的本質其實是將YACEP自定義的抽象語法樹轉換為C#抽象語法樹,表達式樹所定義的抽象語法樹幾乎和YACEP自定義的抽象語法樹是一比一的,這種轉換要比生成IL更簡單。
? ? ? ? ? 那為什么YACEP不使用表達式樹呢?
? ? ? ? ? 在實現YACEP的編譯過程時,我會先腦補出抽象語法樹轉換出的IL代碼,然后是最終的C#代碼。如何檢驗YACEP生成的結果與我腦補的結果是一致呢?
? ? ? ? ? 如果使用表達式樹,我需要debug看表達式樹的樹結構。如果時間允許,我倒是十分愿意去做這樣的事情,可惜YACEP只是我換工作間隙拿來練手的小玩意。EMIT支持生成動態程序集,然后再用ILSpy去檢查生成IL代碼是否符合我預期,讀生成的DLL代碼可是比去看表達式樹的樹結構來的簡單。
? ? ? ? ?所以最終在YACEP的代碼里,你可以看到即有表達式樹的代碼也有EMIT的代碼。按照上表的適用場景,表達式樹用于在處理按照給你名稱獲取或設置對象成員值,EMIT為表達式生成最終的執行代理。
??
3.?工具篇
3.1?測試覆蓋率工具 - Coverlet?
? ? ? ? ? ? 測試覆蓋率是啥就不解釋了。YACEP使用xUnit.net做單元測試,在當前以及未來可能的版本中,YACEP始終要求100%的行覆蓋率,99%以上的分支覆蓋率。
? ? ? ? ? ? 如何做測試覆蓋率統計?
? ? ? ? ? ? 目前?Visual Studio是支持查看測試覆蓋率的,如果安裝了JetBrains的dotCover插件,可以獲得更好的體驗(要錢的)。
? ? ? ? ? ? 這些是用來看測試覆蓋率的,如何搞到測試覆蓋率報表呢?
? ? ? ? ? ? dotnet在跑測試時,可以通過配置數據收集器來生成測試覆蓋率報告(更詳細配置文檔),示例命令如下:
dotnet test --collect:"Code Coverage"? ? ? ? ? ?在項目中運行此命令,就會生成一個*.coverage的文件在TestResults文件夾里面。
? ? ? ? ? ?噠噠噠噠!我來打開這個文件,看看我的覆蓋率是不是已經100%了呢!
? ? ? ? ? ?二進制的,還是專屬文件格式???
? ? ? ? ? ?沒事,興許在命令的執行結果里面能找到覆蓋率的值!
Microsoft (R) Test Execution Command Line Tool Version 16.0.1Copyright (c) Microsoft Corporation. All rights reserved.
Starting test execution, please wait...
Total tests: 80. Passed: 80. Failed: 0. Skipped: 0.
Test Run Successful.
Test execution time: 4.4614 Seconds
? ? ? ? ? 嗯!
? ? ? ? ? 報告很簡潔嘛!
? ? ? ? ? 我想要的覆蓋率呢,到底是個啥數啊!!!
? ? ? ? ??二進制文件非得軟件開,我特么VSCode用戶啊!!!
? ? ? ? ?命令行執行過程就說跑了多少測試 ,我特么寫了多少測試我自己不清楚么!!!
? ? ? ?那有沒有啥工具可以生成不需要特定軟件才能打開的覆蓋率報告,還能在我跑test命令的時候直白的告訴我到底覆蓋了多少代碼呢?
? ? ? 要是還能支持一下VSCode就更好了!!!
? ? ? 沒有的,別想了,乖乖回去寫代碼,不要有非分之想!
? ? ? 非分之想是個好東西!
? ? ? 你都不非分一下,咋知道是不是真的沒有!
? ? ??Coverlet -?https://github.com/tonerdo/coverlet
? ? ? 官方介紹是這么寫的!
Coverlet is a cross platform code coverage library for .NET Core, with support for line, branch and method coverage.
? ? ? 好像可以嘗試一下的樣子啊!
? ? ? 安裝.net core global tool
dotnet tool install --global coverlet.console? ? ? 添加依賴到測試項目中
dotnet add package coverlet.msbuild? ? ? 帶著漠視整個世界的感情開始執行測試
dotnet test ./tests/TupacAmaru.Yacep.Test/TupacAmaru.Yacep.Test.csproj ^/p:CollectCoverage=true ^
/p:Exclude=\"[xunit.*]*,[TupacAmaru.Yacep.Test*]*\"^
/p:CoverletOutputFormat=\"lcov,opencover\" ^
/p:CoverletOutput=./../../results/coverage/
? ? ? 看輸出,好像比之前的輸出多出了一些不得了的東西啊
Calculating coverage result...Generating report '.\..\..\results\coverage\coverage.info'
Generating report '.\..\..\results\coverage\coverage.opencover.xml'
+------------------+------+--------+--------+
| Module | Line | Branch | Method |
+------------------+------+--------+--------+
| TupacAmaru.Yacep | 100% | 98.9% | 100% |
+------------------+------+--------+--------+
+---------+------+--------+--------+
| | Line | Branch | Method |
+---------+------+--------+--------+
| Total | 100% | 98.9% | 100% |
+---------+------+--------+--------+
| Average | 100% | 98.9% | 100% |
+---------+------+--------+--------+
? ? ? 行覆蓋率,分支覆蓋率,方法覆蓋率一目了然,使用還簡單,無需做任何代碼修改,只需要引用一下,再跑一下.net core global tool就好了!
? ? ?
? ? ?那你以為這個工具的能力就到此為止了嗎?
? ? ?沒有的!
? ? ?VSCode有個擴展:Coverage Gutters,https://marketplace.visualstudio.com/items?itemName=ryanluker.vscode-coverage-gutters
? ? ?它支持顯示全部的語言的代碼覆蓋情況,只要你能給它一個?lcov格式的文件。
? ? ?Coverlet?是支持生成lcov格式的文件的,這樣你就可以在跑完測試去看你的代碼到底哪些地方沒有被覆蓋了。
? ? ?簡直是我等屌絲程序員的福音有沒有,感謝耶穌大佬,感謝釋迦摩尼先生。
3.2?覆蓋率報表轉換工具 -?ReportGenerator
? ? ?上面的Coverlet配合Coverage Gutters基本可以解決開發過程中98%的代碼覆蓋問題。
? ? ?那還有2%呢?
? ? ?是很多時候我們需要能夠更直觀看覆蓋率的情況,我們需要對的人類更易讀的報表。
? ? ?我們不太可能任何時候都打開VSCode找到Icov文件,再找到源碼逐個去看具體代碼到底有沒有被覆蓋。
? ? ?我們需要一種能夠脫離開發環境,脫離源碼去查看覆蓋率的報表。
? ? ?最佳的文件格式是什么呢?
? ? ?當然是HTML!
? ? ?發送HTML的報告給其他人,告訴他,二貨!快,打開你的瀏覽器,看,我的覆蓋率,已經100%!
? ? ?是100%哦!? ? ??
? ? ?
? ? 但是一想到還不知道有沒有工具能夠支撐我們裝逼的內心,就會對著這個世界,悵然若失,淡淡的開始感嘆,人間不值得。
? ? 所幸,人間是值得的!!!
? ??ReportGenerator -?https://github.com/danielpalme/ReportGenerator
? ?官方介紹是這么寫的!
?
ReportGenerator?converts coverage reports generated by OpenCover, dotCover, Visual Studio, NCover, Cobertura, JaCoCo or Clover into human readable reports in various formats.
?
The reports do not only show the coverage quota, but also include the source code and visualize which lines have been covered.
?
ReportGenerator?supports merging several reports into one.
?
?
? ? 恩!給力!
? ? 再往下看!!!
? ? 竟然提供了.net core global tool。
? ? 安裝.net core global tool,搞起!!!
dotnet tool install -g dotnet-reportgenerator-globaltool? ? 生成HTML格式的報表!
reportgenerator "-reports:results/coverage/coverage.opencover.xml" "-targetdir:./results/coverage/reports"?瀏覽器打開生成的index.html文件
? ? ?
? ? ?現在做工具的都好良心!
? ? ?操作簡單,功能強大,最關鍵的還不要錢。
? ? ?感謝耶穌大佬,感謝釋迦摩尼先生。
3.3?基準測試工具 - BenchmarkDotNet
? ? ??但凡喜歡造輪子(我已經潛意識的把我排除在外了:))的都喜歡一個詞 -?高性能,為了能夠證明自己的輪子性能很贊,出現了各色各樣的性能測試工具。
? ? ?個人推薦?BenchmarkDotNet -?https://benchmarkdotnet.org/
? ? ?這次就不貼官方介紹了,太長了,有興趣的可以自己去看,英文的!
? ? ?這里我把官方的說法做一個簡單的翻譯,大致是這么個意思。
? ? ?老郭:搞基準測試不簡單啊!
? ? ?老于:咋?您老是搞出啥問題了?
? ? ?老郭:您看啊,這要考慮咋設計執行迭代次數?
? ? ?老于:對,是這理兒!
? ? ?老郭:還要考慮咋做執行預熱?
? ? ?老于:對,是這理兒!
? ? ?老郭:還要考慮咋樣支持不同平臺?
? ? ?老于:對,是這理兒!
? ? ?老郭:人間不值得啊!
? ? ?老于:啊? 剛剛人家和尚還說人間是值得的,還感謝耶穌大佬,感謝釋迦摩尼先生呢!
? ? ?老郭:那您?是有好招?
? ? ?老于:對的,推薦一個小玩意給您得了!
? ? ?老郭:甭管啥東西?得!放著好用就成,您給我掰哧掰哧!
? ? ?老于:知道金坷垃么?
?? ? 老郭:金坷垃?
? ? ?老于:哦,不?是BenchmarkDotNet!
?? ? 老郭:那是嘛玩意?
? ? ?老于:您啊,剛說的那些問題BenchmarkDotNet都能解決!比如說這設計迭代執行次數的問題,人家這庫自動幫您選,都不帶要您動手的!
?? ? 老郭:這勁兒勁兒的!
? ? ?老于:庫那邊,引用一下,給您的類或方法打幾個特性標記一下,代碼就搞完了!
?? ? 老郭:這勁兒勁兒的!
? ? ?老于:想測試不一樣的平臺?什么.netfx,.netcore,corert和mono啊,統統支持!
? ? ?老郭:這勁兒勁兒的!
? ? ?老于:想測試不一樣的處理器架構?x86支持!x64支持!
? ? ?老郭:這勁兒勁兒的,勁兒勁兒的!
? ? ?老于:想測試不一樣的JIT版本?LegacyJIT支持!RyuJIT支持!
? ? ?老郭:這倍兒勁兒啊!
? ? ?老于:GC標記不一樣也想測?服務器,工作站都支持!除此以外,還支持各種參數搞出不一樣的結果!
?? ? 老郭:我算是聽明白了,是個夠勁兒的玩意,那它支持生成啥樣的報告呢?
? ? ?老于:GitHub的文檔格式,StackOverflow的文檔格式,RPlot,CSV,JSON,XML還有HTML!哎,還有特性我還沒說完呢,您這是要干啥去啊!
? ? ?老郭:我回家開電腦去啊!
? ? ?老于:這火急火燎的回去下種子?啥片子啊,給我也發一份啊!
? ? ?老郭:我回家開電腦開VS下BenchmarkDotNet去啊!
? ? ? 在GitHub上,tupac-amaru?組織下有個開源的項目benchmarks:?https://github.com/tupac-amaru/benchmarks
? ? ? 這個項目是一個基于BenchmarkDotNet?的一個基準測試項目模板。
? ? ? 包含了一個基準測試項目的腳手架工程。還有準備了可以直接運行的腳本。
? ? ? 面向windows用戶的上可用的bat腳本和Docker腳本。
? ? ? 面向Mac/Linux上可用的sh腳本和Docker腳本。
? ? ? 有興趣的可用去看一下。因為編寫基準測試的代碼已經有具體的示例項目,網上也有大量的相關博文,這里就不再做詳細介紹。
? ? ? 不過網上對很少有解讀生成的報告的,在這里我嘗試解讀一下。
? ? ? 在YACEP的基準測試報告中:https://github.com/tupac-amaru/yacep/tree/_benchmark#atomicvaluestring?
? ? ? 上面的報告來自代碼:https://github.com/tupac-amaru/yacep/blob/master/tests/TupacAmaru.Yacep.Benchmark/AtomicValue/String.cs
? ? ? 這個代碼是用來測試YACEP將表達式中的字符串轉換為C#字符串的能力。
? ? ? 字符串和數值是表達式的幾大基本數據類型之一,所以YACEP必須要對字符串負責,不能面對字符串表達式硬氣不起來,要硬,要有一定的性能。
? ? ? 上面的報告有兩部分信息。
? ? ? 第一部分顯示的是這個基準測試運行的宿主機配置以及為基準測試配置的參數。
? ? ??宿主機配置信息:
BenchmarkDotNet=v0.11.5, OS=ubuntu 16.04Intel Xeon CPU E5-2673 v3 2.40GHz, 1 CPU, 2 logical and 2 physical cores
.NET Core SDK=2.2.204
[Host] : .NET Core 2.2.5 (CoreCLR 4.6.27617.05, CoreFX 4.6.27618.01), 64bit RyuJIT
所用BenchmarkDotNet的版本是0.11.5
操作系統是Ubuntu 16.04
CPU型號E5-2673 v3 2.40GHz, 1 CPU, 2 logical and 2 physical cores
后面一部分是.NET Core的信息
? ? ? 為基準測試配置的參數(官方文檔):
Toolchain=InProcessEmitToolchain InvocationCount=8 IterationCount=200LaunchCount=1 RunStrategy=Throughput UnrollFactor=4
WarmupCount=1
Toolchain參數,用來配置用于生成,構建和執行具體基準測試工具鏈的類型。InProcessEmitToolchain參數的意思是用在進程內用emit的方式生成用于做基準測試的對象,而不生成新的可執行文件去獨立運行。
InvocationCount,是指在單次迭代中一個方法的調用次數。該值必須是后面值UnrollFactor的倍數。
IterationCount,是指在測試過程中迭代的總數。
LaunchCount,是指需要使用的進程總數。
RunStrategy,這個參數在這個基準測試中配置其實無意義的,不指定的話,默認值就是Throughput。
UnrollFactor,是指基準測試的過程中方法在循環中循環執行的次數。
WarmupCount,是指預熱的次數?
? ? ? 第二部分是一張表,有六列,各列的意思大致如下:
Method,代表測試的方法。如果一個基準測試中包含多個測試方法,就可以用來橫向比較各個方法的測試結果
StringLength,這是一個自定義參數(代碼)。表示YACEP處理的隨機字符串長度。
Mean,是指方法執行時間的平均值,和后面的中值不一樣,{1, 2, 5, 8}的均值是(1+2+5+8)/4=4,?{1, 2, 5, 8, 10}的均值是(1+2+5+8+10)/5=5.2
Error,置信區間。
StdDev,執行時間的偏差,值越大,偏差越大
Median,是指方法執行時間的中值,和前面的均值不一樣,{1, 2, 5, 8}的中值是中間兩個數的均值(2+5)/2=3.5,?{1, 2, 5, 8, 10}的中值是中間那個值5
? ? ? 好了,現在來解讀一下這個報告的最后一行。
? ? ??從表達式中將長度為1000的字符串轉換C#字符串,YACEP平均處理時間是194.1ns.?
? ? ? ? ?注:1秒=1000000000 納秒,以后誰說C#性能不好,去,錘他!
?
?
?4.?服務篇
? ? ?借助于上面的工具,我們已經可以在本地做單元測試,計算覆蓋率,拿到更易讀的覆蓋率報告以及基準測試報告。
? ? ?但是!這還不夠好,我們的代碼不可能總是要人去跑測試,要人去執行命令行代碼獲取覆蓋率,要人去跑基準測試再拿具體的報告。
? ? ?很早很早之前一位來自中國的和尚曾有言:
道求道,佛家求善,儒學求中庸。如是搞IT的,當求懶!
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?--?沃·茲基碩德
? ? ?說的對,搞IT的就是應該求懶!!!
??
?
? ? ?懶???難道意味著啥都不做?
嘿,老板!我,告訴你,作為搞IT的,大佬說要追求懶!
所以,從明天開始,我~決定啥都不做!
面對我~這樣一個有追求的程序員,請記得!要多發一倍工資給我!? ? ?
老板看了看你,徑直走到門前,關上門,再慢悠悠的走到窗前,緩緩的拉下百葉窗。
偌大的辦公室暗了下來。
整個辦公室,除了你,就剩他。
只見他點燃一根煙,靠著沙發坐了下來,若有所思。
沉默著抽完一整支煙。
正欲張口,忽聽前臺小妹一聲尖叫。
心中暗道:莫不是婦產科醫院的結果出來了?
欲知后事如何,請看下篇!
原文地址:https://www.cnblogs.com/wushilonng/p/10917619.html
.NET社區新聞,深度好文,歡迎訪問公眾號文章匯總?http://www.csharpkit.com?
總結
以上是生活随笔為你收集整理的在上司面前硬不起来?教你如何快速将字符串转换为可执行代码的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: .NET Core 3.0之创建基于Co
- 下一篇: 记一次ORM的权衡和取舍