3

我们有一个应用程序不断地从网络中读取大量数据。我们将 GC(甚至是 gen0 收集的累积效应。我们使用 ETW 收集来支持我们的发现)确定为最大的瓶颈,因此我们尝试执行内存池以避免任何收集启动。

我们可以预先分配巨大的字节数组以连续从网络读取而无需分配,我们可以对 char 数组执行相同的操作(以避免在使用 Encoding 类转换期间分配),但是似乎没有办法转换为基本类型(int , decimal ...) 无需重新发明轮子(意味着重新实现 BCL 在 TryParse 方法中所做的事情)或不创建垃圾(将 char[] 转换为要丢弃的字符串)。

所以这是我的问题:

  • 是否可以以某种方式将 char 数组注入字符串,或者强制字符串从可重用的内存池中分配内存?我正在研究字符串的反射内部,这似乎是一项不可能完成的任务,但我欢迎任何建议

或者

  • 是否可以利用一些标准转换函数从 char[] (或其他文本,但不是 System.String 形式)转换为基本类型?再次 - 我正在研究 System.Number 的反射代码 - 看起来卧底函数需要 char *,因此可以通过反射调用它们;然而,日期时间转换仍然使用字符串。

欢迎任何建议。

4

1 回答 1

1

如果不安全代码是您的应用程序的可行替代方案,那么您可以重写字符串的内容和长度。这将允许您拥有一个可以预分配的可重用字符串池,从而避免垃圾收集。

AC# 字符串在内存中的布局如下:

int Capacity;
int Length;
char FirstCharacter;
// remaining characters follow

字符数据以空值结尾(便于与非托管 C/C++ 代码进行互操作),并且还存储当前长度和最大容量,以避免那些讨厌的缓冲区溢出问题。

以下是如何在分配任何新内存的情况下将新内容注入现有字符串:

    static unsafe void RecycleString(string s, char[] newcontents)
    {
        // First, fix the string so the GC doesn't move it around on us, and get a pointer to the character data.
        fixed (char* ps = s)
        {
            // We need an integer pointer as well, to check capacity and update length.
            int* psi = (int*)ps;
            int capacity = psi[-2];

            // Don't overrun the buffer!
            System.Diagnostics.Debug.Assert(capacity > newcontents.Length);
            if (capacity > newcontents.Length)
            {
                for (int i = 0; i < newcontents.Length; ++i)
                {
                    ps[i] = newcontents[i];
                }

                // Add null terminator and update length accordingly.
                ps[newcontents.Length] = '\0';
                psi[-1] = newcontents.Length;
            }
        }
    }

有了它,您可以回收并重新解析相同的字符串以符合您的心意。这是一个简单的示例来演示:

    private static void ReusableStringTest()
    {
        char[] intFromWire = new char[] { '9', '0', '0', '0' };
        char[] floatFromWire = new char[] { '3', '.', '1', '4', '1', '5' };

        string reusableBuffer = new string('\0', 128);

        RecycleString(reusableBuffer, intFromWire);
        int i = Int32.Parse(reusableBuffer);
        Console.WriteLine("Parsed integer {0}", i);

        RecycleString(reusableBuffer, floatFromWire);
        float f = Single.Parse(reusableBuffer);
        Console.WriteLine("Parsed float {0}", f);
    }

生成的输出正如人们所希望的那样:

解析整数 9000
解析浮点数 3.1415

如果不安全的代码让你感到紧张,请记住我们用 C 和 C++ 编程的那些年,那时一切都是不安全的!

于 2015-03-23T06:21:34.723 回答