【问题标题】:Java Circular Byte Buffer that Extends java.nio.ByteBuffer扩展 java.nio.ByteBuffer 的 Java 循环字节缓冲区
【发布时间】:2011-03-04 00:57:56
【问题描述】:

我在 SO 和其他地方看到的每个 Java 循环字节缓冲区实现都没有扩展 java.nio.ByteBuffer,这对我来说是与 SocketChannel 一起使用所必需的。有谁知道扩展 ByteBuffer 的开源实现。我尝试自己编写,但是当我意识到位置和剩余功能是最终的并且我要覆盖它们以调整头部并防止缓冲区溢出异常时,我陷入了困境。在通过套接字通道发送 5000 条消息时,每条消息都需要我将内容复制到线性缓冲区的头部,这会增加每条消息大约 450 毫秒或 90 微秒(其中包含 10 个数据包,因此每个数据包需要 9 微秒)。现在我能想到的唯一可行的方法是覆盖每一个方法并重写所有内容。有什么想法吗?

【问题讨论】:

    标签: java buffer bytebuffer


    【解决方案1】:

    您可以使缓冲区比一条消息大得多,而不是创建一个循环缓冲区。假设最大消息大小为 N 字节。创建一个 100 * N 字节的缓冲区,只有在剩余少于 N 字节时才会 compact() ByteBuffer。这将使复制量减少 100 倍。

    另一个优化是在没有剩余数据时压缩() ByteBuffer,因为这非常快。

    【讨论】:

    • 我大部分时间都避免使用 compact() 。相反,当缓冲区无法变空并且大约 1/8 的容量可用于进一步写入时,我使用 compact()。缓冲区未完全传输时的常见情况。使用缓冲区进行读取后:buffer().mark(). buffer.position(buffer.limit()).limit(buffer.capacity());。标记显示读数可能开始的位置。此外,我对 buffer.capacity 的指导方针与 socket.writebuffer 完全相同
    • 我同意,当我开始修改我的帧同步方法以允许缓冲区中的多个帧时,这很可能是我的实现,但缺点是它使用更多的内存。我当前的实现是一个长度为 N 的缓冲区,我在每条消息之后都进行压缩。我犹豫使用 compact 的原因是 ByteBuffer 的文档说它是可选的。循环缓冲区仍然是理想的解决方案,但似乎不太合理。
    • compact() 最适合它的功能,但这并不意味着它便宜。另一方面,内存相对便宜。 1 MB 的内存大约需要 10 美分,这相当于以最低工资计算大约一分钟的时间。 ;)
    • @PeterLawrey 除非您将代码放入 1000 万个单位,在这种情况下,您的 10 美分变成 100 万美元。这可能值得花一点时间。只是说...
    • @refuser 我同意。不是每个人都在开发那么多单元,尤其是服务器,如果是,那么可能是内存更重要的移动设备。 ;)
    【解决方案2】:

    你不能扩展 java.nio.ByteBuffer,就这么简单。 c-tor 是包私有的。 它不会起作用,b/c 的主要思想是将地址传递给一些 C 代码。你也不能覆盖任何东西,因为许多方法都是最终的。 ByteBuffers 是为提高速度而设计的,一些决定可能看起来很奇怪,但它们没问题。

    java.nio.channels.GatheringByteChanneljava.nio.channels.ScatteringByteChannel 值得一试,尽管有相当多的实现依赖(本机 C)才能使它们有用。

    【讨论】:

    • 我不一定同意包应该是私有的,因为它涉及到本机代码。通过扩展 ByteBuffer,您无法在外部做任何事情。至少应该公开抽象的 Buffer 类。
    【解决方案3】:

    【讨论】:

    • 这个实现似乎不支持 ByteBuffer 的大多数实际方法,我不禁想到要真正使它有用需要做多少工作。我的应用程序都需要按索引、位置、标记、限制获取。
    • 这实际上并没有扩展java.nio.ByteBuffer。这是其他同名的类。
    【解决方案4】:

    我会利用方法 ByteBuffer.wrap()。快速查看Dean Hiller 先生之前发布的Cisco 的实现,您可以将put() 替换为getWriteBuffer(),它会返回一个ByteBuffer,它将包装在它可以写入的缓冲区中的部分。

    阅读部分也可以采用同样的逻辑。

    它的优点是不必压缩,这可能会很昂贵,具体取决于 ByteBuffer 中的字节数,代价是使解析逻辑复杂化:您可能会得到 ByteBuffer 的消息的第一部分包装底层循环缓冲区的最后一个区域。要获取消息的第二部分,需要再次读取以将循环缓冲区开头的字节数组包装到另一个 ByteBuffer 中。

    【讨论】:

    • 这已经可以通过 slice 方法获得,但仍然不能真正解决问题。如果剩余为零并且我没有收到完整的消息,我仍然需要压缩。重点是不必将数据复制到任何地方,因此必须执行两次读取的工作流程意味着您正在将数据复制到某个地方。
    • 您还没有声明您正在执行零拷贝解析......在这种情况下,您可能会通过对用于读取和写入的缓冲区进行动态大小限制来使实现更加复杂。您将需要一个特殊的逻辑来了解您可以在接近结尾或开头写入多少字节,具体取决于消息大小。你应该把它放在最后,否则你把它放在开头,然后在缓冲区开始时将读取指针的动态大小限制更改为取模。
    猜你喜欢
    • 1970-01-01
    • 2019-06-08
    • 2016-06-11
    • 1970-01-01
    • 2022-11-14
    • 2014-07-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多