了解VS性能分析

时间:2015-06-30 10:36:46

标签: c# performance boxing

我有一个C#程序集来处理零售促销活动。它能够在7秒内处理包含1,288种合格产品的促销活动。但是,如果负责处理具有大量合格产品的促销,则所花费的时间会随着产品数量呈指数增长。例如,29,962个产品的促销需要7分7秒,而77,350个产品的促销需要39分钟和7秒。

我一直在努力确定程序集中的代码是否可以轻松优化。我将汇编处理设置为最大的促销,然后将性能分析器附加到包含进程(BizTalk主机实例),得到以下报告:

enter image description here

这表明花费最多时间的函数是“GetDataPromoLines”。此函数包含简单的字符串格式。它是从函数“MapForFF”的以下循环中调用的:

foreach (var promoLine in promoLineChunk.PromoLines)
{
    outputFile = outputFile + GetDataPromoLines(promoLine, promoLineNumber+1);
    promoLineNumber++;
}

promoLineChunck.PromoLines是一个描述促销的类的列表,它只包含私有字符串 - 一个用于数据库表的每一列,从中选择了促销详细信息。 “GetDataPromoLines”函数的内容如下所示:

private string GetDataPromoLines(VW_BT_PROMOTIONSRECORDSELECT promoLine, int sequenceNumber)
{
    StringBuilder sb = new StringBuilder();

    string seqNum = sequenceNumber.ToString().PadLeft(5, '0');
    string uniqueNumber = promoLine.CIMS_PROMO_NUMBER + seqNum;

    sb.AppendLine(string.Format("PromoDiscount,\"{0}\",\"{1}\",\"{2}\",\"{3}\",\"{4}\",\"{5}\",\"{6}\",\"{7}\"",
        uniqueNumber,
        promoLine.CIMS_PROMO_NAME,
        promoLine.TYPE,
        promoLine.DESCRIPTION_,
        promoLine.DISCOUNTLEVEL,
        promoLine.COUPONNUMBERMIN,
        promoLine.COUPONNUMBERMAX,
        promoLine.COUPONNUMBERLENGTH
        ));

    sb.AppendLine(string.Format("ItemReq,\"{0}\",\"{1}\",\"{2}\",\"{3}\",\"{4}\",\"{5}\",\"{6}\",\"{7}\",\"{8}\"",
        "00001",
        promoLine.IDENTITYTYPE,
        promoLine.ITEMNUM,
        promoLine.DIVISIONNUM,
        promoLine.DEPARTMENTNUM,
        promoLine.DEPTGROUPNUM,
        promoLine.CLASSNUM,
        promoLine.ITEMGROUPNUM,
        promoLine.IR_QUANTITY
        ));

    sb.AppendLine(string.Format("TierDefinition,\"{0}\",\"{1}\",\"{2}\",\"{3}\",\"{4}\",\"{5}\",\"{6}\",\"{7}\",\"{8}\"",
        "00001",
        promoLine.THRESHOLDTYPE,
        promoLine.THRESHOLDQTY,
        promoLine.THRESHOLDAMT,
        promoLine.DISCTYPE,
        promoLine.DISCPCT,
        promoLine.DISCAMT,
        promoLine.DISCAPPLIESTO,
        promoLine.DISCQTY,
        promoLine.ADDLINFO
        ));

    return sb.ToString();
}

任何人都可以建议导致处理时间呈指数增长的原因吗?是否与CLR拆箱有关?

1 个答案:

答案 0 :(得分:4)

outputFile = outputFile + GetDataPromoLines(promoLine, promoLineNumber+1);

是否尝试通过附加字符串来构建整个输出文件?这是你的施莱米尔。

对于这样的情况,你真的想使用StringBuilder(甚至更好,使用StreamWriter或其他东西直接输出到文件流中):

StringBuilder outputFile;

foreach (var promoLine in promoLineChunk.PromoLines)
{
  outputFile.Append(GetDataPromoLines(promoLine, promoLineNumber+1));
  promoLineNumber++;
}

简单附加的问题是string在.NET中是不可变的 - 每次修改它时,它都会被复制。对于诸如输出大量文本文件之类的东西,这当然是非常昂贵的 - 你花费大部分时间来复制没有改变的字符串部分。

同样,不要sb.AppendLine(string.Format(...)); - 只需使用sb.AppendFormat。理想情况下,将StringBuilder作为参数传递,以避免必须复制这些行本身 - 尽管这应该是outputFile += ...旁边相对无关紧要的性能。

作为旁注,在解释分析结果时要小心 - 这通常会产生微妙的误导。在您的情况下,我非常确定您的问题不在GetDataPromoLines本身(尽管可以改进,如上所示),但在outputFile += ...中。仅查看具有最高独占样本的函数是不够的。仅仅看一下热门路径也是不够的,尽管这已经是一个巨大的升级,通常会引导你直接到需要注意的地方。另外,要了解采样和检测之间的区别 - 采样通常会导致您尝试优化一种本身并不是真正的性能问题的方法 - 相反,它根本不应该像它那样经常被调用。不要将探查器结果用作眼罩 - 你仍然需要注意实际上是什么使感觉