有没有办法创建扩展ByteBuffer类的类?
ByteBuffer的一些抽象方法是包私有的,如果我创建包java.nio,则抛出安全异常。
出于性能原因,我想这样做 - 例如,getInt有大约10个方法调用,以及相当多的if。即使剩下所有检查,只有方法调用被内联并且大/小端检查被删除,我创建的测试表明它可以快4倍。
答案 0 :(得分:13)
你无法扩展ByteBuffer并感谢上帝。
你不能延伸b / c没有受保护的c-tors。为什么感谢上帝?好吧,只有2个真正的子类可以确保JVM可以重大优化任何涉及ByteBuffer的代码。
最后,如果你需要扩展类的实际,编辑字节代码,只需将c-tor和public属性的protected属性添加到DirectByteBuffer(和DirectByteBufferR)。 扩展HeapBuffer没有任何意义,因为你无论如何都可以访问底层数组
使用-Xbootclasspath/p
并在那里添加自己的类,扩展到您需要的包中(在java.nio之外)。这就是它的完成方式。
另一种方法是使用sun.misc.Unsafe并在address()
之后随意访问内存,执行任何操作。
我想这样做 表现原因 - getInt for 例子有大约10个方法 调用,以及不少 如果的。即使剩下所有支票,也是如此 只有方法调用内联和 大/小端检查被删除, 我创建的测试显示了它 可以快4倍左右。
现在好的部分,使用gdb并检查真正生成的机器代码,你会惊讶地发现将删除多少支票。
我无法想象为什么一个人想要扩展课程。它们的存在是为了获得良好的性能而不仅仅是OO多态执行。
编辑:
On Unsafe:Unsafe有2个绕过验证程序的方法,如果你有一个扩展ByteBuffer的类,你可以调用它们中的任何一个。对于编译器,你需要一些带有公共访问和受保护的c-tor的ByteBuffer的黑客版本(但这非常简单)。 方法如下。你可以自己承担风险。声明类之后你甚至可以使用w / 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);
答案 1 :(得分:9)
您可以通过使用反射忽略保护级别,但这有点大大地击败了性能目标。
你不能在java.nio包中创建一个类 - 这样做(并以任何方式分发结果)违反了Sun的Java许可证,理论上可能会让你陷入法律麻烦。
我认为没有办法在没有原生的情况下做你想做的事情 - 但我也怀疑你是否屈服于过早优化的诱惑。假设您的测试是正确的(通常不是哪些微基准测试):您是否真的确定对ByteBuffer的访问将成为实际应用程序中的性能瓶颈?当你的应用程序只花费5%的时间在95%处理所提取的数据时,ByteBuffer.get()的速度是否快4倍是无关紧要的。
为了(可能纯粹是理论上的)性能而想要绕过所有检查并不是一个好主意。性能调优的基本规则是“首先让它正常工作,然后让它更快地工作”。
编辑如果如评论中所述,应用实际上确实花了20-40%的时间在ByteBuffer方法中并且测试是正确的,那意味着15-的加速潜力30% - 重要,但IMO不值得开始使用JNI或搞乱API源。我会先尝试用尽所有其他选择:
答案 2 :(得分:2)
ByteBuffer是抽象的,是的,你可以扩展它...但我认为你想要做的是扩展实际实例化的类,你可能不会。也可能是实例化的特定方法会覆盖该方法,使其比ByteBuffer中的方法更有效。
我还会说你可能总是对所有需要的东西都是错的 - 或许它不是你正在测试的东西,但可能代码是有原因的(可能在其他平台上)。
如果您确实认为自己是正确的,请打开bug并查看他们要说的内容。
如果要添加到nio包,可以在调用Java时尝试设置引导类路径。它应该让你在rt.jar之前放入你的课程。键入java -X以查看如何执行此操作,您需要-Xbootclasspath / p开关。
答案 3 :(得分:1)
+50赏金以避免访问限制(tt不能 仅使用反射完成。也许 有一种方法使用sun.misc.Unsafe 等?)
答案是:没有办法规避Java中的所有访问限制。
sun.misc.Unsafe
在安全管理人员的授权下工作,因此无法帮助ByteBuffer包私有 abstract _set和_get方法,所以你 无法覆盖它。还有所有 构造函数是包私有的,所以 你不能打电话给他们。
问题的底线是尝试覆盖字节缓冲区不会解决问题。
除了您自己实现一个类,没有其他选择,使用您需要的方法。使方法最终是你可以帮助编译器执行优化(减少为运行时多态性和内联生成代码的需要)。
答案 4 :(得分:1)
获取Unsafe实例的最简单方法是通过反射。但是,如果您无法使用反射,则可以创建另一个实例。你可以通过JNI做到这一点。
我尝试使用字节代码来创建一个不调用构造函数的实例,允许您创建一个没有可访问构造函数的对象实例。但是,这个id不起作用,因为我得到了字节码的VerifyError。该对象必须有一个调用它的构造函数。
我所做的是有一个包含直接ByteBuffer的ParseBuffer。我使用反射来获取Unsafe
引用和address
。为了避免运行缓冲区的末端并终止JVM,我分配了比我需要的页面更多的页面,只要它们没有被触及,就不会为应用程序分配物理内存。这意味着我的边界检查要少得多,只能检查关键点。
使用OpenJDK的调试版本,您可以看到Unsafe get / put方法变为单个机器代码指令。但是,并非所有JVM都不提供此功能,并且可能无法在所有平台上获得相同的改进。
使用这种方法我会说你可以减少40%的时间,但是存在普通Java代码没有的风险,即你可以杀死JVM。我使用的用例是一个对象创建免费XML解析器和使用Unsafe包含的数据处理器与使用普通直接ByteBuffer相比。我在XML解析器中使用的一个技巧是getShort()和getInt()一次检查多个字节,而不是一次检查一个字节。
对Unsafe类使用反射是一个开销的开销。一旦你有了Unsafe实例,就没有开销。
答案 5 :(得分:1)
Java Agent可以修改ByteBuffer的字节码并更改构造函数的访问修饰符。当然,您需要在JVM上安装代理,并且仍然需要编译获取子类进行编译。如果您正在考虑进行此类优化,那么您必须为此做好准备!
我从来没有尝试过如此低级别的操纵。希望JVM在代理可以挂钩之前不需要ByteBuffer。
答案 6 :(得分:-1)
我正在回答你想要答案的问题,而不是你问过的问题。你真正的问题是“我怎样才能让它变得更快?”答案是“一次处理一个数组的整数,而不是单独处理。”
如果瓶颈确实是ByteBuffer.getInt()或ByteBuffer.getInt(location),那么你不需要扩展类,你可以使用预先存在的IntBuffer类来批量获取数据,以便更有效地处理
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 []数组,它具有set和known-good bounds,通过消除边界检查和ByteBuffer可以抛出的异常,也可以让你的代码JIT更加紧凑。
如果您需要进一步的性能,可以调整代码,或将自己的大小优化的byte []转换为int []转换代码。使用它代替IntBuffer方法并部分循环展开,我能够获得一些性能提升......但是无论如何都没有建议。