编译最佳运行时性能的方法

发布于 2025-01-27 14:40:22 字数 1636 浏览 4 评论 0原文

我试图确定哪种最佳方法获得了各种对象的完全优化代表,以提高序列化速度。简而言之:我想删除各种不同的检查,并在我的应用程序开始时一次编译更有效的序列化功能。

让我们来看看这个简单的例子:

public class GamePacket
{
    [Length(10)]
    [ReadBackwards]
    public string Id { get; set; }
}

现在,我可能会创建一个序列化器,出于性能原因,将属性存储在缓存的字段中。每次我想从流(或字节数组)中对游戏包装进行估算,我都会称之为:

Deserialize(byte[] stream)
{
    var header = stream.ReadByte();
    var packet = cachedDeserializers[header];

    var instance = packet.DelegateForCreateInstance();

    foreach (var field in packet.Fields)
    {
        if (field.Type != TypeCode.String) continue;

        var str = stream.ReadBytes(field.LengthAttribute.Length);

        if (field.HasReadBackwardsAttribute)
            str = str.Reverse();

        field.DelegateForSetValue(instance, str);
    }
     
}

现在的问题在于,每次我在该流上进行调用时,我都需要循环并检查各种属性和其他检查之类的东西。在示例中,这些事情可能会被省略(也许还有更多):

  • if(field.type!= typecode.string)继续;
  • if(field.hasreadbackwardsattribute)

如果我知道该字段具有向后读取属性,我想在应用程序启动时编译一个简化的代表,以省略这些检查,然后简单地向后读取它。是否可以创建一个可以删除不需要逻辑的委托?例如:

Deserialize(byte[] stream)
{
    var header = stream.ReadByte();
    var packet = cachedDeserializers[header];

    var instance = packet.CallCachedCompile(stream);
}

// CallCachedCompile for GamePacket would look something like this:
CallCachedCompile(byte[] stream)
{
    var instance = this.DelegateForCreateInstance();
    var str = stream.ReadBytes(10);
    str = str.Reverse();
    this.DelegateForSetValue(instance, "Id", str);
    return instance;
}

我简要介绍了表达树。像这样的事情在表达树上可以做到吗?最有效的方法是什么?

I'm trying to determine what the best way to get fully optimized delegates for various objects to improve the speed of serialization. Simply put: I'd like to remove various different checks, and compile more efficient serialize functions one time at the start of my app.

Let's take a look at this simple example:

public class GamePacket
{
    [Length(10)]
    [ReadBackwards]
    public string Id { get; set; }
}

Now, I'd likely create a serializer, and for performance reasons store the attributes in a cached field. Everytime I want to deserialize a GamePacket from a stream (or byte array), I'd call something like:

Deserialize(byte[] stream)
{
    var header = stream.ReadByte();
    var packet = cachedDeserializers[header];

    var instance = packet.DelegateForCreateInstance();

    foreach (var field in packet.Fields)
    {
        if (field.Type != TypeCode.String) continue;

        var str = stream.ReadBytes(field.LengthAttribute.Length);

        if (field.HasReadBackwardsAttribute)
            str = str.Reverse();

        field.DelegateForSetValue(instance, str);
    }
     
}

The problem now lies in the fact that EVERY time I'm calling Deserialize on that stream, I need to loop through and check various things like attributes, and other checks. In the example, these things can potentially be omitted (And maybe more):

  • if (field.Type != TypeCode.String) continue;
  • if (field.HasReadBackwardsAttribute)

If I know the field has a read backwards attribute, I'd like to compile a simplified delegate on app start that omits these checks, and simply reads it backwards. Is it possible to create a delegate that can remove unneeded logic? For example:

Deserialize(byte[] stream)
{
    var header = stream.ReadByte();
    var packet = cachedDeserializers[header];

    var instance = packet.CallCachedCompile(stream);
}

// CallCachedCompile for GamePacket would look something like this:
CallCachedCompile(byte[] stream)
{
    var instance = this.DelegateForCreateInstance();
    var str = stream.ReadBytes(10);
    str = str.Reverse();
    this.DelegateForSetValue(instance, "Id", str);
    return instance;
}

I've looked briefly into expression trees. Would something like this be doable in expression Trees? What would be the most efficient way?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

娜些时光,永不杰束 2025-02-03 14:40:22

是的,使用代码生成方法可以为特定类型生成委托。因此,代替这种通用反射式代码:

foreach (var field in packet.Fields)
{
    if (field.Type != TypeCode.String) continue;

    var str = stream.ReadBytes(field.LengthAttribute.Length);

    if (field.HasReadBackwardsAttribute)
        str = str.Reverse();

    field.DelegateForSetValue(instance, str);
}

您可以为特定类型生成代码:

gamePacketInstance.Id = SomeConvertionToString(stream.ReadBytes(field.LengthAttribute.Length).Revers());

代码生成主题很大,我不知道您在代表内部做什么。您可以在运行时(发射IL或表达式树)或在编译时间(源生成器)中生成特定的委托。我建议您阅读我的文章。它将为示例提供良好的概述。

