Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/angularjs/23.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 为什么ByteBuffer.allocate()和ByteBuffer.allocateDirect()之间存在奇数性能曲线差异_Java_Nio_Bytebuffer - Fatal编程技术网

Java 为什么ByteBuffer.allocate()和ByteBuffer.allocateDirect()之间存在奇数性能曲线差异

Java 为什么ByteBuffer.allocate()和ByteBuffer.allocateDirect()之间存在奇数性能曲线差异,java,nio,bytebuffer,Java,Nio,Bytebuffer,我正在研究一些SocketChannel-to-SocketChannel代码,它最适合使用直接字节缓冲区——长寿命且大(每个连接数十到数百兆字节)。在使用FileChannels散列出确切的循环结构时,我在ByteBuffer.allocate()上运行了一些微基准测试vs.ByteBuffer.allocateDirect()性能 结果令人惊讶,我真的无法解释。在下图中,ByteBuffer.allocate()transfer实现的256KB和512KB内存明显下降,性能下降了约50%!对

我正在研究一些
SocketChannel
-to-
SocketChannel
代码,它最适合使用直接字节缓冲区——长寿命且大(每个连接数十到数百兆字节)。在使用
FileChannel
s散列出确切的循环结构时,我在
ByteBuffer.allocate()上运行了一些微基准测试
vs.
ByteBuffer.allocateDirect()
性能

结果令人惊讶,我真的无法解释。在下图中,
ByteBuffer.allocate()
transfer实现的256KB和512KB内存明显下降,性能下降了约50%!对于
ByteBuffer.allocateDirect()
,似乎还有更小的性能悬崖。(百分比增益系列有助于可视化这些更改。)

缓冲区大小(字节)与时间(毫秒)

为什么
ByteBuffer.allocate()
ByteBuffer.allocateDirect()之间存在奇怪的性能曲线差异?幕后到底发生了什么

它很可能依赖于硬件和操作系统,因此以下是这些详细信息:

  • MacBook Pro带双核2 CPU
  • 英特尔X25M SSD驱动器
  • OSX 10.6.4
源代码,按请求:

package ch.dietpizza.bench;

import static java.lang.String.format;
import static java.lang.System.out;
import static java.nio.ByteBuffer.*;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;

public class SocketChannelByteBufferExample {
    private static WritableByteChannel target;
    private static ReadableByteChannel source;
    private static ByteBuffer          buffer;

    public static void main(String[] args) throws IOException, InterruptedException {
        long timeDirect;
        long normal;
        out.println("start");

        for (int i = 512; i <= 1024 * 1024 * 64; i *= 2) {
            buffer = allocateDirect(i);
            timeDirect = copyShortest();

            buffer = allocate(i);
            normal = copyShortest();

            out.println(format("%d, %d, %d", i, normal, timeDirect));
        }

        out.println("stop");
    }

    private static long copyShortest() throws IOException, InterruptedException {
        int result = 0;
        for (int i = 0; i < 100; i++) {
            int single = copyOnce();
            result = (i == 0) ? single : Math.min(result, single);
        }
        return result;
    }


    private static int copyOnce() throws IOException, InterruptedException {
        initialize();

        long start = System.currentTimeMillis();

        while (source.read(buffer)!= -1) {    
            buffer.flip();  
            target.write(buffer);
            buffer.clear();  //pos = 0, limit = capacity
        }

        long time = System.currentTimeMillis() - start;

        rest();

        return (int)time;
    }   


    private static void initialize() throws UnknownHostException, IOException {
        InputStream  is = new FileInputStream(new File("/Users/stu/temp/robyn.in"));//315 MB file
        OutputStream os = new FileOutputStream(new File("/dev/null"));

        target = Channels.newChannel(os);
        source = Channels.newChannel(is);
    }

