理解VS性能分析

Understanding VS performance analysis

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

我一直在尝试确定程序集中是否有可以轻松优化的代码。我将程序集处理设置为最大的提升,然后将性能分析器附加到包含进程(BizTalk 主机实例),生成以下报告:

这表明花费最多时间的函数是 "GetDataPromoLines"。此函数包含简单的字符串格式。它是从函数 "MapForFF":

的以下循环中调用的
foreach (var promoLine in promoLineChunk.PromoLines)
{
    outputFile = outputFile + GetDataPromoLines(promoLine, promoLineNumber+1);
    promoLineNumber++;
}

promoLineChunck.PromoLines 是描述促销活动的 class 的列表,它仅包含私有字符串 - 一个用于数据库 table 的每一列,促销详情来自选择。 "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 拆箱有关吗?

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

这是试图通过附加字符串来构建整个输出文件吗?有你的 Schlemiel。

对于这种情况,你真的想使用 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 += ...。只看独家样本最高的功能是不够的。仅仅查看热路径也是不够的,尽管这已经是一个巨大的提升,通常会直接将您引向需要您注意的地方。此外,了解采样和检测之间的区别 - 采样通常会导致您尝试优化一种本身并不是真正性能问题的方法 - 相反,它不应该像现在这样经常调用。不要将探查器结果用作蒙眼布 - 您仍然需要注意真正使 sense.

的因素