我有一个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是一个描述促销的类的列表,它只包含私有字符串 - 一个用于数据库表的每一列,从中选择了促销详细信息。 “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拆箱有关?
答案 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 += ...
中。仅查看具有最高独占样本的函数是不够的。仅仅看一下热门路径也是不够的,尽管这已经是一个巨大的升级,通常会引导你直接到需要注意的地方。另外,要了解采样和检测之间的区别 - 采样通常会导致您尝试优化一种本身并不是真正的性能问题的方法 - 相反,它根本不应该像它那样经常被调用。不要将探查器结果用作眼罩 - 你仍然需要注意实际上是什么使感觉。