2026年8月24日 星期一

[.NET/Visual Studio] 如何使用Visual Studio Performance Profiler來找耗時操作?

 前言:

系統的優化最常見的是降低操作的執行時間,Visual Studio(以下簡稱VS)內建的profiler其一的CPU Usage是很適合的入門工具。


使用方式:

  • 打開VS目錄 -> [Debug] ->[Performance Profiler](或快捷鍵Alt + F12):


  • 此例目標選擇「啟動專案」,選擇Release build / CPU Usage / Start with collection paused(執行profiler後自己啟動錄製) -> Start:

  • 在要量測的執行前,按下Record,執行後,按下Stop Collection,即可產出分析結果







  • 報表主要分為3個部分
    • Top Insights:會列出主要問題,並可搭配copilot分析或優化
    • Top Functions:依序列出耗時的函式
    • Hot Path:可列出最耗時的執行路徑



To be continued...


參考資料:

https://learn.microsoft.com/en-us/visualstudio/profiling/cpu-usage?view=visualstudio

https://learn.microsoft.com/en-us/visualstudio/profiling/running-profiling-tools-with-or-without-the-debugger?view=visualstudio

https://learn.microsoft.com/en-us/visualstudio/profiling/cpu-insights?view=visualstudio




2025年12月21日 星期日

[.NET/Visual Studio] 如何使用Visual Studio Performance Profiler來找memory leak?

要抓.NET應用程式的memory leak,最基本就是使用Visual Studio Performance Profiler,

該如何用呢? 操作步驟如下:

  • 打開Performance Profiler(Alt + F2)
  • 選擇分析目標,此處使用啟動專案並勾選Memory Usage,點擊啟動(建議使用release build)

  • 在baseline時機點,點擊快照

  • 執行懷疑有memory leak的操作後,再快照1次來比較2次快照的差異(點擊紅框處)。

  • 可針對size差異做排序,找出那些物件造成leak,點擊其中一個物件,下方面板會顯示此物件的參考鏈,就可以去判斷為何物件還沒被GC。

  • 取得所需的快照後,即可停止收集,並可將結果儲存起來。


參考資料:
https://learn.microsoft.com/en-us/visualstudio/profiling/memory-usage?view=visualstudio
https://learn.microsoft.com/en-us/visualstudio/profiling/memory-usage-without-debugging2?view=visualstudio&pivots=programming-language-dotnet
https://learn.microsoft.com/en-us/visualstudio/profiling/hot-path-to-root?view=visualstudio


2024年8月12日 星期一

