2009-11-11 63 views
3

對於我的生活,我無法弄清楚我的代碼中的這種性能。我有,我測量它需要多長時間運行構造的容器對象(對象下方),在公共構造計時碼現在C#創建對象比構造函數調用慢得多

public class WorkUnit : IWorkUnit 
{ 
    private JobInformation m_JobInfo; 
    private MetaInfo m_MetaInfo; 
    private IPreProcJobInfo m_PreprocDetails; 


    readonly private Guid m_ID; 
    private Guid m_ParentID; 
    private Guid m_MasterJobID; 


    private string m_ErrorLog = string.Empty; 
    private PriorityKeeper m_Priority; 
    private WorkUnitClassification m_Classification; 

    private IJobPayload m_CachedPayload; 
    private IJobLogger m_Logger; 
    private EventHandler<JobEventArgs> m_IsFinished; 
    private ReaderWriterLockSlim m_Lock; 

    public WorkUnit(string InputXML, Guid JobID, IJobLogger Logger) 
    { 
     DateTime overstarttime = DateTime.Now; 

     try 
     { 
     ....Do Stuff.... 
     } 
     catch(XMLException e) 
     {...} 
     catch(Exception e) 
     { 
     ... 
     throw; 
     } 

     double time = (DateTime.Now - overstarttime).TotalMilliseconds 
     Console.WriteLine("{0}", time); 
    } 

    /// <summary> 
    /// Private Constructor used to create children 
    /// </summary> 
    private WorkUnit(Guid MasterID, Guid ParentID, WorkUnitClassification Classification, PriorityKeeper Keeper) 
    {...} 

    [OnDeserializing()] 
    private void OnDeserialize(StreamingContext s) 
    {...} 

    public PriorityKeeper PriorityKey 
    {...} 

    public bool HasError 
    {...} 

    public bool Processing 
    {...} 

    public bool Splittable 
    {...} 

    public IEnumerable<IWorkUnit> Split(int RequestedPieces, int Bonds) 
    {...} 

    public void Merge(IResponse finishedjob) 
    {...} 

    public ReadOnlyCollection<IWorkUnit> Children 
    {...} 

    public bool IsMasterJob 
    {...} 

    public Guid MasterJobID 
    {...} 

    public Guid ID 
    {...} 

    public Guid ParentID 
    {...} 

    public EnumPriority Priority 
    {...} 

    public void ChangePriority(EnumPriority priority) 
    {...} 

    public string ErrorLog 
    {...} 

    public IMetaInfo MetaData 
    {...} 

    public IJobPayload GetProcessingInfo() 
    {... } 

    public IResponseGenerator GetResponseGenerator() 
    {... } 

} 

,我測量它需要創建對象的總時間

DateTime starttime = DateTime.Now; 
var test = new WorkUnit(temp, JobID, m_JobLogger); 
double finished = (DateTime.Now - starttime).TotalMilliseconds; 

,我一直得到以下性能數字 -

構造的時間 - 47毫秒

對象的創建時間 - 387毫秒

47 ms是可以接受的,387真的很差。取出日誌會忽略改變這些數字。有誰知道爲什麼這麼長時間?我的系統是VS 2008 SP1,面向Windows XP上的.NET 3.5 SP1。我將不勝感激任何解釋。我很快就會分析這個剖析器,但我覺得它無法深入到我需要解釋這種行爲的層面。謝謝你的幫助。

編輯:我在發佈運行

+0

如果沒有'try'和'catch'代碼中的代碼,就很難猜測。也許你可以發佈更多的代碼?是否拋出異常(斯諾克)? – 2009-11-11 18:14:37

+2

您是在測量第一次調用特定代碼路徑還是平均調用SUBSEQUENT?請記住,對任何事物的第一次調用可能會更加昂貴,因爲它需要加載它所引用的所有DLL的傳遞閉包,在它使用的類型的靜態構造函數中運行所有代碼,然後jit編譯所有不是代碼的代碼。首次下載代碼路徑通常比每個後續的調用都要昂貴得多。 – 2009-11-11 18:20:04

回答

7

您確定您所看到的是對象創建時間,而不是CLR啓動的影響嗎?

嘗試在循環中運行測試50次並忽略第一個結果。

+0

就是這樣,第一次真的很慢,在這之後快得多 – Steve 2009-11-11 18:20:54

+1

