c# - 将数组映射到定义的段/字段
问题描述
我有一个类型的数组double[]
,我希望能够映射到预定义的段中。每个段都与一组double
值有关,这些值可以通过自定义类型适配器与任意类型进行序列化。例如,考虑以下数组:
var data = new double[] {
0, 0, 1, 0, 0, 0, 0, // DayOfWeek (Deserializes to DayOfWeek.Tuesday)
0.5, // Deserializes to double
0.3, 0.7, // Deserializes to Tuple<double,double>
};
有了这些数据,我希望能够将每组数组元素拆分为适当的Memory<double>
对象。
我目前的计划是拥有三个包装器结构:Field
、FieldSet
和MappedArray
,它们目前是这样定义的(但仍在进行中):
public readonly struct Field
{
public readonly Guid Id;
public readonly string Name;
public readonly double Value;
private readonly int _hash;
public Field( string name )
{
Id = Guid.NewGuid();
Name = name;
Value = default;
_hash = Name.GetHashCode();
}
private Field( Field template, double value )
{
Id = template.Id;
Name = template.Name;
Value = value;
_hash = template._hash;
}
}
public struct FieldSet
{
public readonly Guid Id;
public readonly string Name;
public readonly Field[] Fields;
private readonly Memory<double> _data;
private readonly int _hash;
public double this[ int index ] => _data[ index ];
public Field this[ string name ]
{
get => throw new NotImplementedException();
}
public Field this[ Field field ]
{
get => throw new NotImplementedException();
}
public FieldSet( string name, Field[] fields )
{
Id = Guid.NewGuid();
Name = name;
Fields = fields;
_data = null;
_hash = name.GetHashCode();
}
private FieldSet( FieldSet template, Memory<double> data )
{
Id = template.Id;
Name = template.Name;
fields = template.Fields;
_data = data;
_hash = template._hash;
}
}
public struct MappedArray
{
private Memory<double> _data;
public double this[ int index ] => _data[ index ];
public FieldSet this[ string name ]
{ // Lookup via name
get => throw new NotImplementedException();
}
public FieldSet this[ FieldSet set ]
{ // Lookup via Id
get => throw new NotImplementedException();
}
}
有了这些,我有几个目标:
- 双向。例如,可以获取类型并将它们转换为 double[] 数组。这不是该问题的优先级和范围,但可能有助于考虑。
- 能够按名称、索引和 Id 对
FieldSet
s 和Field
s 进行索引(避免字符串比较以提高性能) - 不惜一切代价避免分配,除非它是不可避免的(例如,如果可能的话能够重用
Field/FieldSet
。
第二个目标是我最挣扎的目标。这个映射系统将在非常热的代码路径上运行(实时神经网络输入/输出),我担心 GC 在所有分配和解除分配方面都会遇到困难,所以我会喜欢尽可能重用Field
,FieldSet
因为它们的目的是将数组抽象为易于使用的结构。
有了这个,主要问题是:我如何设计它以便可以重用“模式”结构?本质上,拥有定义架构的静态FieldSet
和Field
实例,然后能够访问这些段,所有这些都无需分配和解除分配。在下面@Pressacco 的回复中,浮现在脑海中的是享元模式,在哪里回收,并FieldSet
附加了 a。Field
Memory<double>
解决方案
这绝不是答案,但希望它能为您指明正确的方向。
反序列化
我将从查看System.Runtime
名称空间开始。特别是System.Runtime.InteropServicesStructLayout
中的属性。几点注意事项:
- 考虑创建一个沙盒应用程序以确保您获得预期的性能。
StructLayout
有它的限制...确保它们不会影响你。
表现
考虑创建一个资源池,您可以在其中回收对象实例。如果您不分配和取消分配内存......那么您将不会受到性能影响。我相信设计模式可以称为Object Pool。
伪代码看起来像
var message = resourcePool.GetInstance();
MessageFactory.Create(message, serializedData);
...
// do work
...
resourcePool.ReturnInstance(message); // the allocated memory can now be used to de-serialize a future message
最后,尽量避免进行过早的性能优化。