/** * Copyright 2011 LiveRamp * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.liveramp.hank.performance; import com.liveramp.hank.util.FormatUtils; import com.liveramp.hank.util.HankTimer; import com.liveramp.hank.util.HankTimerEventAggregator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.util.Random; public class RandomReadPerformance { private static final Logger LOG = LoggerFactory.getLogger(RandomReadPerformance.class); private static final int NUM_RANDOM_READS = 8 << 10; private static final int NUM_RANDOM_READ_THREADS = 8; private static final int NUM_OPTIONS = 1; public static void main(String[] args) throws IOException, InterruptedException { int randomReadBufferSize = Integer.valueOf(args[0]); long totalRandomReads = NUM_RANDOM_READS * NUM_RANDOM_READ_THREADS; File[] testFiles = new File[args.length - NUM_OPTIONS]; for (int i = NUM_OPTIONS; i < args.length; ++i) { testFiles[i - NUM_OPTIONS] = new File(args[i]); LOG.info("Using test file: " + testFiles[i - NUM_OPTIONS].getAbsolutePath()); } Thread[] threads = new Thread[NUM_RANDOM_READ_THREADS]; for (int i = 0; i < NUM_RANDOM_READ_THREADS; ++i) { threads[i] = new Thread(new RandomReadsRunnable(testFiles, randomReadBufferSize)); } LOG.info("Calculating time taken to perform " + totalRandomReads + " random " + FormatUtils.formatNumBytes(randomReadBufferSize) + " reads in " + NUM_RANDOM_READ_THREADS + " threads (" + NUM_RANDOM_READS + " random reads each)"); long startTime = System.currentTimeMillis(); for (Thread thread : threads) { thread.start(); } for (Thread thread : threads) { thread.join(); } long totalDuration = System.currentTimeMillis() - startTime; LOG.info("Total duration: " + totalDuration + " ms"); LOG.info("Total throughput: " + ((double) totalRandomReads / (totalDuration / 1000.0)) + " random reads per second"); LOG.info("Total throughput: " + FormatUtils.formatNumBytes((long) ((totalRandomReads * randomReadBufferSize) / (totalDuration / 1000.0))) + "/s"); } private static class RandomReadsRunnable implements Runnable { private final FileChannel[] testChannels; private final HankTimerEventAggregator timerAggregator; private final int randomReadBufferSize; public RandomReadsRunnable(File[] testFiles, int randomReadBufferSize) throws FileNotFoundException { // Open file channels testChannels = new FileChannel[testFiles.length]; for (int i = 0; i < testFiles.length; ++i) { testChannels[i] = new FileInputStream(testFiles[i]).getChannel(); } timerAggregator = new HankTimerEventAggregator("Random reads", 1); this.randomReadBufferSize = randomReadBufferSize; } @Override public void run() { try { Random random = new Random(); // Perform random reads byte[] readBufferArray = new byte[randomReadBufferSize]; ByteBuffer readBuffer = ByteBuffer.wrap(readBufferArray); HankTimer timer = timerAggregator.getTimer(); for (int i = 0; i < NUM_RANDOM_READS; ++i) { readBuffer.clear(); FileChannel testChannel = testChannels[i % testChannels.length]; long randomPosition = Math.abs(random.nextLong()) % (testChannel.size() - randomReadBufferSize); testChannel.position(randomPosition) .read(readBuffer); } timerAggregator.add(timer, NUM_RANDOM_READS); // Close file channels for (FileChannel testChannel : testChannels) { testChannel.close(); } } catch (Exception e) { LOG.error("Failed to perform random reads", e); } } } }