只是我的兩分錢:我認爲這裏真正的罪魁禍首是'DateTime.Now',它在第一次運行時比之後的所有時間都要慢。 (我知道,我已經介紹過它。)既然你在構造函數之前調用它,*那*是緩慢的;那麼在你的構造函數和後來的兩個應該很快。 – 2009-11-11 18:44:09

+2

這是有道理的。 DateTime.Now需要做各種有趣的事情,比如說我現在是夏令時,我在哪個時區等等。第一次加載所有的代碼是昂貴的;隨後每個時間都很便宜,因爲它只是調用「熱門」代碼,而不是加載它「冷」。 – 2009-11-11 19:57:07

2

時間帶出Red Gate Performance Profiler。而不是要求我們猜測問題可能是什麼......下載試用版,並讓它告訴你究竟你的問題在哪裏。

輪廓儀是很好的工具。任何開發人員都應該熟悉如何利用它們來查明性能問題。

0

你知道嗎Console.WriteLine在構造函數中拋出你的時機巨大?任何IO操作都會拋棄這些時序。

如果你想要真正的數字,把持續時間存儲在一個全球的某個地方然後打印出來你已經記錄了一切。

+0

如果我發表評論,我仍然可以大致同時構建對象 – Steve 2009-11-11 18:13:52

6

史蒂夫,

這裏有幾件事情要考慮:使用日期時間來使用StopWatch

  1. 開關。對於這些類型的情況來說更準確。
  2. 停止在計時過程中寫入控制檯。 IO將會很重要,並影響你的時間安排。
  3. 確保您在發佈/優化版本中運行,並且不在Visual Studio測試主機下運行。如果你從一個默認VS運行,切換到Release,編譯並使用Ctrl + F5(而不是F5)來運行。

鑑於您的時機,我猜#2是你的問題。 Visual Studio增加了大量影響性能的「鉤子」。在Visual Studio中運行時的時間。

+0

不幸的是,我在發佈中運行 – Steve 2009-11-11 18:13:12

+0

您是否在Visual Studio中運行?只需切換到釋放是不夠的 - 確保不要在測試主機內部運行。 – 2009-11-11 18:14:09

+0

這聽起來像是你遇到了這樣一個事實,即測試主機需要掛鉤到每個對象中,這會增加很大的開銷。 – 2009-11-11 18:14:46

4

首先使用StopWatch類來測量時間。系統時間的分辨率太低,無法給出準確的結果。

嘗試創建該類的多個實例。程序集第一次可能不是JIT:ed,這當然需要一些時間。

1

該問題包含自己的答案;除了運行構造函數之外,還有更多的實例化對象。當你調用new時,你要求運行時爲對象分配空間,處理運行時需要的任何內部簿記,爲每個基類型調用構造函數(在本例中,僅爲object),最後調用構造函數。

當你測量總實例化時間時,你正在測量所有這些;當你單獨計算構造函數時,你只能測量一部分。如果數字不是不同,那會引起擔憂。

1

正如其他人建議,首先,絕對開關使用System.Diagnostics.Stopwatch:

public WorkUnit(string InputXML, Guid JobID, IJobLogger Logger, out TimeSpan elapsed) 
{ 
    Stopwatch constructionStopwatch = Stopwatch.StartNew(); 

    // constructor logic 

    constructionStopwatch.Stop(); 
    elapsed = constructionStopwatch.Elapsed; 
} 

然後:

TimeSpan constructionTime = TimeSpan.Zero; 
Stopwatch creationStopwatch = Stopwatch.StartNew(); 

var test = new WorkUnit(temp, JobID, m_JobLogger, out constructionTime); 

creationStopwatch.Stop(); 
TimeSpan creationTime = creationStopwatch.Elapsed; 

double constructionMs = constructionTime.TotalMilliseconds; 
double creationMs = creationTime.TotalMilliseconds; 

的原因我建議切換到使用TimeSpan對象,而不是像(DateTime.Now - startTime).TotalMilliseconds這樣的東西是,雖然它應該沒有什麼區別,但在技術上,在後一種情況下,你首先要求獲得時間和然後得到TotalMilliseconds屬性,我幾乎可以肯定是一個計算值,在構造函數中。這意味着在之間實際上有一個步驟檢查構造函數中的時間,並在之後立即檢查時間。真的,這應該基本上可以忽略不計,但覆蓋所有基地是很好的。

相關問題