有没有办法创建一个继承ByteBuffer类的类?
ByteBuffer中的一些抽象方法是包私有的,如果我创建java.nio包,则会抛出安全异常。
出于性能原因,我想这样做-例如,getInt大约有10个方法调用,以及相当多的if。即使所有检查都被保留,只有方法调用被内联化且大小端检查被删除,我创建的测试表明它可以快约4倍。
有没有办法创建一个继承ByteBuffer类的类?
ByteBuffer中的一些抽象方法是包私有的,如果我创建java.nio包,则会抛出安全异常。
出于性能原因,我想这样做-例如,getInt大约有10个方法调用,以及相当多的if。即使所有检查都被保留,只有方法调用被内联化且大小端检查被删除,我创建的测试表明它可以快约4倍。
-Xbootclasspath/p
并在需要的包中扩展自己的类(不在java.nio之内)。这是完成它的方法。另一种方法是使用sun.misc.Unsafe并在address()
后直接访问内存来执行所需操作。我想出于性能原因,例如getInt有大约10个方法调用以及相当多的if语句。即使所有检查都留下来,而只有方法调用被插入,big / small endian检查被删除,我进行的测试表明,它可能快4倍。现在好处是使用gdb并检查真正生成的机器代码,您会惊讶于将删除多少个检查。我无法想象为什么一个人想要扩展这些类。它们存在是为了允许良好的性能,而不仅仅是OO多态执行。如何声明任何类并绕过Java验证器。关于Unsafe:Unsafe有两种绕过验证器的方法,如果您有一个扩展ByteBuffer的类,可以调用其中任何一个。你需要一些被黑客攻击的版本(但这很容易)ByteBuffer w /公共访问和受保护的c-tor只是为了编译器。以下是这些方法。您可以在自己的风险上使用它们。在声明该类之后,甚至可以使用new关键字(前提是有适当的c-tor)。public native Class defineClass(String name, byte[] b, int off, int len, ClassLoader loader, ProtectionDomain protectionDomain);
public native Class defineClass(String name, byte[] b, int off, int len);
ByteBuffer
传递给一个固定的 API(只能接受 ByteBuffer
),但你需要让 ByteBuffer
表现出自定义的行为方式(例如通过实现 Closeable
并在调用 close()
时释放一些底层资源,或者可能需要使用自定义数据存储来支持 ByteBuffer
)。我之前遇到过多次这种需求。 - Luke Hutchison通过使用反射,您可以忽略保护级别,但这会严重破坏性能目标。
您不能创建 java.nio 包中的类 - 这样做(并以任何方式分发结果)将违反 Sun 的 Java 许可证,理论上可能会导致法律纠纷。
我认为,如果不使用本地方法,想实现您想要的功能可能是不可能的 - 但我也怀疑您是否屈服于过早优化的诱惑。假设您的测试是正确的(微基准测试经常不是):您真的确定访问 ByteBuffer 将成为实际应用程序性能瓶颈的5%吗?当您的应用程序只花费5%的时间获取数据并处理95%的时间时,无论 ByteBuffer.get() 可以快4倍都没多大意义。
为了实现(可能仅仅是理论上的)性能而绕过所有检查并不是一个好主意。性能调整的基本规则是“首先使其正确工作,然后再使其更快”。
编辑:如果如评论中所述,该应用程序实际上在 ByteBuffer 方法中花费了20-40%的时间,而且测试是正确的,这意味着速度提升的潜力为15-30% - 这是显着的,但在我看来并不值得开始使用 JNI 或玩弄 API源代码。我会首先尝试用完所有其他选择:
除了自己实现所需的方法之外,没有其他选择。在可能的情况下,将方法设为final将有助于编译器进行优化(减少运行时多态性和内联代码生成的需要)。
一个Java代理可以修改ByteBuffer的字节码并更改构造函数的访问修饰符。当然,您需要在JVM上安装代理,并且仍然需要编译子类以进行编译。如果您正在考虑这样的优化,则必须准备好接受挑战!
我从未尝试过这种低级别的操作。希望在代理程序钩入之前JVM不需要使用ByteBuffer。
Unsafe
引用和address
。为了避免超出缓冲区的范围并杀死JVM,我分配比我需要的更多的页面,只要它们没有被触及,就不会为应用程序分配物理内存。这意味着我有更少的边界检查,并且只在关键点进行检查。int totalLength = numberOfIntsInBuffer;
ByteBuffer myBuffer = whateverMyBufferIsCalled;
int[] block = new int[1024];
IntBuffer intBuff = myBuffer.asIntBuffer();
int partialLength = totalLength/1024;
//Handle big blocks of 1024 ints at a time
try{
for (int i = 0; i < partialLength; i++) {
intBuff.get(block);
// Do processing on ints, w00t!
}
partialLength = totalLength % 1024; //modulo to get remainder
if (partialLength > 0) {
intBuff.get(block,0,partialLength);
//Do final processing on ints
}
} catch BufferUnderFlowException bufo {
//well, dang!
}
这比一次获取一个int要快得多。迭代int[]数组,该数组具有设置和已知的良好边界,还可以通过消除边界检查和ByteBuffer可能引发的异常来使您的代码JIT更紧凑。
如果需要更高的性能,您可以调整代码或自己编写大小优化的byte[]到int[]转换代码。我曾经使用它来代替IntBuffer方法并进行部分循环展开以获得一些性能提升...但这绝不是建议的做法。