Yes, using code generation approach you can generate delegates for the particular type. So instead of this generic reflection-like code:

foreach (var field in packet.Fields)
{
    if (field.Type != TypeCode.String) continue;

    var str = stream.ReadBytes(field.LengthAttribute.Length);

    if (field.HasReadBackwardsAttribute)
        str = str.Reverse();

    field.DelegateForSetValue(instance, str);
}

You can generate code for a specific type:

gamePacketInstance.Id = SomeConvertionToString(stream.ReadBytes(field.LengthAttribute.Length).Revers());

The code generation topic is quite big and I don't know what exactly you do inside your delegates. You can generate specific delegates in runtime (emit il or expression trees) or in compile time (source generators). I suggest you to read my article Dotnet code generation overview by example. It will give good overview with examples.

也只是曾经 2025-02-03 14:40:22

如果不使用任何代码生成,您仍然可以非常有效地执行此操作。

基本上,您需要使用仿制药和多态性来缓存您想要为遇到的每种类型所做的所有代码。

请记住,属性具有潜在的方法,我们可以创建代表以设置属性而无需使用代码生成。

abstract class DeserializerBase
{
    object DeserializePacket(Stream stream);
}

class Deserializer<T> : DeserializerBase where T : new()
{
    FieldAction<T>[] fieldActions =
        typeof(T).GetProperties()
        .Where(p => p.Type == TypeCode.String)
        .Select(p => IsReverseAttribute(p)
                     ? new FieldActionReverse<T>
                     {
                         Setter = p.SetMethod.CreateDelegate<Action<T, string>>(),
                         Length = GetLengthAttribute(p),
                     }
                     : new FieldAction<T>
                     {
                         Setter = p.SetMethod.CreateDelegate<Action<T, string>>(),
                         Length = GetLengthAttribute(p),
                     })
        .ToArray();

    object DeserializePacket(Stream stream);
    {
        var packet = new T();
        foreach (var action in fieldActions)
            action.Deserialize(packet);
    }
}

class FieldAction<T>
{
    public Action<T, string> Setter;
    public int Length;

    void Deserialize(Stream stream, T instance)
    {
        var str = ReadString(stream);
        Setter(instance, str);
    }

    virtual string GetString(Stream stream)
    {
        return stream.ReadBytes(Length);
    }
}

class FieldActionReverse<T> : FieldAction<T>
{
    override string GetString(Stream stream)
    {
        return stream.ReadBytes(Length).Reverse();
    }
}

您的最终进入代码成为此。

Dictionary<int, DeserializerBase> cachedDeserializers = new Dictionary<int, DeserializerBase>
    {
        {5, new Deserializer<GamePacket>()}
    };

Deserialize(Stream stream)
{
    var header = stream.ReadByte();
    var packet = cachedDeserializers[header].DeserializePacket(stream);
}

您甚至可以将通用约束放在t上,以确保它是数据包,然后您可以从此条目函数返回基本packet键入。

Without using any code generation you can still do this pretty efficiently.

You basically need to use generics and polymorphism to cache all code that you want done for each type that you encounter.

Bearing in mind that properties have underlying methods, we can create delegates to set properties without using code generation.

abstract class DeserializerBase
{
    object DeserializePacket(Stream stream);
}

class Deserializer<T> : DeserializerBase where T : new()
{
    FieldAction<T>[] fieldActions =
        typeof(T).GetProperties()
        .Where(p => p.Type == TypeCode.String)
        .Select(p => IsReverseAttribute(p)
                     ? new FieldActionReverse<T>
                     {
                         Setter = p.SetMethod.CreateDelegate<Action<T, string>>(),
                         Length = GetLengthAttribute(p),
                     }
                     : new FieldAction<T>
                     {
                         Setter = p.SetMethod.CreateDelegate<Action<T, string>>(),
                         Length = GetLengthAttribute(p),
                     })
        .ToArray();

    object DeserializePacket(Stream stream);
    {
        var packet = new T();
        foreach (var action in fieldActions)
            action.Deserialize(packet);
    }
}

class FieldAction<T>
{
    public Action<T, string> Setter;
    public int Length;

    void Deserialize(Stream stream, T instance)
    {
        var str = ReadString(stream);
        Setter(instance, str);
    }

    virtual string GetString(Stream stream)
    {
        return stream.ReadBytes(Length);
    }
}

class FieldActionReverse<T> : FieldAction<T>
{
    override string GetString(Stream stream)
    {
        return stream.ReadBytes(Length).Reverse();
    }
}

Your final entry code becomes this.

Dictionary<int, DeserializerBase> cachedDeserializers = new Dictionary<int, DeserializerBase>
    {
        {5, new Deserializer<GamePacket>()}
    };

Deserialize(Stream stream)
{
    var header = stream.ReadByte();
    var packet = cachedDeserializers[header].DeserializePacket(stream);
}

You can even place generic constraints on T to ensure it is a Packet then you can return a base Packet type from this entry function.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文