I have a situation where I need to do many gets/sets, after perfomance analysis this is one of the more expensive parts of my application. Origionally I was using a Dictionary but switched to a jagged array internally which gave it a signficant perfomance boost, still I'd like to see if theres a way to improve the perfomance of this code without ditching the nice and useable syntax.
Note calling Convert.ToInt32 is signficantly more expensive than calling a cast, and since the generic constraint TStatus : int doesn't work for an enum I had to implement this as a abstract class, it would be nice if this collection would work with any enum out of the box.
Also I tried implementing yield for the IEnumerable, however that was actually slower than just populating a list.
public abstract class LoanStatusVectorOverTime<TStatus> : ILoanStatusVectorOverTime<TStatus>
where TStatus: struct
{
protected static readonly TStatus[] LoanStatusTypes = (TStatus[])Enum.GetValues(typeof(TStatus));
protected static readonly int LoanStatusCount = Enum.GetValues(typeof(TStatus)).Length;
protected const int MonthsSinceEventCount = 25;
private readonly object SYNC = new object();
protected double[,] VectorDictionary { get; set; }
public LoanStatusVectorOverTime()
{
this.VectorDictionary = new double[LoanStatusCount, MonthsSinceEventCount];
}
public double this[TStatus status, int monthsSince]
{
get
{
if (monthsSince >= MonthsSinceEventCount)
return 0;
return VectorDictionary[GetKeyValue(status), monthsSince];
}
set
{
if (monthsSince >= MonthsSinceEventCount)
return;
VectorDictionary[GetKeyValue(status), monthsSince] = value;
}
}
public double SumOverStatus(TStatus status)
{
double sum = 0;
foreach (var fromStatus in LoanStatusTypes)
{
int i = 0;
while (i < MonthsSinceEventCount)
{
sum += VectorDictionary[GetKeyValue(fromStatus), i];
i++;
}
}
return sum;
}
public IEnumerator<KeyValuePair<Tuple<TStatus, int>, double>> GetEnumerator()
{
List<KeyValuePair<Tuple<TStatus, int>, double>> data = new List<KeyValuePair<Tuple<TStatus, int>, double>>();
foreach (var fromStatus in LoanStatusTypes)
{
int i = 0;
while (i < MonthsSinceEventCount)
{
var val = VectorDictionary[GetKeyValue(fromStatus), i];
if (val != default(double))
data.Add(new KeyValuePair<Tuple<TStatus, int>, double>(new Tuple<TStatus, int>(fromStatus, i), val));
i++;
}
}
return data.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
protected abstract int GetKeyValue(TStatus status);
protected abstract ILoanStatusVectorOverTime<TStatus> Initalize();
public ILoanStatusVectorOverTime<TStatus> Copy()
{
var vect = Initalize();
foreach (var fromStatus in LoanStatusTypes)
{
int i = 0;
while (i < MonthsSinceEventCount)
{
vect[fromStatus, i] 开发者_如何转开发= VectorDictionary[GetKeyValue(fromStatus), i];
i++;
}
}
return vect;
}
public double SumOverAll(int monthsSince = 1)
{
double sum = 0;
foreach (var status in LoanStatusTypes)
{
sum += this[status, monthsSince];
}
return sum;
}
}
public class AssetResolutionVector : LoanStatusVectorOverTime<AssetResolutionStatus>
{
protected override int GetKeyValue(AssetResolutionStatus status)
{
return (int)status;
}
protected override ILoanStatusVectorOverTime<AssetResolutionStatus> Initalize()
{
return new AssetResolutionVector();
}
}
var arvector = new AssetResolutionVector();
arvector[AssetResolutionStatus.ShortSale, 1] = 10;
If the enum to int conversion is taking up a lot of your time, make sure that don't do the conversion during every iteration of your inner loop. Here's an example of the conversion getting cached for your SumOverStatus
method:
public double SumOverStatus(TStatus status)
{
double sum = 0;
foreach (var fromStatus in LoanStatusTypes)
{
int statusKey = GetKeyValue(fromStatus);
int i = 0;
while (i < MonthsSinceEventCount)
{
sum += VectorDictionary[statusKey, i];
i++;
}
}
return sum;
}
Extra tip: although it may not have give a performance boost, you can avoid making your class abstract by using a Func<TStatus, int>
converter. Here's how the converter could be exposed as a property (a constructor argument would work fine too):
public class LoanStatusVectorOverTime<TStatus>
{
public Func<TStatus, int> GetKeyValue { get; set; }
}
// When the object gets instantiated
loanStatusVectorOverTime.GetKeyValue = status => (int)status;
It sounds like you have two separate problems here:
Converting from an enum to an integer has an overhead when the conversion is being done using an abstract method or a delegate. You have two options here:
A. Take out the generic parameter from your class and hardcode the enum type (making multiple copies of the class if necessary).
B. Have your accessors take two integers instead of an enum and an integer (letting the client do a cheap cast from enum to integer).
A lot of time is being used during get/set. This may not be because get/set are inefficient, but because get/set are being called too many times. Suggestions:
A. Group your operations by month or by status, restructure your data structures (maybe using nested arrays), and write efficient loops.
B. Reduce the computational complexity of the code that is doing all the getting and setting. By stepping back and mapping out your program, you may find more efficient algorithms.
精彩评论