YACEP相關技術工具服務技巧(上)

    這篇隨筆的核心是介紹一下YACEP所用到的一些技術工具,服務和技巧,鑑於篇幅緣由,不可能面面俱到,只能點到爲止,目錄以下:html


    

    目錄:git

          1. YACEP簡介(上)程序員

            2. 技術篇(上)
github

               2.1 利用優先登山算法解決運算符優先級的問題算法

               2.2 利用ReadOnlySpan加速字符串解析express

               2.3 利用表達式樹和Emit生成表達式執行代理ubuntu

           3. 工具篇(上)windows

                3.1 測試覆蓋率工具 - Coverlet api

                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 * d + e

            按照四則運算法則: 先乘除後加減,對於上述表達式,咱們標記一下運算的優先級

         再按照結合律,對於更高優先級的乘法,在求 a*b*c 的值的時候,咱們無所謂先算 a*b 再用這個結果去乘c,仍是先算b*c再去乘a。按照當前主流的文字閱讀規則,咱們選擇從左往右,即先算b*c,而後拿到這個結果再乘d。咱們將b*c的結果存儲爲m,因而上述表達式能夠簡化爲:

// m = b * c
a + 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的結果就行了,把前面的步驟合併在一塊兒。

a + b * c * d + e
    -----
      m    
    ---------
       n
---------------
         x
----------------------

      去掉字符,咱們把下面的虛線鏈接起來,大概是這樣:

       山頂
      / ----- \
      /          \
      / ---------   \
      /                \
      / -------------     \
      /                     \
      / -----------------     \

      這就是傳說中的登山算法!!!

     是否是超好懂!!!

     10秒鐘讀懂了登山算法!!!     

      其實怎麼說呢,理解到這一步基本就離理解完整的登山算法差的沒幾百步了。總共也就幾百步,你再走幾百步就徹底理解了,加油,咱們繼續。

      按照上述的步驟,咱們如今開始理解算法的流程。咱們將表達式中的元素分爲兩類,一類咱們叫原子值,好比上面表達式中的a、b、c、d、e,另外一類咱們叫他們爲運算符,好比+和*。對於帶括號的表達式,咱們能夠稱其爲子表達式,也是一種表達式,因此咱們老是能夠將任何一個表達式拆解爲只包含原子值或運算符的表達式(對於一個不包含運算符的表達式,直接拿值就完了)。

      基於上述的分類,咱們開始描述一下登山算法的運算步驟:

               1. 讀取當前一個原子值和它最鄰近的運算符

               2. 讀取下一個原子值和它最鄰近的運算符

               3. 若是步驟1中的運算符的優先級大於步驟2中的優先級,則算法返回當前原子值,當前原子值最鄰近運算符與下一個原子值的子表達式

               4. 不然,從步驟2開始構建新的子表達式繼續按照上述步驟處理

      回到表達式:

