/** * Copyright 2014 JogAmp Community. All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of JogAmp Community. */ package com.jogamp.common.nio; import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.RandomAccessFile; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import org.junit.AfterClass; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; import com.jogamp.common.os.Platform; import com.jogamp.common.util.IOUtil; import com.jogamp.junit.util.SingletonJunitCase; import org.junit.FixMethodOrder; import org.junit.runners.MethodSorters; /** * Testing serial read of {@link ByteBufferInputStream} and {@link MappedByteBufferInputStream}, * i.e. basic functionality only. * <p> * Focusing on comparison with {@link BufferedInputStream} regarding * performance, used memory heap and used virtual memory. * </p> */ @FixMethodOrder(MethodSorters.NAME_ASCENDING) public class TestByteBufferInputStream extends SingletonJunitCase { /** {@value} */ static final int buffer__8KiB = 1 << 13; /** {@value} */ static final int halfMiB = 1 << 19; /** {@value} */ static final int oneMiB = 1 << 20; /** {@value} */ static final int tenMiB = 1 << 24; /** {@value} */ static final int hunMiB = 1 << 27; /** {@value} */ static final int halfGiB = 1 << 29; /** {@value} */ static final int oneGiB = 1 << 30; /** {@value} */ static final long twoPlusGiB = ( 2L << 30 ) + halfMiB; static final String fileHalfMiB = "./testHalfMiB.bin" ; static final String fileOneMiB = "./testOneMiB.bin" ; static final String fileTenMiB = "./testTenMiB.bin" ; static final String fileHunMiB = "./testHunMiB.bin" ; static final String fileHalfGiB = "./testHalfGiB.bin" ; static final String fileOneGiB = "./testOneGiB.bin" ; static final String fileTwoPlusGiB = "./testTwoPlusGiB.bin" ; static final String fileOut = "./testOut.bin" ; public static final String PrintPrecision = "%8.3f"; public static final double MIB = 1024.0*1024.0; @BeforeClass public static void setup() throws IOException { final Runtime runtime = Runtime.getRuntime(); System.err.printf("Total Memory : "+PrintPrecision+" MiB%n", runtime.totalMemory() / MIB); System.err.printf("Max Memory : "+PrintPrecision+" MiB%n", runtime.maxMemory() / MIB); setup(fileHalfMiB, halfMiB); setup(fileOneMiB, oneMiB); setup(fileTenMiB, tenMiB); setup(fileHunMiB, hunMiB); setup(fileHalfGiB, halfGiB); setup(fileOneGiB, oneGiB); setup(fileTwoPlusGiB, twoPlusGiB); } static void setup(final String fname, final long size) throws IOException { final File file = new File(fname); final RandomAccessFile out = new RandomAccessFile(file, "rws"); out.setLength(size); out.close(); } @AfterClass public static void cleanup() { cleanup(fileHalfMiB); cleanup(fileOneMiB); cleanup(fileTenMiB); cleanup(fileHunMiB); cleanup(fileHalfGiB); cleanup(fileOneGiB); cleanup(fileTwoPlusGiB); cleanup(fileOut); } static void cleanup(final String fname) { final File file = new File(fname); file.delete(); } @Test public void test01MixedIntSize() throws IOException { // testCopyIntSize1Impl(fileHalfMiB, halfMiB); // testCopyIntSize1Impl(fileOneMiB, oneMiB); testCopyIntSize1Impl(fileTenMiB, tenMiB); testCopyIntSize1Impl(fileHunMiB, hunMiB); testCopyIntSize1Impl(fileHalfGiB, halfGiB); // testCopyIntSize1Impl(fileOneGiB, oneGiB); } static enum SrcType { COPY, MMAP1, MMAP2_NONE, MMAP2_SOFT, MMAP2_HARD }; @Test public void test11MMap1GiBFlushNone() throws IOException { if( !manualTest && Platform.OSType.MACOS == Platform.getOSType() ) { testCopyIntSize1Impl2(0, SrcType.MMAP2_NONE, 0, fileOneMiB, oneMiB); } else { testCopyIntSize1Impl2(0, SrcType.MMAP2_NONE, 0, fileOneGiB, oneGiB); // testCopyIntSize1Impl2(0, SrcType.MMAP2_NONE, 0, fileTwoPlusGiB, twoPlusGiB); } } @Test public void test12MMap1GiBFlushSoft() throws IOException { if( !manualTest && Platform.OSType.MACOS == Platform.getOSType() ) { testCopyIntSize1Impl2(0, SrcType.MMAP2_SOFT, 0, fileOneMiB, oneMiB); } else { testCopyIntSize1Impl2(0, SrcType.MMAP2_SOFT, 0, fileOneGiB, oneGiB); // testCopyIntSize1Impl2(0, SrcType.MMAP2_SOFT, 0, fileTwoPlusGiB, twoPlusGiB); } } @Test public void test13MMap2GiBFlushHard() throws IOException { if( !manualTest && Platform.OSType.MACOS == Platform.getOSType() ) { testCopyIntSize1Impl2(0, SrcType.MMAP2_HARD, 0, fileOneMiB, oneMiB); } else { // testCopyIntSize1Impl2(0, SrcType.MMAP2_HARD, 0, fileOneGiB, oneGiB); testCopyIntSize1Impl2(0, SrcType.MMAP2_HARD, 0, fileTwoPlusGiB, twoPlusGiB); } } void testCopyIntSize1Impl(final String testFileName, final long expSize) throws IOException { testCopyIntSize1Impl2(0, SrcType.COPY, buffer__8KiB, testFileName, expSize); testCopyIntSize1Impl2(0, SrcType.COPY, hunMiB, testFileName, expSize); testCopyIntSize1Impl2(0, SrcType.MMAP1, 0, testFileName, expSize); testCopyIntSize1Impl2(0, SrcType.MMAP2_SOFT, 0, testFileName, expSize); System.err.println(); } boolean testCopyIntSize1Impl2(final int iter, final SrcType srcType, final int reqBufferSize, final String testFileName, final long expSize) throws IOException { final int expSizeI = (int) ( expSize <= Integer.MAX_VALUE ? expSize : Integer.MAX_VALUE ); final int bufferSize = reqBufferSize < expSizeI ? reqBufferSize : expSizeI; final File testFile = new File(testFileName); final long hasSize1 = testFile.length(); final long t0 = System.currentTimeMillis(); Assert.assertEquals(expSize, hasSize1); final Runtime runtime = Runtime.getRuntime(); final long[] usedMem0 = { 0 }; final long[] freeMem0 = { 0 }; final long[] usedMem1 = { 0 }; final long[] freeMem1 = { 0 }; final String prefix = "test #"+iter+" "+String.format(PrintPrecision+" MiB", expSize/MIB); System.err.printf("%s: mode %-5s, bufferSize %9d: BEGIN%n", prefix, srcType.toString(), bufferSize); dumpMem(prefix+" before", runtime, -1, -1, usedMem0, freeMem0 ); final IOException[] ioe = { null }; OutOfMemoryError oome = null; InputStream bis = null; FileInputStream fis = null; FileChannel fic = null; boolean isMappedByteBufferInputStream = false; try { fis = new FileInputStream(testFile); if( SrcType.COPY == srcType ) { if( hasSize1 > Integer.MAX_VALUE ) { fis.close(); throw new IllegalArgumentException("file size > MAX_INT for "+srcType+": "+hasSize1+" of "+testFile); } bis = new BufferedInputStream(fis, bufferSize); } else if( SrcType.MMAP1 == srcType ) { if( hasSize1 > Integer.MAX_VALUE ) { fis.close(); throw new IllegalArgumentException("file size > MAX_INT for "+srcType+": "+hasSize1+" of "+testFile); } fic = fis.getChannel(); final MappedByteBuffer fmap = fic.map(FileChannel.MapMode.READ_ONLY, 0, hasSize1); // survives channel/stream closing until GC'ed! bis = new ByteBufferInputStream(fmap); } else { isMappedByteBufferInputStream = true; MappedByteBufferInputStream.CacheMode cmode; switch(srcType) { case MMAP2_NONE: cmode = MappedByteBufferInputStream.CacheMode.FLUSH_NONE; break; case MMAP2_SOFT: cmode = MappedByteBufferInputStream.CacheMode.FLUSH_PRE_SOFT; break; case MMAP2_HARD: cmode = MappedByteBufferInputStream.CacheMode.FLUSH_PRE_HARD; break; default: fis.close(); throw new InternalError("XX: "+srcType); } final MappedByteBufferInputStream mis = new MappedByteBufferInputStream(fis.getChannel(), FileChannel.MapMode.READ_ONLY, cmode); Assert.assertEquals(expSize, mis.remaining()); Assert.assertEquals(expSize, mis.length()); Assert.assertEquals(0, mis.position()); bis = mis; } } catch (final IOException e) { if( e.getCause() instanceof OutOfMemoryError ) { oome = (OutOfMemoryError) e.getCause(); // oops } else { ioe[0] = e; } } catch (final OutOfMemoryError m) { oome = m; // oops } IOException ioe2 = null; try { if( null != ioe[0] || null != oome ) { if( null != oome ) { System.err.printf("%s: mode %-5s, bufferSize %9d: OutOfMemoryError.1 %s%n", prefix, srcType.toString(), bufferSize, oome.getMessage()); oome.printStackTrace(); } else { Assert.assertNull(ioe[0]); } return false; } Assert.assertEquals(expSizeI, bis.available()); final long t1 = System.currentTimeMillis(); final File out = new File(fileOut); IOUtil.copyStream2File(bis, out, -1); final long t2 = System.currentTimeMillis(); final String suffix; if( isMappedByteBufferInputStream ) { suffix = ", cacheMode "+((MappedByteBufferInputStream)bis).getCacheMode(); } else { suffix = ""; } System.err.printf("%s: mode %-5s, bufferSize %9d: total %5d, setup %5d, copy %5d ms%s%n", prefix, srcType.toString(), bufferSize, t2-t0, t1-t0, t2-t1, suffix); Assert.assertEquals(expSize, out.length()); out.delete(); Assert.assertEquals(0, bis.available()); if( isMappedByteBufferInputStream ) { final MappedByteBufferInputStream mis = (MappedByteBufferInputStream)bis; Assert.assertEquals(0, mis.remaining()); Assert.assertEquals(expSize, mis.length()); Assert.assertEquals(expSize, mis.position()); } dumpMem(prefix+" after ", runtime, usedMem0[0], freeMem0[0], usedMem1, freeMem1 ); System.gc(); try { Thread.sleep(500); } catch (final InterruptedException e) { } dumpMem(prefix+" gc'ed ", runtime, usedMem0[0], freeMem0[0], usedMem1, freeMem1 ); } catch( final IOException e ) { if( e.getCause() instanceof OutOfMemoryError ) { oome = (OutOfMemoryError) e.getCause(); // oops } else { ioe2 = e; } } catch (final OutOfMemoryError m) { oome = m; // oops } finally { if( null != fic ) { fic.close(); } if( null != fis ) { fis.close(); } if( null != bis ) { bis.close(); } System.err.printf("%s: mode %-5s, bufferSize %9d: END%n", prefix, srcType.toString(), bufferSize); System.err.println(); } if( null != ioe2 || null != oome ) { if( null != oome ) { System.err.printf("%s: mode %-5s, bufferSize %9d: OutOfMemoryError.2 %s%n", prefix, srcType.toString(), bufferSize, oome.getMessage()); oome.printStackTrace(); } else { Assert.assertNull(ioe2); } return false; } else { return true; } } public static void dumpMem(final String pre, final Runtime runtime, final long usedMem0, final long freeMem0, final long[] usedMemN, final long[] freeMemN ) { usedMemN[0] = runtime.totalMemory() - runtime.freeMemory(); freeMemN[0] = runtime.freeMemory(); System.err.printf("%s Used Memory : "+PrintPrecision, pre, usedMemN[0] / MIB); if( 0 < usedMem0 ) { System.err.printf(", delta "+PrintPrecision, (usedMemN[0]-usedMem0) / MIB); } System.err.println(" MiB"); /** System.err.printf("%s Free Memory : "+printPrecision, pre, freeMemN[0] / mib); if( 0 < freeMem0 ) { System.err.printf(", delta "+printPrecision, (freeMemN[0]-freeMem0) / mib); } System.err.println(" MiB"); */ } static boolean manualTest = false; public static void main(final String args[]) throws IOException { for(int i=0; i<args.length; i++) { if(args[i].equals("-manual")) { manualTest = true; } } final String tstname = TestByteBufferInputStream.class.getName(); org.junit.runner.JUnitCore.main(tstname); } }