C# 存储大量数字字段的最佳数据结构

C# 存储大量数字字段的最佳数据结构,c#,data-structures,C#,Data Structures,我正在使用一个类,比如Widget,它有大量真实世界的数字属性(例如,身高、长度、体重、成本等)。有不同类型的小部件(链轮、齿轮等),但每个小部件共享完全相同的属性(当然,小部件的值不同,但它们都有权重、权重等)。我有1000个每种类型的小部件(1000个齿轮、1000个链轮等) 我需要对这些属性进行大量计算(比如计算1000个不同小部件的属性的加权平均值)。对于加权平均值,我对每个小部件类型有不同的权重(即,我可能更关心链轮的长度,而不是齿轮的长度) 现在,我将所有属性存储在每个小部件内的Di

我正在使用一个类,比如Widget,它有大量真实世界的数字属性(例如,身高、长度、体重、成本等)。有不同类型的小部件(链轮、齿轮等),但每个小部件共享完全相同的属性(当然,小部件的值不同,但它们都有权重、权重等)。我有1000个每种类型的小部件(1000个齿轮、1000个链轮等)

我需要对这些属性进行大量计算(比如计算1000个不同小部件的属性的加权平均值)。对于加权平均值,我对每个小部件类型有不同的权重(即,我可能更关心链轮的长度,而不是齿轮的长度)

现在,我将所有属性存储在每个小部件内的Dictionary中(小部件有一个枚举,指定它们的类型:cog、sprocket等)。然后我有一些计算器类,它们将每个属性的权重存储为Dictionary>。为了计算每个小部件的加权平均值,我只需遍历其属性字典键,如下所示:

double weightedAvg = 0.0;
foreach (string attibuteName in widget.Attributes.Keys)
{
    double attributeValue = widget.Attributes[attributeName];
    double attributeWeight = calculator.Weights[widget.Type][attributeName];
    weightedAvg += (attributeValue * attributeWeight);
}
因此,它工作良好,可读性好,易于维护,但对于基于某些评测的1000多个小部件来说,速度非常慢。我的属性名的范围是已知的,并且在应用程序的生命周期中不会改变,所以我想知道有哪些更好的选项。我能想到的几个:

1) 将属性值和权重存储在双[]s中。我认为这可能是最有效的选择,但是我需要确保数组在小部件和计算器之间总是以正确的顺序存储。这还将数据与元数据分离,因此我需要在某个位置存储一个数组(?),该数组在属性名称和索引之间映射为属性值和权重的double[]

2) 在不可变结构中存储属性值和权重。我喜欢这个选项,因为我不必担心排序问题,而且数据是“自我记录的”。但是有没有一种简单的方法可以在代码中循环这些属性呢?我有将近100个属性,所以我不想硬编码代码中的所有属性。我可以使用反射,但我担心这会导致更大的惩罚,因为我循环了这么多小部件,并且必须在每个小部件上使用反射


还有其他选择吗?

数据规范化通常都是这样,选择规范化级别决定了性能的一部分。看起来您必须从当前模型转换到另一个模型或混合模型


如果不使用C#端处理,而是使用数据库处理,则场景的性能可能会更好。然后,您就可以享受索引带来的好处,除了想要的结果之外,没有数据传输,再加上已经花费在性能优化上的100000工时。

三种可能性立即浮现在脑海中。第一个,我认为你太容易拒绝了,就是在你的类中有单独的字段。也就是说,名为
高度
长度
重量
成本
,等等的单个
双倍
值。您是对的,进行计算需要更多的代码,但您没有字典查找的间接方式

第二种方法是放弃字典而使用数组。因此,与其使用
字典,不如使用
双[]
。再一次,我认为你太快拒绝了。您可以轻松地用枚举替换字符串字典键。所以你会:

enum WidgetProperty
{
    First = 0,
    Height = 0,
    Length = 1,
    Weight = 2,
    Cost = 3,
    ...
    Last = 100
}
考虑到这一点和一个
double
数组,您可以轻松浏览每个实例的所有值:

for (int i = (int)WidgetProperty.First; i < (int)WidgetProperty.Last; ++i)
{
    double attributeValue = widget.Attributes[i];
    double attributeWeight = calculator.Weights[widget.Type][i];
    weightedAvg += (attributeValue * attributeWeight);
}

使用.NET4及更高版本支持的数据并行性

