【问题标题】:More efficent ways of cleanly dealing with a multi-dimensional array keyd by enums通过枚举干净地处理多维数组键的更有效方法
【发布时间】:2011-11-04 00:35:35
【问题描述】:

我有一种情况,我需要执行许多获取/设置,在性能分析之后,这是我的应用程序中更昂贵的部分之一。最初我使用的是字典,但在内部切换到锯齿状数组,这给它带来了显着的性能提升,但我仍然想看看是否有一种方法可以在不放弃漂亮且可用的语法的情况下提高此代码的性能。

注意调用 Convert.ToInt32 比调用强制转换要昂贵得多,并且由于通用约束 TStatus : int 不适用于枚举,我必须将其实现为抽象类,如果这个集合能够使用任何开箱即用的枚举。

我还尝试为 IEnumerable 实现 yield,但这实际上比仅填充列表要慢。

    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;

【问题讨论】:

  • 能否也给ILoanStatusVectorOverTime的代码
  • 公共接口 ILoanStatusVectorOverTime : IEnumerable, double>> where TStatus: struct { double this[TStatus status, int monthsSince] { get;放; } double SumOverAll(int monthsSince = 1); ILoanStatusVectorOverTime Copy(); }
  • @Jason Lind:您可以通过单击“编辑”按钮为您的问题添加额外的代码。 (这样阅读界面会容易得多。)
  • 我没有看到锯齿状数组

标签: c# performance generics collections


【解决方案1】:

如果 enum 到 int 的转换占用了您的大量时间,请确保不要在您的内部循环的每次迭代中进行转换。以下是为您的 SumOverStatus 方法缓存转换的示例:

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;
}

额外提示:虽然它可能不会提高性能,但您可以通过使用Func&lt;TStatus, int&gt; 转换器来避免使您的类抽象。以下是如何将转换器公开为属性(构造函数参数也可以正常工作):

public class LoanStatusVectorOverTime<TStatus>
{
    public Func<TStatus, int> GetKeyValue { get; set; }
}

// When the object gets instantiated
loanStatusVectorOverTime.GetKeyValue = status => (int)status;

【讨论】:

  • 上帝对代表的建议,尽管我不确定是否每次都必须调用它。至于 sum over status,另一个很好的建议虽然很少被调用,但 get/set 在大多数情况下都是所谓的。
【解决方案2】:

听起来你有两个不同的问题:

  1. 当使用抽象方法或委托进行转换时,从枚举转换为整数会产生开销。您有两种选择:

    A.从您的类中取出泛型参数并对枚举类型进行硬编码(如有必要,制作该类的多个副本)。

    B.让您的访问器采用两个整数而不是一个枚举和一个整数(让客户端进行从枚举到整数的廉价转换)。

  2. 在获取/设置期间使用了大量时间。这可能不是因为 get/set 效率低下,而是因为 get/set 被调用太多次。建议:

    A.按月或按状态对操作进行分组,重构数据结构(可能使用嵌套数组),并编写高效循环。

    B.降低执行所有获取和设置的代码的计算复杂度。通过退后一步来规划您的程序,您可能会发现更有效的算法。

【讨论】:

    猜你喜欢
    • 2021-06-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多