    private static void rest() throws InterruptedException {
        System.gc();
        Thread.sleep(200);      
    }
}
包装ch.dietpizza.bench;
导入静态java.lang.String.format;
导入静态java.lang.System.out;
导入静态java.nio.ByteBuffer.*;
导入java.io.File;
导入java.io.FileInputStream;
导入java.io.FileOutputStream;
导入java.io.IOException;
导入java.io.InputStream;
导入java.io.OutputStream;
导入java.net.UnknownHostException;
导入java.nio.ByteBuffer;
导入java.nio.channels.channels;
导入java.nio.channels.ReadableByteChannel;
导入java.nio.channels.WritableByteChannel;
公共类SocketCannelByteBufferExample{
私有静态可写ByTechannel目标;
Techannel源的私有静态可读性;
专用静态字节缓冲区;
公共静态void main(字符串[]args)引发IOException、InterruptedException{
长期直接;
长期正常;
out.println(“开始”);

对于(int i=512;i有很多原因导致这种情况发生。如果没有代码和/或更多关于数据的详细信息,我们只能猜测发生了什么

一些猜测:

  • 也许您达到了一次可以读取的最大字节数,因此IOwaits会变得更高,或者在不减少循环的情况下增加内存消耗
  • 可能您遇到了严重的内存限制,或者JVM正在尝试在新分配之前释放内存。请尝试使用
    -Xmx
    -Xms
    参数
  • 可能HotSpot无法/不会优化,因为对某些方法的调用次数太少
  • 可能是操作系统或硬件条件导致了这种延迟
  • 也许JVM的实现只是有缺陷;-)
线程本地分配缓冲区(TLAB)
我想知道测试期间线程本地分配缓冲区(TLAB)是否在256K左右。使用TLAB可以优化堆中的分配,因此的非直接分配我怀疑是由于跨越CPU缓存边界而导致的。“非直接”缓冲区读()/写()实现“缓存未命中”与“直接”缓冲区读()/写()实现相比,前面提到了额外的内存缓冲区拷贝。

ByteBuffer是如何工作的,以及为什么直接(字节)缓冲区现在是唯一真正有用的。 首先,我有点惊讶,这不是常识,但我可以忍受

直接字节缓冲区在java堆之外分配地址

这一点至关重要:所有操作系统(和本机C)函数都可以利用该地址,而不锁定堆上的对象并复制数据。复制的简短示例:为了通过Socket.getOutputStream()发送任何数据。写入(字节[]),本机代码必须“锁定”字节[],将其复制到java堆外,然后调用OS函数,例如,复制可以在堆栈上执行(对于较小的字节[]),也可以通过malloc/free执行(对于较大的字节[])。 DatagramSockets没有什么不同,它们也会复制—除了它们被限制为64KB,并在堆栈上分配,如果线程堆栈不够大或递归不够深,甚至会终止进程。 注意:锁定可防止JVM/GC在堆周围移动/重新分配对象

因此,引入NIO的想法是避免复制和大量的流管道/间接寻址。通常在数据到达目的地之前有3-4个缓冲类型的流。(耶,波兰用漂亮的镜头平衡了(!) 通过引入直接缓冲区,java可以直接与C本机代码通信,而无需任何必要的锁定/复制。因此,
sent
函数可以获取缓冲区的地址添加位置,性能与本机C基本相同。 这是关于直接缓冲区的

直接缓冲区的主要问题是,它们的成本很高,使用起来也很麻烦,与字节[]不同

非直接缓冲区不能提供直接缓冲区的真正本质-即直接桥接到本机/操作系统-相反,它们是轻量级的,共享完全相同的API-而且,它们可以
包装字节[]
,甚至它们的备份数组也可以直接操作-有什么不喜欢的?它们必须被复制

因此,Sun/Oracle如何处理非直接缓冲区,因为OS/native无法使用它们——很好,很幼稚。当使用非直接缓冲区时,必须创建一个直接计数器部分。该实现足够智能,可以使用
ThreadLocal
,并通过
SoftReference
*缓存一些直接缓冲区,以避免高昂的创建成本。naive部件在复制它们时出现-它每次都尝试复制整个缓冲区(
remaining()

现在想象一下:512 KB非直接缓冲区变为64 KB
-XX:TLABSize= -XX:-ResizeTLAB
-XX:+PrintTLAB