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

C#对象创建比构造函数调用慢得多,c#,performance,C#,Performance,在我的一生中,我无法理解代码中的这种性能影响。我有一个容器对象,我在其中测量运行构造函数(下面的对象)所需的时间,并在公共构造函数中计时代码 public class WorkUnit : IWorkUnit { private JobInformation m_JobInfo; private MetaInfo m_MetaInfo; private IPreProcJobInfo m_PreprocDetails; readonly private Gu

在我的一生中,我无法理解代码中的这种性能影响。我有一个容器对象,我在其中测量运行构造函数(下面的对象)所需的时间,并在公共构造函数中计时代码

 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()
    {... }

}
我一直得到以下性能数据-

构造函数时间-47毫秒

对象创建时间-387毫秒

47毫秒是可以接受的,387毫秒是非常糟糕的。去掉日志记录可以忽略地改变这些数字。有人知道为什么要花这么长时间吗?我的系统是VS2008SP1,目标是WindowsXP上的.NET3.5SP1。我希望你能解释一下。我将很快发布分析器,但我觉得它无法深入到我需要解释这种行为的级别。谢谢你的帮助

编辑:我正在运行发行版

是时候推出了。与其让我们猜测问题可能是什么,不如下载一个试用版,让它告诉你问题的确切位置


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

您知道构造函数中的
控制台.WriteLine
极大地浪费了您的时间吗?任何IO操作都会偏离这些计时

如果你想要真实的数字,请将持续时间存储在一个全局文件中,然后在记录完所有内容后打印出来。

Steve

这里有几件事需要考虑:

  • 从使用DateTime切换到使用。对于这类情况,它要准确得多
  • 在计时过程中停止向控制台写入。IO将非常重要,并影响您的时间安排
  • 确保您正在版本/优化版本中运行,而不是在Visual Studio测试主机下运行。如果从默认VS运行,请切换到Release、build,并使用Ctrl+F5(而不仅仅是F5)来运行

  • 考虑到你的时间安排,我猜2号是你的问题。VisualStudio添加了许多“挂钩”,这些挂钩会显著影响性能。在Visual Studio内部运行时的计时。

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


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

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


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

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


    当你测量总的实例化时间时,你就是在测量所有的实例化时间;当你单独计算构造器的时间时,你只是在测量一个部分。如果数字没有差异,这将是一个值得关注的问题。

    正如其他人所建议的,首先和最重要的是,一定要切换到使用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).totalmillizes
    的操作的原因是,尽管这应该没有什么区别,但从技术上讲,在后一种情况下,您首先调用获取时间,然后获取
    totalmillizes
    属性,我几乎可以肯定这是一个计算值,在构造函数中。这意味着在检查构造函数中的时间和检查之后的时间之间实际上有一个步骤。事实上,这基本上可以忽略不计,但是覆盖所有的基础是很好的。

    遗憾的是,我正在运行Release。不过,你是在Visual Studio内部运行吗?仅将ign切换到release是不够的-请确保不要在测试主机内运行。听起来您确实遇到了这样一个事实,即测试主机需要连接到每个对象,这会增加大量开销。很抱歉,在VSSwitch to stopwatch外部运行它,并取消IO调用。如果这并不能让事情变得明显,那么很可能是其他“奇怪”的事情发生了。分析你的应用程序,看看它可能是什么。如果我把它注释掉,我仍然可以在大致相同的时间构建对象。如果没有
    try
    catch
    块中的代码,就很难猜测。也许你可以发布更多的代码?是否抛出异常(sloow)?您是在测量特定代码路径下的第一次调用,还是后续调用的平均值?请记住,对任何对象的第一次调用都可能要昂贵得多,因为它需要加载它引用的所有DLL的可传递闭包,运行它使用的类型的静态构造函数中的所有代码,并jit编译所有未被ngen编译的代码。第一次在代码路径上运行通常比每次后续调用都要昂贵得多。就是这样,第一次运行速度非常慢,之后快得多。我只花了两分钱:我认为真正的罪魁祸首是
    DateTime。现在
    ,它在第一次运行时比所有后续时间都慢得多。(我知道,我已经分析过了。)既然你在构造函数之前调用了它,那就是慢的一个;那么构造函数中的两个和之后的一个应该是快的,这是有道理的。DateTime。现在需要做各种有趣的事情,比如计算我现在是否在夏令时,我在哪个时区,等等。第一次加载所有这些代码是昂贵的;随后的每一次都很便宜,因为它只是调用“热”代码,而不是加载“冷”代码。
    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;