c# - 抛出异常的性能注意事项(重构此模式的最佳方法)
问题描述
我不确定我是否需要在这里或在https://softwareengineering.stackexchange.com/上问这个问题,但让我们从我目前所拥有的开始。
现在的情况
我正在维护一个数据转换器,它正在逐个记录转换,其中很多(30M+)。我从旧数据库收到一个 id,插入后我有了新的主键。这些键将存储在某种字典中,因此当我需要查找新 ID 时,我可以找到它。这是通过以下代码完成的(简化的示例代码,不是真实的)
public class PersonConverter : Converter
{
public bool Convert(int oldPersonId /*some more parameters*/)
{
int newPersonId;
try
{
newPersonId = GetNewPersonIdForPerson(oldPersonId);
}
catch (SourceKeyNotFoundException)
{
SomeLogging("Log message");
return true;
}
//lots of more thing are happening here!
return true;
}
}
public class Converter
{
private Dictionary<int,int> _convPersonId;
protected int GetNewPersonIdForPerson(int oldPersonId)
{
int key = oldPersonId;
if (_convPersonId.TryGetValue(key, out int newPersonId))
return newPersonId;
throw new SourceKeyNotFoundException(key.ToString());
}
protected void SomeLogging(string message)
{
//Implement logging etc...
}
}
在这里,PersonConverter
我们只有一个调用,GetNewPersonIdForPerson(oldPersonId)
但在实际代码中,有很多对不同字典的调用。如您所见,我的前任喜欢抛出异常。就性能而言,这并不理想,根据微软关于异常和性能的网站,他们建议Tester-Doer Pattern
使用Try-Parse pattern
解决方案
解决方案 1
我想出的解决方案是GetNewPersonIdForPerson(oldPersonId)
返回一个int.MinValue
或其他确定性值,而不是try/catch block
使用if/else block
检查该值。
抢夺新方法GetNewPersonIdForPerson2(int oldPersonId)
protected int GetNewPersonIdForPerson2(int oldPersonId)
{
int key = oldPersonId;
if (_convPersonId.TryGetValue(key, out int newPersonId))
return newPersonId;
return int.MinValue;
}
以及我调用它的方式而不是try/catch block
内部的Convert
方法PersonConverter
if(GetNewPersonIdForPerson2(oldPersonId) != int.MinValue)
{
newPersonId = GetNewPersonIdForPerson(oldPersonId);
}
else
{
SomeLogging("Log message");
return true;
}
这个解决方案有一些问题,因为我需要调用GetNewPersonIdForPerson2
两次事件,尽管我认为性能方面这比抛出异常要快。
解决方案 2
另一种解决方案是在方法上有一个 out 变量,GetNewPersonIdForPerson
如下所示
protected bool GetNewPersonIdForPerson3(int oldPersonId, out int returnPersonId)
{
int key = oldPersonId;
if (_convPersonId.TryGetValue(key, out returnPersonId))
return true;
return false;
}
并在 PersonConverter 的 Convert 方法中执行以下操作
if (!GetNewPersonIdForPerson3(oldPersonId, out newPersonId))
{
SomeLogging("Log message");
return true;
}
我还没有做任何事情来重构这个,因为我想要一些关于什么是最好的解决方案的输入。我更喜欢Solution 1
,因为这更容易重构,但在同一个字典中有 2 次查找。我不能确切地说出Try/Catch blocks
我有多少,但有很多。并且该GetNewPersonIdForPerson
方法不是我唯一拥有的(20+)不知道确切的方法。
问题
谁能告诉我解决这个问题的好模式是什么,或者是我想出的最好的两个解决方案之一。
PS:大转换时,根据性能计数器会抛出 750 万次以上的异常# of Exceps Thrown
PS 2:这只是一些示例代码,字典与此示例不同,它永远存在。
解决方案
如果您要使用大部分或全部,Persons
最好在启动应用程序时(或任何其他运行良好的时间)在初始加载中加载所有用户,然后每次快速查找你需要它。
性能方面TryGetValue
很快,您不需要进行任何优化。请参阅此处的基准:什么更有效:字典 TryGetValue 或 ContainsKey+Item?
在正常情况下Try-Catch
,不应该像这个线程中讨论的那样昂贵的性能:
一般来说,在今天的实现中,输入一个 try 块一点也不昂贵(这并不总是正确的)。但是,抛出和处理异常通常是一项相对昂贵的操作。因此,异常通常应该用于异常事件,而不是正常的流控制。
我们预计GetNewPersonIDForPerson()
失败的频率是多少?
有了这些信息,我会使用类似的东西:
public class PersonConverter : Converter
{
private Dictionary<int, int> _IDs;
public PersonConverter()
{
this._IDs = new Dictionary<int, int>();
}
public int Convert(int oldPersonID)
{
int newPersonID = int.MinValue;
if (this._IDs.TryGetValue(oldPersonID, out newPersonID))
{
/* This oldPerson has been looked up before.
* The TryGetValue is fast so just let's do that and return the newPersonID */
return newPersonID;
}
else
{
try
{
/* This oldPerson has NOT been looked up before
* so we need to retrieve it from out source and update
* the dictionary */
int newPersonID = GetNewPersonIDForPerson(oldPersonID);
this._IDs.Add(oldPersonID, newPersonID);
return newPersonID;
}
catch (SourceKeyNotFoundException)
{
throw;
}
}
}
}
但是,如果您仍然担心该Try-Catch
声明,我可能会检查该GetNewPersonIDForPerson
方法。如果它对数据库执行查询,如果 的记录oldPersonID
不存在,它可能会返回一些值(0 或 -1),并根据该数据创建我的逻辑Tester-Doer
——而不是 using Try-Catch
。
我也可能会进行基准测试,看看Try-Catch
在这种情况下使用语句是否有什么大的不同。如果执行时间有很大差异,我会使用最快的,如果差异可以忽略不计,我会使用最容易理解和维护的代码。
如果真的不值得,我们会尝试优化很多时间。
推荐阅读
- java - 方法java中的基本计数器arraylist
- swift - 以编程方式约束时uiview不绘图
- r - qplot 函数中的 facets 参数
- arrays - 如何随机选择五个不重复的按钮并将它们复制到panel1?
- python - 有没有办法在散点图中以相等的间隔进行可视化,跳过月份的第一个日期碰撞间隔
- c++ - QDialog自行关闭,我该如何解决?
- python - 让 chromedriver 在 Ubuntu AWS 上工作的问题
- python - 如何在 Python 中使用 PDFKit 发送 cookie?
- android - 编译时出错:ERROR: Cause: compileSdkVersion is not specified
- python - 为什么这个函数只返回列表中的第一项?