【问题标题】:String vs byte array, Performance字符串与字节数组,性能
【发布时间】:2011-12-14 03:57:17
【问题描述】:

(这篇文章是关于高频类型编程的)

我最近在一个论坛上看到(我认为他们正在讨论 Java),如果您必须解析大量字符串数据,最好使用字节数组而不是带有 split() 的字符串。确切的帖子是:

使用任何语言、C++、Java、C# 的一个性能技巧是 避免创建对象。这不是分配或 GC 的成本,它的 访问不适合 CPU 缓存的大型内存阵列的成本。

现代 CPU 比内存快得多。他们为许多人停滞不前, 每个缓存未命中的许多周期。大多数 CPU 晶体管预算是 分配以通过大型缓存和大量滴答声来减少这种情况。

GPU 通过准备大量线程来解决问题 执行以隐藏内存访问延迟并且几乎没有缓存和 将晶体管花在更多内核上。

因此,例如,而不是使用字符串和拆分来解析 消息,使用可以就地更新的字节数组。你真的想要 避免对大型数据结构的随机内存访问,至少在 内部循环。

他只是说“不要使用字符串,因为它们是一个对象并且创建对象的成本很高”?还是他在说别的?

使用字节数组能否确保数据尽可能长时间地保留在缓存中? 当您使用字符串时,它是否太大而无法保存在 CPU 缓存中? 一般来说,使用原始数据类型是编写更快代码的最佳方法吗?

【问题讨论】:

    标签: c# java c++ oop


    【解决方案1】:

    他的意思是,如果你将一个块文本分解成单独的字符串对象,那么这些字符串对象的局部性要比大量的文本数组更差。每个字符串及其包含的字符数组都将位于内存中的其他位置;它们可以遍布各处。在处理数据时,内存缓存很可能必须反复进出才能访问各种字符串。相比之下,一个大数组具有最好的局部性,因为所有数据都在一个内存区域上,缓存抖动将保持在最低限度。

    当然,这是有限制的:如果文本非常非常大,并且您只需要解析出其中的一部分,那么这几个小字符串可能比大块文本更适合缓存。

    【讨论】:

    • 您说“它们可以遍布各处”。 String 的字符是存储在连续内存中,还是像链表一样?
    • 字符在连续内存中。但通常一个字符串对象由两个独立的块组成:字符串对象本身和一个保存字符的数组。然后,如果您创建许多字符串,那么这些字符串中的每一个以及它们的每个数组都是someplace,并且无法保证大量对象中的任何一个都将位于同一内存区域;每一个都被单独分配,可以在任何地方。在 C++ 中,如果字符串对象被分配在一个值数组中,它们本身可以都在同一个位置;在 Java 中,你甚至不会拥有它。
    • 字符串中的字符是连续的,但是如果您有多个字符串,它们可以遍布各处。如果您在 Java 中使用 String.substring,它是底层字符串的视图,因此不会发生这种情况,但是 C++ 和 C# 在获取另一个字符串的子字符串时会复制源数据。
    【解决方案2】:

    使用byte[]char* 代替字符串进行HFT 的原因还有很多。字符串由 Java 中的 16 位 char 组成,并且是不可变的。 byte[]ByteBuffer 易于回收,具有良好的缓存位置,可以在堆外(直接)保存副本,避免字符编码器。这一切都假设您使用的是 ASCII 数据。

    char* 或 ByteBuffers 也可以映射到网络适配器以保存另一个副本。 (对 ByteBuffers 进行一些摆弄)

    在 HFT 中,您很少同时处理大量数据。理想情况下,您希望在数据通过 Socket 时立即处理数据。即一次一个数据包。 (约 1.5 KB)

    【讨论】:

    • 如何将字节数组保留在堆外,您不必在声明中使用“new”吗?
    • 在 C++ 中,您需要使用 newmalloc 在 Java 中,您可以使用 ByteBuffer.allocateDirect()(它是 malloced 内存块的包装器)使用反射或JNI,您可以更改 address 指向的位置,以便它可以直接访问网络适配器(如果您正在使用内核旁路)如果您使用 Unsafe 类,您可以完全取消 ByteBuffer(尽管它很少使足够的差异)
    • 亲爱的 Peter,您能否详细说明“使用反射或 JNI,您可以更改地址指向的位置,以便它可以直接访问网络适配器(如果您使用内核旁路)”。您知道任何带有一些小示例代码的网站吗?我认为这在 C++ 中比在 Java 中容易得多?
    • 这在 C 或 C++ 中是微不足道的。您所需要的只是一个第二天性的指针。在 Java 中,您必须跳过一些障碍,但您可以实现相同的目标。除了使用反射设置字段之外,我不确定我可以向您展示哪些示例。即 Field.setLong();
    猜你喜欢
    • 2016-03-01
    • 2013-10-23
    • 2020-11-04
    • 1970-01-01
    • 2021-01-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-04
    相关资源
    最近更新 更多