摘自上述链接


当并行循环运行时,TPL对数据源进行分区,以便循环可以同时在多个部分上运行。在幕后,任务调度器根据系统资源和工作负载对任务进行分区。如果可能,调度程序会在多个线程和处理器之间重新分配工作,如果工作负载变得不平衡,则计算加权平均值而不进行循环或反射,一种方法是计算各个属性的加权平均值并将其存储在某个位置。这应该在创建小部件实例时发生。下面是需要根据您的需要修改的示例代码。 此外,对于小部件本身的进一步处理,您可以使用数据并行性。请参阅此线程中的其他响应

public enum WidgetType { }

public class Claculator { }

public class WeightStore
{
    static Dictionary<int, double> widgetWeightedAvg = new Dictionary<int, double>();
    public static void AttWeightedAvgAvailable(double attwightedAvg, int widgetid)
    {
        if (widgetWeightedAvg.Keys.Contains(widgetid))
            widgetWeightedAvg[widgetid] += attwightedAvg;
        else
            widgetWeightedAvg[widgetid] = attwightedAvg;
    }
}

public class WidgetAttribute
{
    public string Name { get; }
    public double Value { get; }
    public WidgetAttribute(string name, double value, WidgetType type, int widgetId)
    {
        Name = name;
        Value = value;
        double attWeight = Calculator.Weights[type][name];
        WeightStore.AttWeightedAvgAvailable(Value*attWeight, widgetId);
    }
}

public class CogWdiget
{
    public int Id { get; }
    public WidgetAttribute height { get; set; }
    public WidgetAttribute wight { get; set; }
}

public class Client
{
    public void BuildCogWidgets()
    {
        CogWdiget widget = new CogWdiget();
        widget.Id = 1;
        widget.height = new WidgetAttribute("height", 12.22, 1);
    }
}
公共枚举WidgetType{}
公共类计算器{}
公共类商店
{
静态字典widgetWeightedAvg=新字典();
公共静态无效attweightedavga可用(双attwightedAvg,int-widgetid)
{
if(widgetWeightedAvg.Keys.Contains(widgetid))
widgetWeightedAvg[widgetid]+=attwightedAvg;
其他的
widgetWeightedAvg[widgetid]=attwightedAvg;
}
}
公共类WidgetAttribute
{
公共字符串名称{get;}
公共双值{get;}
公共WidgetAttribute(字符串名称、双值、WidgetType类型、int-widgetId)
{
名称=名称;
价值=价值;
double attWeight=计算器。权重[类型][名称];
WeightStore.AttWeightedAvgAvailable(值*attWeight,widgetId);
}
}
公共类CogWdiget
{
公共int Id{get;}
公共WidgetAttribute高度{get;set;}
公共WidgetAttribute wight{get;set;}
}
公共类客户端
{
公共void BuildCogWidgets()
{
CogWdiget小部件=新的CogWdiget();
widget.Id=1;
widget.height=新的WidgetAttribute(“height”,12.22,1);
}
}

当您说需要循环100多个属性时,您的意思是每个类都有100个属性吗?或者,您的意思是必须在一个数据库上循环100个类实例
public enum WidgetType { }

public class Claculator { }

public class WeightStore
{
    static Dictionary<int, double> widgetWeightedAvg = new Dictionary<int, double>();
    public static void AttWeightedAvgAvailable(double attwightedAvg, int widgetid)
    {
        if (widgetWeightedAvg.Keys.Contains(widgetid))
            widgetWeightedAvg[widgetid] += attwightedAvg;
        else
            widgetWeightedAvg[widgetid] = attwightedAvg;
    }
}

public class WidgetAttribute
{
    public string Name { get; }
    public double Value { get; }
    public WidgetAttribute(string name, double value, WidgetType type, int widgetId)
    {
        Name = name;
        Value = value;
        double attWeight = Calculator.Weights[type][name];
        WeightStore.AttWeightedAvgAvailable(Value*attWeight, widgetId);
    }
}

public class CogWdiget
{
    public int Id { get; }
    public WidgetAttribute height { get; set; }
    public WidgetAttribute wight { get; set; }
}

public class Client
{
    public void BuildCogWidgets()
    {
        CogWdiget widget = new CogWdiget();
        widget.Id = 1;
        widget.height = new WidgetAttribute("height", 12.22, 1);
    }
}