简体   繁体   English

如何避免对象分配? 如何重用对象而不是分配它们?

[英]how to avoid objects allocations? how to reuse objects instead of allocating them?

My financical software processes constantly almost the same objects. 我的财务软件会不断处理几乎相同的对象。 For example I have such data online: 例如,我在线上有这样的数据:

HP 100 1
HP 100 2
HP 100.1 1
etc.

I've about 1000 updates every second. 我每秒大约有1000次更新。

Each update is stored in object - but i do not want to allocate these objects on the fly to improve latency. 每次更新都存储在对象中-但我不想动态分配这些对象以提高延迟。 I use objects only in short period of time - i recive them, apply and free. 我只在短时间内使用对象-我会接受它们,申请并免费使用。 Once object is free it actually can be reused for another pack of data. 对象释放后,实际上可以将其重新用于另一包数据。

So I need some storage (likely ring-buffer) that allocates required number of objects once and them allow to "obtain" and "free" them. 因此,我需要一些存储(可能是环形缓冲区),该存储一次分配所需数量的对象,并且它们允许“获取”和“释放”它们。 What is the best way to do that in c#? 用c#做到这一点的最佳方法是什么?

Each object has id and i assign id's sequentially and free them sequentially too. 每个对象都有id和我指定id's顺序释放她们sequentially了。 For example i receive id's 1 2 and 3 , then I free 1 , 2 , 3 . 例如我接收的ID 1 23 ,然后我释放123 So any FIFO collection would work, but i'm looking for some library class that cover's required functionality. 因此,任何FIFO收集都可以使用,但是我正在寻找一些涵盖所需功能的库类。

Ie I need FIFO collection that do not allocates objects, but reuse them and allows to reconfigure them. 即,我需要不分配对象,而是重用它们并允许重新配置它们的FIFO收集。

upd 更新

I've added my implementation of what I want. 我添加了我想要的实现。 This is not tested code and probably has bugs. 该代码未经测试,可能存在错误。 Idea is simple. 想法很简单。 Writer should call Obtain Commit methods. 编写者应调用Obtain Commit方法。 Reader should call TryGet method. 读者应调用TryGet方法。 Reader and writer can access this structure from different threads: 读者和作家可以从不同的线程访问此结构:

public sealed class ArrayPool<T> where T : class
{
    readonly T[] array;
    private readonly uint MASK;

    private volatile uint curWriteNum;
    private volatile uint curReadNum;

    public ArrayPool(uint length = 1024) // length must be power of 2
    {
        if (length <= 0) throw new ArgumentOutOfRangeException("length");
        array = new T[length];
        MASK = length - 1;
    }

    /// <summary>
    /// TryGet() itself is not thread safe and should be called from one thread.
    /// However TryGet() and Obtain/Commit can be called from different threads
    /// </summary>
    /// <returns></returns>
    public T TryGet()
    {
        if (curReadNum == curWriteNum)
        {
            return null;
        }
        T result = array[curReadNum & MASK];
        curReadNum++;
        return result;
    }

    public T Obtain()
    {
        return array[curWriteNum & MASK];
    }

    public void Commit()
    {
        curWriteNum++;
    }

}

Comments about my implementation are welcome and probably some library method can replace this simple class? 欢迎欢迎对我的实现发表评论,也许某些库方法可以代替这个简单的类?

I don't think you should leap at this, as per my comments on the question - however, a simple approach would be something like: 我不认为你应该在这个飞跃 ,按照我的意见对这个问题-但是,一个简单的方法是这样的:

public sealed class MicroPool<T> where T : class
{
    readonly T[] array;
    public MicroPool(int length = 10)
    {
        if (length <= 0) throw new ArgumentOutOfRangeException("length");
        array = new T[length];
    }
    public T TryGet()
    {
        T item;
        for (int i = 0; i < array.Length; i++)
        {
            if ((item = Interlocked.Exchange(ref array[i], null)) != null)
                return item;
        }
        return null;
    }
    public void Recycle(T item)
    {
        if(item == null) return;
        for (int i = 0; i < array.Length; i++)
        {
            if (Interlocked.CompareExchange(ref array[i], item, null) == null)
                return;
        }
        using (item as IDisposable) { } // cleaup if needed
    }
}

If the loads come in burst, you may be able to use the GC's latency modes to offset the overhead by delaying collects. 如果负载突增,您可以使用GC的延迟模式通过延迟收集来抵消开销。 This is not a silver bullet, but in some cases it can be very helpful. 这不是灵丹妙药,但在某些情况下可能会很有帮助。

I am not sure, if this is what you need, but you could always make a pool of objects that are going to be used. 我不确定这是否是您所需要的,但是您始终可以创建将要使用的对象池。 Initialize a List of the object type. 初始化对象类型的列表。 Then when you need to use an object remove it from the list and add it back when you are done with it. 然后,当您需要使用对象时,将其从列表中删除,完成后再添加回去。

http://www.codeproject.com/Articles/20848/C-Object-Pooling Is a good start. http://www.codeproject.com/Articles/20848/C-Object-Pooling是一个好的开始。

Hope I've helped even if a little :) 希望我有所帮助,即使有一点:)

If you are just worried about the time taken for the GC to run, then don't be - it can't be beaten by anything you can do yourself. 如果您只是担心GC的运行时间,那就不要-它不能被您自己做的任何事情所击败。

However, if your objects' constructors do some work it might be quicker to cache them. 但是,如果对象的构造函数做了一些工作,则将它们缓存起来可能会更快。

A fairly straightforward way to do this is to use a ConcurrentBag 一个相当简单的方法是使用ConcurrentBag

Essentially what you do is to pre-populate it with a set of objects using ConcurrentBag.Add() (that is if you want - or you can start with it empty and let it grow). 本质上,您要做的就是使用ConcurrentBag.Add()一组对象预填充到该对象中(也就是说,如果您愿意-可以先将其清空并使其增长)。

Then when you need a new object you use ConcurrentBag.TryTake() to grab an object. 然后,当您需要一个新对象时,可以使用ConcurrentBag.TryTake()来获取一个对象。

If TryTake() fails then you just create a new object and use that instead. 如果TryTake()失败,则只需创建一个新对象并使用它即可。

Regardless of whether you grabbed an object from the bag or created a new one, once you're done with it you just put that object back into the bag using ConcurrentBag.Add() 不管您是从袋子中抓取一个物体还是创建一个新物体,一旦完成,您都可以使用ConcurrentBag.Add()将那个物体放回袋子中。

Generally your bag will get to a certain size but no larger (but you might want to instrument things just to check it). 通常,您的包会达到一定的大小,但不会变大(但您可能想要装东西以检查它)。

In any case, I would always do some timings to see if changes like this actually make any difference. 无论如何,我总是会花一些时间看这样的变化是否真的有所不同。 Unless the object constructors are doing a fair bit of work, chances are it won't make much difference. 除非对象构造函数做大量工作,否则它不会有太大改变。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM