C#对象创建比构造函数调用慢得多

时间:2009-11-11 18:07:44

标签: c# performance

对于我的生活,我无法弄清楚我的代码中的性能影响。我有一个容器对象,我测量运行构造函数(下面的对象)所需的时间,公共构造函数中的计时代码

 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 ms

对象创建时间 - 387 ms

47毫秒是可以接受的,387非常糟糕。取出记录可以忽略不计地改变这些数字。有谁知道为什么这么长时间?我的系统是VS 2008 SP1,针对Windows XP上的.NET 3.5 SP1。我将不胜感激任何解释。我将很快打破探查器,但我觉得它无法深入研究解释这种行为所需的水平。谢谢你的帮助。

编辑:我正在发布

7 个答案:

答案 0 :(得分:7)

你确定你所看到的是对象创建时间而不是CLR启动的效果吗?

尝试在循环中运行测试50次并忽略第一个结果。

答案 1 :(得分:6)

史蒂夫,

以下是需要考虑的几件事:

  1. 从使用DateTime切换到使用StopWatch。对于这些类型的情况,它更准确。
  2. 在计时过程中停止写入控制台。 IO将是重要的,并影响你的时间。
  3. 确保您在发布/优化版本中运行,而不是在Visual Studio测试主机下运行。如果从默认VS运行,请切换到Release,build,然后使用Ctrl + F5(而不仅仅是F5)运行。
  4. 考虑到你的时间安排,我猜测#2是你的问题。 Visual Studio添加了许多能够显着影响性能的“钩子”。在Visual Studio中运行时的计时。

答案 2 :(得分:4)

首先使用StopWatch类来测量时间。系统时间的分辨率太低,无法给出任何准确的结果。

尝试创建多个类的实例。第一次装配可能不是JIT:ed,这当然需要一些时间。

答案 3 :(得分:2)

是时候提出Red Gate Performance Profiler了。而不是要求我们猜测问题可能是什么...下载试用版并让它告诉您问题的确切位置。

Profilers是很棒的工具。任何开发人员都应该熟悉如何利用它们来查明性能问题。

答案 4 :(得分:1)

这个问题包含了自己的答案;实例化一个对象不仅仅是运行它的构造函数。当你调用new时,你要求运行时为对象分配空间,处理运行时需要的内部簿记,调用每个基类型的构造函数(在本例中,只是object),以及最后打电话给你的构造函数。

当您测量总实例化时间时,您正在测量所有这些;当你单独为构造函数计时时,你只测量一个零件。如果数字不同,那将引起关注。

答案 5 :(得分: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属性,我几乎可以肯定它是一个计算值,在构造函数中。这意味着在之间实际上有一个步骤检查构造函数中的时间并在之后立即检查时间。真的,这应该基本上可以忽略不计,但是覆盖你所有的基础是好的。

答案 6 :(得分:0)

您是否知道构造函数中的Console.WriteLine会导致您的时间非常?任何IO操作都会抛弃这些时间。

如果您想要实数,请将持续时间存储在某个地方,然后在记录完所有内容后将其打印出来