a + b * c * d + e

      如今按照登山算法開始處理這個表達式:

  1.  讀到原子值a以及優先級爲1的運算符+, a + 
  2.  讀取下一個原子值b以及優先級爲2的運算符*, b * 
  3.  優先級2大於1,因此咱們開始從b開始構建子表達式 
  4.  讀取下一個原子值c以及優先級爲2的運算符*, c * 
  5.  當前的子表達式的  b * 優先級是2(,新讀到   c * 的優先級也是2,獲得新表達式 b * c * , 優先級是2。上面的步驟的值  m * 
  6.  讀取下一個表達式  d + , +的優先級是1,因此整個子表達式返回值爲  m * d ,這兩步須要理解結合律。上面的值  n +  
  7.  子表達式已經處理完成,退出子表達式的處理流程,當前的表達式爲的優先級爲步驟1中, a +
  8.  下一個表達式爲 n +  ,二者的優先級都是1,返回子串  a + n 上面的值 x + 
  9.  下一個表達式爲  e  ,直接返回
  10.  獲得最終表達式  x + e 

       回到以前咱們獲得的那座山,再看看這個步驟,你會發現這個流程還真的就是在爬這座山 。

       山頂
      / ----- \
      /          \
      / ---------   \
      /                \
      / -------------     \
      /                     \
      / -----------------     \

2.2 利用ReadOnlySpan加速字符串解析

         C#中String對象是一個只讀對象,一旦建立將不可更改。因此C#中對String對象作更改的方法底層都會建立一個新的String對象。好比在以下代碼中:

 1             unsafe
 2             {
 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             unsafe
 2             {
 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來處理字符串,那麼上述的問題均可以被解決。

      而後大體說一下SpanSpan能夠用於表達任何一段連續內存空間,不管是數組,非託管指針,可獲取到指針值的託管內存等等等等(是否是回憶起當初被指針支配的恐懼感),其實在它內部的實現就是一個指針。相對於C/C++裏面的指針須要各類當心翼翼,不敢有一絲怠慢忘記釋放,或訪問到離奇的地址,或由於各類緣由變成野指針。Span會在內部維護這個指針的地址,在作指針運算時,會作邊界檢查,在更新引用時,垃圾回收器也能判斷出該如何回收內存。

      對於Span的解讀,推薦閱讀下面這個系列,做者的解讀很是贊。如今願意寫博文講清 What、How 和 Why的博主很少了,且讀且珍惜。

      http://www.javashuo.com/article/p-xihxuzhj-dm.html

2.3 利用表達式樹和Emit生成表達式執行代理

     動態生成代理是一個古老的話題。最開始是由於你們都以爲.NET自帶的那個反射操做太慢,怎麼說呢,其實對於大部分場景是夠用的,某知名大佬說:

往往看人在談論代碼時,都說那反射操做是極慢的,萬萬不可取。

在我本身,卻認爲反射之慢不過毫秒。

用不正當的思路寫出的代碼纔會引發真正的慢。

                                                       -- 樹人Groot

         

      之因此會成爲一個古老的話題,是由於動態生成代理會出如今太多的業務場景中。

      最多見的就是快速獲取一個對象指定名稱的成員值,你會看到各色愛寫庫愛造輪子的大佬很是熱衷去搞的個快速對象訪問器什麼的。

      多年前博客園大佬趙姐夫還參與過此事寫過一個庫,地址以下:

            http://blog.zhaojie.me/2009/02/fast-reflection-library.html

      :雖然老趙已經好久沒有更新博客了,可是他的博客仍是很是推薦去閱讀一下,內容很豐富,乾貨特別多。

          博客地址:http://blog.zhaojie.me

      好了,回過頭來。如今對使用動態生成代理的場景作一個彙總,常出現的場景以下:

  1.  對象序列化反序列化,這種場景多出現於RPC中,代理要把stream轉換爲object
  2.  實現ORM,代理要把reader轉換爲object(這種其實也是rpc)
  3.  實現AOP,代理要爲具體類生成一個包含一系列切面函數的類
  4.  綁定求值,好比模板渲染或YACEP這種對編譯結果調用執行獲取結果的過程

        動態生成代理實現方式有以下四種:

  1.  利用Expression構造代理方法
  2.  DynamicMethod生成動態函數構造代理方法
  3.  DynamicAssembly構建代理類
  4.  利用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.1                                                                                 
Copyright (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.04
Intel 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=200  
LaunchCount=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的,大佬說要追求懶!

因此,從明天開始,我~決定啥都不作!

面對我~這樣一個有追求的程序員,請記得!要多發一倍工資給我!     

老闆看了看你,徑直走到門前,關上門,再慢悠悠的走到窗前,緩緩的拉下百葉窗。

偌大的辦公室暗了下來。

整個辦公室,除了你,就剩他。

只見點燃一根菸,靠着沙發坐了下來,如有所思。

沉默着抽完一整支菸。

正欲張口,忽聽前臺小妹一聲尖叫。

心中暗道:莫不是婦產科醫院的結果出來了?

欲知後事如何,請看下篇!

相關文章
相關標籤/搜索