处理foreach循环的执行时间过长
Excessive execution time processing foreach loop
我正在编写一些代码(不是我自己的代码)来更新 UCommerce 中的项目。这个特定的代码块更新了产品的 UDF。我遇到执行速度问题。通过 foreach
循环的每次迭代都比前一次慢。当执行开始时,它每秒循环 5-6 次,但是当它处理了 500 次迭代时,它已经减慢到每 5-6 秒 1 次,现在是 910 项并且每 10-11 秒减慢到 1 次。就代码而言,它在提交事务和刷新会话之前处理 200 (_batchsize
) 个项目。
内存、CPU 和磁盘 IO 看起来都很好。没有过度使用 CPU,有足够的可用内存并且没有磁盘瓶颈。当 运行ning 时,应用内存使用量稳定在 350Mb 左右。
无论是 运行 来自 IDE 还是编译的 exe,我都看到了同样的问题。
我试过减小批大小,我也试过为每个项目提交事务和刷新会话,但这几乎没有什么区别。我只是想知道是否有人可以建议我可以尝试什么,甚至可以确定可能的问题区域。
public class ccProductUDFs
{
public string ProductName { get; set; }
public string FieldName { get; set; }
public string DataType { get; set; }
public string DisplayName { get; set; }
public string SKU { get; set; }
public bool Facet { get; set; }
public bool Searchable { get; set; }
public bool RenderInEditor { get; set; }
public string DefinitionName { get; set; }
public string DefinitionDescription { get; set; }
public string UdfValue { get; set; }
public bool UdfValueHasChanged { get; set; }
public bool UdfFieldHasChanged { get; set; }
public bool UdfFieldDeleted { get; set; }
public bool DisplayOnSite { get; set; }
public string CultureCode { get; set; }
public override string ToString()
{
return string.Format("{0} {1} : {2} = {3}", this.SKU, this.ProductName, this.DisplayName, this.UdfValue);
}
}
//This will load approx. 173000 item for a SQL database
List<ccProductUDFs> listudfs = ccProductUDFsData.Load(DBConfiguration.GetDBContext());
//_batchsize is set to 200
//processUdFs is a Boolean = true stored in AppSettings
#region Update any UDFs...
_startrange = 0;
started = DateTime.Now;
timeTaken = TimeSpan.MinValue;
timeLeft = TimeSpan.MinValue;
doneCount = 0;
_totalCount = listudfs.Count();
while (_processUdFs && _startrange < listudfs.Count())
{
int sz = listudfs.Count() - _startrange >= _batchsize ? _batchsize : listudfs.Count() - _startrange;
List<ccProductUDFs> shallowlist = listudfs.GetRange(_startrange, sz);
_startrange = _startrange + sz;
_session = SessionContext.Session;
// create a response list to hold results of uploads...
List<ccResponse> response_categoryUDFs = new List<ccResponse>();
// start the transaction
using (var tx = _session.BeginTransaction())
{
_dbcontext.BeginTransaction();
int counter = 0;
int listcount = listudfs.Count();
// loop through each remaining UDF
// these are UDFs where the FIELD or the VALUE has changed, and we have not dealt with it as part of the product
foreach (ccProductUDFs udf in shallowlist)
{
if (ShowLog(verbose, (_startrange - sz) + ++counter, listcount))
{
TimeSpan elapsed = (DateTime.Now - started);
WriteLogV2("product udfs {0} of {1}", (_startrange - sz) + counter, listcount, elapsed);
}
// get the product for this UDF...
var product = _session.Query<Product>().FirstOrDefault(a => a.Sku == udf.SKU);
if (product != null)
{
// check that product has a product definition...
if (product.ProductDefinition == null)
{
// product has no definition assigned, so check that the product definition exists in data...
ProductDefinition definition = _session.Query<ProductDefinition>().FirstOrDefault(a => a.Description == udf.DefinitionName);
if (definition == null)
{
// product definition doesn;t exist in data, so create it...
definition = new ProductDefinition();
definition.Description = udf.DefinitionDescription;
definition.Name = udf.DefinitionName;
// save the changes...
_session.SaveOrUpdate((ProductDefinition)definition);
}
// assign this product definition to the product record...
product.ProductDefinition = definition;
}
// determine if the UDF FIELD exists...
ProductDefinitionField definitionfield = product.ProductDefinition.ProductDefinitionFields.FirstOrDefault(a => a.Name == udf.FieldName);
if (definitionfield == null)
{
// the UDF FIELD does NOT exist, so we shall add it.
definitionfield = new ProductDefinitionField();
definitionfield.Name = udf.FieldName;
definitionfield.ProductDefinition = product.ProductDefinition;
// locate the data type record and assign it to this UDF FIELD
var dt = _session.Query<DataType>().FirstOrDefault(a => a.TypeName == udf.DataType);
if (dt != null)
{
definitionfield.DataType = dt;
}
// add the UDF FIELD to the product category...
product.ProductDefinition.ProductDefinitionFields.Add(definitionfield);
// save the changes...
_session.SaveOrUpdate((Product)product);
}
bool changed = definitionfield.Deleted != udf.UdfFieldDeleted;
// assign properties to this UDF FIELD...
definitionfield.Deleted = udf.UdfFieldDeleted;
if (changed)
{
// save the changes...
_session.SaveOrUpdate((ProductDefinitionField)definitionfield);
}
// determine if the UDF VALUE record exists...
ProductProperty property = product.ProductProperties.FirstOrDefault(a => a.ProductDefinitionField.Name == definitionfield.Name && a.Product.Id == product.ProductId);
if (property == null)
{
// the UDF VALUE does NOT exist, so we shall add it.
property = new ProductProperty();
property.ProductDefinitionField = definitionfield;
property.Product = product;
// add the UDF VALUE to the product category...
product.ProductProperties.Add(property);
}
changed = false;
string v = udf.UdfValue == null ? string.Empty : udf.UdfValue.Trim();
changed = property.Value != v;
// assign properties to this UDF FIELD...
property.Value = v;
if (changed)
{
// save the changes...
_session.SaveOrUpdate((ProductProperty)property);
// save the changes...
_session.SaveOrUpdate((Product)product);
}
// update the response with a successful result...
response_categoryUDFs.Add(new ccResponse(udf.SKU, udf.FieldName, ccCategoryUDFsData.Source, true, "", 0));
}
object[] prodparam =
{
_dbcontext.NewParameter("@Sku", udf.SKU),
_dbcontext.NewParameter("@udfName", udf.FieldName)
};
_dbcontext.ExecuteNonQuery(CommandType.Text,
"UPDATE [LOAD_ProductUdfValues] SET HasChanged = 0 WHERE ProductId = @Sku And FieldName = @udfName",
prodparam);
TimeSpan ts = DateTime.Now - started;
doneCount++;
Console.WriteLine("Done {0} of {1} in {2}", doneCount, _totalCount, ts.ToString(@"hh\:mm\:ss"));
}
try
{
// commit all changes...
tx.Commit();
_dbcontext.CommitTransaction();
}
catch (Exception ex)
{
// Error Handler
tx.Rollback();
_dbcontext.RollbackTransaction();
response_categoryUDFs.Clear();
response_categoryUDFs.Add(new ccResponse("", "", ccCatalogData.Source, false, "Commit failed [" + ex.ToString() + "]", 1));
}
}
// send any response...
ccResponse.Write(_dbcontext, response_categoryUDFs, ccCatalogData.ResponseTarget);
// tidy up the session before is it disposed...
_session.Flush();
// }
}
#endregion
如果有人在搜索中找到这个,我已经通过在 _session.Flush();
之后添加 _session.Clear();
来解决我的问题
我正在编写一些代码(不是我自己的代码)来更新 UCommerce 中的项目。这个特定的代码块更新了产品的 UDF。我遇到执行速度问题。通过 foreach
循环的每次迭代都比前一次慢。当执行开始时,它每秒循环 5-6 次,但是当它处理了 500 次迭代时,它已经减慢到每 5-6 秒 1 次,现在是 910 项并且每 10-11 秒减慢到 1 次。就代码而言,它在提交事务和刷新会话之前处理 200 (_batchsize
) 个项目。
内存、CPU 和磁盘 IO 看起来都很好。没有过度使用 CPU,有足够的可用内存并且没有磁盘瓶颈。当 运行ning 时,应用内存使用量稳定在 350Mb 左右。
无论是 运行 来自 IDE 还是编译的 exe,我都看到了同样的问题。
我试过减小批大小,我也试过为每个项目提交事务和刷新会话,但这几乎没有什么区别。我只是想知道是否有人可以建议我可以尝试什么,甚至可以确定可能的问题区域。
public class ccProductUDFs
{
public string ProductName { get; set; }
public string FieldName { get; set; }
public string DataType { get; set; }
public string DisplayName { get; set; }
public string SKU { get; set; }
public bool Facet { get; set; }
public bool Searchable { get; set; }
public bool RenderInEditor { get; set; }
public string DefinitionName { get; set; }
public string DefinitionDescription { get; set; }
public string UdfValue { get; set; }
public bool UdfValueHasChanged { get; set; }
public bool UdfFieldHasChanged { get; set; }
public bool UdfFieldDeleted { get; set; }
public bool DisplayOnSite { get; set; }
public string CultureCode { get; set; }
public override string ToString()
{
return string.Format("{0} {1} : {2} = {3}", this.SKU, this.ProductName, this.DisplayName, this.UdfValue);
}
}
//This will load approx. 173000 item for a SQL database
List<ccProductUDFs> listudfs = ccProductUDFsData.Load(DBConfiguration.GetDBContext());
//_batchsize is set to 200
//processUdFs is a Boolean = true stored in AppSettings
#region Update any UDFs...
_startrange = 0;
started = DateTime.Now;
timeTaken = TimeSpan.MinValue;
timeLeft = TimeSpan.MinValue;
doneCount = 0;
_totalCount = listudfs.Count();
while (_processUdFs && _startrange < listudfs.Count())
{
int sz = listudfs.Count() - _startrange >= _batchsize ? _batchsize : listudfs.Count() - _startrange;
List<ccProductUDFs> shallowlist = listudfs.GetRange(_startrange, sz);
_startrange = _startrange + sz;
_session = SessionContext.Session;
// create a response list to hold results of uploads...
List<ccResponse> response_categoryUDFs = new List<ccResponse>();
// start the transaction
using (var tx = _session.BeginTransaction())
{
_dbcontext.BeginTransaction();
int counter = 0;
int listcount = listudfs.Count();
// loop through each remaining UDF
// these are UDFs where the FIELD or the VALUE has changed, and we have not dealt with it as part of the product
foreach (ccProductUDFs udf in shallowlist)
{
if (ShowLog(verbose, (_startrange - sz) + ++counter, listcount))
{
TimeSpan elapsed = (DateTime.Now - started);
WriteLogV2("product udfs {0} of {1}", (_startrange - sz) + counter, listcount, elapsed);
}
// get the product for this UDF...
var product = _session.Query<Product>().FirstOrDefault(a => a.Sku == udf.SKU);
if (product != null)
{
// check that product has a product definition...
if (product.ProductDefinition == null)
{
// product has no definition assigned, so check that the product definition exists in data...
ProductDefinition definition = _session.Query<ProductDefinition>().FirstOrDefault(a => a.Description == udf.DefinitionName);
if (definition == null)
{
// product definition doesn;t exist in data, so create it...
definition = new ProductDefinition();
definition.Description = udf.DefinitionDescription;
definition.Name = udf.DefinitionName;
// save the changes...
_session.SaveOrUpdate((ProductDefinition)definition);
}
// assign this product definition to the product record...
product.ProductDefinition = definition;
}
// determine if the UDF FIELD exists...
ProductDefinitionField definitionfield = product.ProductDefinition.ProductDefinitionFields.FirstOrDefault(a => a.Name == udf.FieldName);
if (definitionfield == null)
{
// the UDF FIELD does NOT exist, so we shall add it.
definitionfield = new ProductDefinitionField();
definitionfield.Name = udf.FieldName;
definitionfield.ProductDefinition = product.ProductDefinition;
// locate the data type record and assign it to this UDF FIELD
var dt = _session.Query<DataType>().FirstOrDefault(a => a.TypeName == udf.DataType);
if (dt != null)
{
definitionfield.DataType = dt;
}
// add the UDF FIELD to the product category...
product.ProductDefinition.ProductDefinitionFields.Add(definitionfield);
// save the changes...
_session.SaveOrUpdate((Product)product);
}
bool changed = definitionfield.Deleted != udf.UdfFieldDeleted;
// assign properties to this UDF FIELD...
definitionfield.Deleted = udf.UdfFieldDeleted;
if (changed)
{
// save the changes...
_session.SaveOrUpdate((ProductDefinitionField)definitionfield);
}
// determine if the UDF VALUE record exists...
ProductProperty property = product.ProductProperties.FirstOrDefault(a => a.ProductDefinitionField.Name == definitionfield.Name && a.Product.Id == product.ProductId);
if (property == null)
{
// the UDF VALUE does NOT exist, so we shall add it.
property = new ProductProperty();
property.ProductDefinitionField = definitionfield;
property.Product = product;
// add the UDF VALUE to the product category...
product.ProductProperties.Add(property);
}
changed = false;
string v = udf.UdfValue == null ? string.Empty : udf.UdfValue.Trim();
changed = property.Value != v;
// assign properties to this UDF FIELD...
property.Value = v;
if (changed)
{
// save the changes...
_session.SaveOrUpdate((ProductProperty)property);
// save the changes...
_session.SaveOrUpdate((Product)product);
}
// update the response with a successful result...
response_categoryUDFs.Add(new ccResponse(udf.SKU, udf.FieldName, ccCategoryUDFsData.Source, true, "", 0));
}
object[] prodparam =
{
_dbcontext.NewParameter("@Sku", udf.SKU),
_dbcontext.NewParameter("@udfName", udf.FieldName)
};
_dbcontext.ExecuteNonQuery(CommandType.Text,
"UPDATE [LOAD_ProductUdfValues] SET HasChanged = 0 WHERE ProductId = @Sku And FieldName = @udfName",
prodparam);
TimeSpan ts = DateTime.Now - started;
doneCount++;
Console.WriteLine("Done {0} of {1} in {2}", doneCount, _totalCount, ts.ToString(@"hh\:mm\:ss"));
}
try
{
// commit all changes...
tx.Commit();
_dbcontext.CommitTransaction();
}
catch (Exception ex)
{
// Error Handler
tx.Rollback();
_dbcontext.RollbackTransaction();
response_categoryUDFs.Clear();
response_categoryUDFs.Add(new ccResponse("", "", ccCatalogData.Source, false, "Commit failed [" + ex.ToString() + "]", 1));
}
}
// send any response...
ccResponse.Write(_dbcontext, response_categoryUDFs, ccCatalogData.ResponseTarget);
// tidy up the session before is it disposed...
_session.Flush();
// }
}
#endregion
如果有人在搜索中找到这个,我已经通过在 _session.Flush();
_session.Clear();
来解决我的问题