[C#/Memory] managed記憶體快照裡的ClassName+<>c是什麼?

 前言:

有一次在記憶體快照中發現某個ClassName+<>c的物件,如下圖紅框處:



第一次看到有點不知所以,查了一下,原來是compiler建立用來執行lambda的class物件。

舉個例子,寫了如下圖的程式(用到lambda):













看了IL code,的確compiler在我的Program類別內多建了一個<>c類別(如下圖),
這個類別還包含:

  • 1個<>c型別的靜態唯讀field(<>9)
  • 1個Func委派的靜態的field(<>9__0_0)
  • 1個靜態的建構子
  • 1個預設建構子
  • 1個符合Func委派簽章的方法(b__0_0)










所以managed記憶體內才會出現Progarm+<>c的物件,但compiler為何需要這麼做?

原因是效能考量,將lambda的delegate宣告成static,免去重複建立instance的effort,

就像類別內的static method。


To be continued...


參考資料:

https://blog.jetbrains.com/dotnet/2019/01/23/c-classes-memory-snapshots/

2023年8月9日 星期三

[C#] 沒有取消訂閱(unsubscribe)事件(event)會導致memory leak?

 Publisher物件可提供事件供其他subscriber物件做訂閱,且一般也會在適當的時機點取消訂閱,

但如果只訂閱但沒有取消訂閱,會讓程式memory leak嗎?


我將情境分為以下4種:

1. Publisher生命週期較subscriber長,subscriber有取消訂閱。

2. Publisher生命週期較subscriber長,subscriber沒有取消訂閱。

3. Publisher生命週期較subscriber短,subscriber有取消訂閱。

4. Publisher生命週期較subscriber短,subscriber沒有取消訂閱。


Publisher使用下圖的Server類別:

Subscriber使用下圖的Log類別,Log訂閱Server的ServerConnected事件:


以下依序列出測試結果,這裡使用VS內建的記憶體快照來確認物件是否已GC:

1. Publisher生命週期較subscriber長,subscriber有取消訂閱:

    可以看到Log物件(subscriber)已經被GC了。

2. Publisher生命週期較subscriber長,subscriber沒有取消訂閱:

    可以看到Log物件(subscriber)還存在記憶體。

3. Publisher生命週期較subscriber短,subscriber有取消訂閱:

4. Publisher生命週期較subscriber短,subscriber沒有取消訂閱:

情境3跟4,都是Server物件先回收,Log物件還留著的情形,記憶體快照也是一樣結果,
代表publisher先回收的話,subscriber沒有取消事件訂閱,也沒有關係:


結論是,subscriber還是要在適當時機點取消事件訂閱,如同情境2的結果,訂閱長生命週期的事件,是會造成memory leak的。

2023年4月16日 星期日

[UML] UML的關聯(association)、依賴(dependency)與差異

 關聯(association)與依賴(dependency)是UML類別圖常用來描述類別間的關係。

 關聯的表示如下,Customer關聯Order,用實線指到目標類別:


依賴的表示如下,MessageBroker依賴MessageFormat,虛線表示:









會覺得好像有點像? 

關聯是用在A類別擁有B類別的執行個體參考,如下程式碼:


而依賴則是代表A類別在其方法內建立或使用B類別的instance,
也就是A類別並沒有保存B類別的物件。








而箭頭上的數字就代表擁有參考的數量,如下圖就代表Customer擁有多張Order。


其實關聯還有其他2個特殊形式,聚合(aggregation)與組合(composition),
不過直接用關聯表示也可以,除非真的需要特別呈現出來。


2022年12月25日 星期日

[C#] 讓event handler method不因event多次觸發而重複執行

 前言:

最近遇到一個情境,物件的某個event trigger會多次,

但訂閱的event handler method只需要處理該event trigger的第一次,

該怎麼做呢?


作法:

如下圖的Document類別,有一個Loaded事件,當呼叫Open方法時會觸發Loaded事件。


client端訂閱該Loaded事件,但只需要針對第一次觸發做處理,也就是讓event handler method只執行一次。因為event是把delegate清單wrap起來,
因此再加入第2個delegate將上一個event handler移除事件訂閱。

可以看到雖然Loaded事件觸發2次,但訂閱的event handler method只執行1次。


2021年10月31日 星期日

[SQL Server] 如何使用執行計畫(execution plan)判斷statement效能

 前言:

提到資料庫存取,大多會擔心撰寫的statement是否有效能問題,

雖然公司可能會有DBA來review開發人員的statement,

但開發人員仍需初步判斷所寫的statement執行的狀況。

以SQL Server來說,會使用SSMS(SQL Server Management Studio)裡的執行計畫(Execution plan)來判斷。


作法:

可以準備幾個要比較statement來跑執行計畫,一般會先看是否有吃到index,

效能通常是:index seek > index scan

但如果SELECT的資料筆數接近整張table的筆數,SQL Server就可能採table scan將資料回傳。


基本上使用PK來當條件,就會套用cluster index seek。cluster index預設是從table的PK建出來的,每張table只會有一組,如下圖OrderID是[Orders]的唯一PK。



還有一種是nonclustered index seek,nonclustered index在一張table可以有多組,可以視SELECT資料的需求來建立。如下圖CustomerID是[Orders]的其一nonclustered index。


至於index scan,可分為cluster index scan,如下圖。


另一種則是noncluster index scan。


to be continued...


參考資料:

https://docs.microsoft.com/zh-tw/sql/relational-databases/sql-server-index-design-guide?view=sql-server-ver15

https://docs.microsoft.com/zh-tw/sql/relational-databases/indexes/clustered-and-nonclustered-indexes-described?view=sql-server-ver15

https://github.com/Microsoft/sql-server-samples/tree/master/samples/databases/northwind-pubs

2021年5月31日 星期一

[Design Pattern] 狀態模式(State Pattern)


前陣子開發電子化表單相關的程式,剛好使用到狀態模式(State Pattern)。

需求是每張表單有其狀態,每個狀態有對應的處理動作,

因此直覺上可以使用狀態模式的概念來處理。


先上個類別圖:



這邊使用抽象類別StateBase,下方的類別就是實際的狀態,

以請假表單來說,會有初始狀態與主管審核後狀態,

Execute方法代表執行狀態對應的動作,可能是狀態移轉或通知對應的人。

而StateContext是管理狀態的類別,其Request方法呼叫state物件的Execute方法,

並注入執行的state instance,以提供狀態執行所需的環境資訊。


剛好這需求,一開始同事是使用switch case來處理各狀態行為,

我改成狀態模式,避免全部的狀態行為都擠在同一個方法,

後續擴充維護也會容易許多,這也是Open–closed principle的精神。

2021年1月19日 星期二

[Web] OAuth Authorization code flow筆記與取得google userinfo範例

 

Authorization Code是OAuth的其中一種授權方式,

可以讓client程式取得使用者(resource owner)外部的資源,需要使用者輸入帳密,

流程如下:

● 觸發驗證流程後,client程式將使用者的瀏覽器導向authorization server的驗證端點(authorize endpoint)。

● 此時網頁會顯示client程式想存取使用者在外部的哪些資源,等待使用者授權。

● authorization server依使用者回應與帳密決定是否授權。

  • 若使用者允許,則authorization server會依client程式註冊OAuth服務時填寫的redirection URI,將authorization code導回給client程式。
  • 若使用者不允許或無法成功授權,則將錯誤訊息導回client程式。

● client程式再帶authorization code、redirection URI等參數POST到authorization server的權杖端點(token endpoint)。

● authorization server確認參數無誤後,即回傳access token相關資訊,即可以此token跟resource server要資料。


以Flow chart描述流程如下:



來個實際操作吧,這邊使用Google OAuth來取得userinfo:

  • 先到google developer console的Credentials頁面,新增OAuth client ID,至少要給redirect URIs,儲存後就會拿到ClientID與Secret。
  • 組合取得Authorization code的url,紅色部份依實際狀況填入:

https://accounts.google.com/o/oauth2/v2/auth?

client_id=client id from google oauth setting&

redirect_uri=redirect_uri from google oauth setting&

response_type=code&

scope=profile&

access_type=offline&

state=up to you

  • GET呼叫取得Authorization code的url,google server會callback Authorization code,code通常是4%2F開頭,要注意有url encode,所以要轉為4/。
  • 接著POST呼叫要token,code就帶Authorization code,通常超過10分鐘會失效,如下圖,即可取得欲存取資源的token。


  • 最後拿token即可存取授權的資源:



參考資料:

https://tools.ietf.org/html/rfc6749

https://developers.google.com/identity/protocols/oauth2/web-server

2020年4月18日 星期六

[MVC/Pattern] Unit of Work與Repository模式紀錄

前言:
上一次用到Unif Of Work,已經快3年了,有機會就來記錄一下吧。

先上個圖:


Repository部分讓商業邏輯跟資料存取可以拆開,也是一種pattern,
IRepository定義了常用的CRUD操作,使用泛型處理不同的資料實體,
建立Repository instance時,注入搭配的UnitOfWork instance。

而Unit Of Work負責處理資料交易的部分,確保資料與db是一致的。
當然,多了這些抽象,寫測試也就水到渠成了。

實際程式碼如下:
public interface IUnitOfWork : IDisposable
{ 

	DbContext DbContext { get; set; }
    
	void Commit();

}

public class UnitOfWork : IUnitOfWork
{

        public DbContext DbContext { get; set; }

        public UnitOfWork()
        {
            DbContext = new ApplicationDbContext();
        }

        public void Commit()
        {
            DbContext.SaveChanges();
        }

        public void Dispose()
        {
            DbContext.Dispose();
        }
        
}

public interface IRepository<T> where T : class
{

        IUnitOfWork UnitOfWork { get; set; }

        void Create(T entity);

        IQueryable<T> ReadAll();

        IQueryable<T> Read(Expression<Func<T, bool>> filter);

        void Delete(T entity);

        void Save();

}

public class Repository<T> : IRepository<T> where T : class
{

	private DbSet _entity;
    
	public Repository(IUnitOfWork unitOfWork)
	{
	    UnitOfWork = unitOfWork;
	}

	public IUnitOfWork UnitOfWork { get; set; }

	public void Create(T entity)
        {
            Entity.Add(entity);
        }

        public IQueryable ReadAll()
        {
            return Entity;
        }

        public IQueryable Read(Expression> filter)
        {
            return Entity.Where(filter);
        }

        public void Delete(T entity);
        {
            Entity.Remove(entity);
        }

        public void Save()
        {
            UnitOfWork.Commit();
        }

}


參考資料:
https://docs.microsoft.com/zh-tw/aspnet/mvc/overview/older-versions/getting-started-with-ef-5-using-mvc-4/implementing-the-repository-and-unit-of-work-patterns-in-an-asp-net-mvc-application
https://web.csulb.edu/~pnguyen/cecs475/pdf/repository%20pattern.pdf

2020年2月29日 星期六

[.Net/C#] C#中List與Array的關係(relationship between List and Array)

前言:
有一次聽到同事A跟同事B說,這裡不要用List,
用Array效能比較好,因為是直接存取記憶體。

當下覺得有點怪怪的,因為就我所知,
該程式是宣告固定長度的List,那跟宣告為Array不是一樣嗎?


從list.cs的Source code可以看到:
宣告固定長度的List,內部的確是用Array宣告。

正確的說,使用List其實就是在操作Array,
所以資料都是在記憶體上。

照這樣看來,宣告固定長度的List跟宣告Array,
在搜尋效能上,應該是一樣的?
簡單來測試一下:

結果跟我想的不一樣...


使用Array「大量」存取時,執行時間少了一半,
推測是因為List在存取Count跟使用indexer取得元素時,有做了一些檢查,
即使時間複雜度一樣,但step count多了一些,
這樣看來,如果是固定長度還是直接用Array,
會變動元素,當然就是List的強項了。


參考資料:

2020年1月31日 星期五

[Web] 初探Http Cache策略(cache-control)

前言:
瀏覽器會將從server下載回來的靜態檔案做快取,
好處是節省頻寬與資源,加快瀏覽速度。
壞處是需要針對存取資源做適當的快取配置,
讓client在瀏覽網頁的速度與資源的更新取得平衡。


作法:
除了更新資源檔案的檔名外,
一般都是從HTTP/1.1 header中的cache-control指令下手,
設定max-age(單位為秒數),讓browser定期跟server擷取資源,
不過各browser作法略有不同。
FireFox從第2次開始,會自行帶max-age=0給server,

Chrome不會自行帶max-age的指令,還是需server設定,
且需再增加no-cache指令,讓browser發request來重新驗證資源是否更新,
否則Chrome仍然使用local cache。


max-age雖然可讓browser定期擷取資源,但若資源內容沒有變動,
也沒有下載的必要,因此會搭配ETag指令:
server會依快取的檔案內容產出一字串,回應給browser,
之後browser送出request會夾帶If-None-Match指令,如果字串比對一致,
則回傳status code 304(表Not Modified),如下圖:

反之,若字串沒有match了,代表資源內容變更了,此時browser再重新擷取該資源即可。


建議font可善用CDN cache,分散又可降低server loading。


參考資料:
https://tools.ietf.org/html/rfc2616
https://tools.ietf.org/html/rfc7234
https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/http-caching
https://developer.mozilla.org/zh-TW/docs/Web/HTTP/Caching
https://medium.com/@codebyamir/a-web-developers-guide-to-browser-caching-cc41f3b73e7c

2019年12月1日 星期日

[Design Pattern] 策略模式(Strategy Pattern) in C#

前言:
一年前在實作資料處理程式時,不經意的用到該模式,
其實策略模式就是一種Dependency Injection的簡單例子,
因此,實務上很容易使用到。


說明:
類別圖如下:


可以看到Calculator是依賴ICal這個介面,
而實作ICal的類別,就是實作所謂的策略,
例如,將公分數值轉成公尺。
而Calculator可選擇要使用哪個策略,就可得到計算後的結果。


簡單實作如下:








但上面的做法,還可以再改善,將選擇策略的部分移到CalContext,
在建構CalContext時,傳入策略的名稱,就能在GetResult方法內再建立具體策略,
讓外部使用者操作更簡化,如下圖。