Last active
August 29, 2015 14:17
-
-
Save tyru/204f22473ecc6448706b to your computer and use it in GitHub Desktop.
『Re: InputStreamからStringへの変換』の記事のコードはNIO使えばもっと速くなるんじゃないかと思ってベンチマーク取ってみた
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.io.ByteArrayOutputStream; | |
import java.io.FileInputStream; | |
import java.io.FileNotFoundException; | |
import java.io.IOException; | |
import java.io.InputStream; | |
import java.io.InputStreamReader; | |
import java.math.BigDecimal; | |
import java.math.RoundingMode; | |
import java.nio.ByteBuffer; | |
import java.nio.channels.Channels; | |
import java.nio.channels.ReadableByteChannel; | |
import java.nio.charset.Charset; | |
import java.util.Arrays; | |
import java.util.concurrent.TimeUnit; | |
public class BenchmarkMain { | |
private static final int BUFFER_SIZE = 1024; | |
private static final Charset UTF16 = Charset.forName("UTF-16"); | |
public static final String TEST_FILE_NAME = "test.txt"; | |
public static final boolean PRINT_EACH_RESULT = false; | |
private static final int COUNT = 100; | |
private static final int MEDIAN_SAMPLE_NUM = 30; | |
private static final TimeUnit RESULT_TIME_UNIT = TimeUnit.NANOSECONDS; | |
public static void main(String[] args) { | |
Task t1 = new Task("convertInputStreamToString()") { | |
public void run() throws Exception { | |
convertInputStreamToString(this.is); | |
} | |
}; | |
Task t2 = new Task("convertInputStreamToStringWithCharset()") { | |
public void run() throws Exception { | |
convertInputStreamToStringWithCharset(this.is, UTF16); | |
} | |
}; | |
Task t3 = new Task("convertInputStreamToStringNio()") { | |
public void run() throws Exception { | |
convertInputStreamToStringNio(is); | |
} | |
}; | |
Task t4 = new Task("convertInputStreamToStringNioWithCharset()") { | |
public void run() throws Exception { | |
convertInputStreamToStringNioWithCharset(is, UTF16); | |
} | |
}; | |
Task t5 = new Task("convertInputStreamToStringNioDirectBuffer()") { | |
public void run() throws Exception { | |
convertInputStreamToStringNioDirectBuffer(is); | |
} | |
}; | |
Task t6 = new Task("convertInputStreamToStringNioDirectBufferWithCharset()") { | |
public void run() throws Exception { | |
convertInputStreamToStringNioDirectBufferWithCharset(is, UTF16); | |
} | |
}; | |
try { | |
Task[] tasks = { t1, t2, t3, t4, t5, t6 }; | |
// Task[] tasks = { t1, t2, t3, t4 }; | |
for (Task task : tasks) { | |
task.start(); | |
} | |
System.out.println("Result:"); | |
for (Task task : tasks) { | |
task.printResult(); | |
} | |
System.out.println(); | |
} catch (Exception e) { | |
e.printStackTrace(); | |
System.exit(1); | |
} | |
} | |
static abstract class Task { | |
private final String taskName; | |
protected InputStream is; | |
private long[] resultTimes = new long[COUNT]; | |
public Task(String taskName) { | |
this.taskName = taskName; | |
} | |
abstract public void run() throws Exception; | |
public void start() throws Exception { | |
this.is = getTestInputStream(); | |
for (int i = 0; i < COUNT; i++) { | |
long start = System.nanoTime(); | |
this.run(); | |
long end = System.nanoTime(); | |
this.resultTimes[i] = end - start; | |
} | |
} | |
public void printResult() { | |
Arrays.sort(this.resultTimes); | |
if (PRINT_EACH_RESULT) { | |
for (int i = 0; i < resultTimes.length; i++) { | |
long resultTime = this.resultTimes[i]; | |
System.out.println(" " + this.taskName + " : " | |
+ RESULT_TIME_UNIT.convert(resultTime, TimeUnit.NANOSECONDS) | |
+ " " + RESULT_TIME_UNIT.name().toLowerCase()); | |
} | |
} | |
long resultTime = calcMedian(); | |
System.out.printf(" %s (median %d/%d) : %d %s\n", | |
this.taskName, | |
MEDIAN_SAMPLE_NUM, | |
COUNT, | |
RESULT_TIME_UNIT.convert(resultTime, TimeUnit.NANOSECONDS), | |
RESULT_TIME_UNIT.name().toLowerCase()); | |
} | |
private long calcMedian() { | |
assert COUNT >= MEDIAN_SAMPLE_NUM; | |
// this.resultTimes must bo sorted. | |
// Arrays.sort(this.resultTimes); | |
BigDecimal sum = new BigDecimal(0); | |
// [begin, end) | |
int begin = (this.resultTimes.length - MEDIAN_SAMPLE_NUM) / 2; | |
int end = begin + MEDIAN_SAMPLE_NUM; | |
for (int i = begin; i < end; i++) { | |
sum = sum.add(new BigDecimal(this.resultTimes[i])); | |
} | |
return sum.divide(new BigDecimal(MEDIAN_SAMPLE_NUM), 0, RoundingMode.HALF_UP).longValueExact(); | |
} | |
} | |
private static InputStream getTestInputStream() throws FileNotFoundException { | |
return new FileInputStream("test.txt"); | |
} | |
/** | |
* thincaさんが教えてくれたコード | |
* @param is | |
* @return String | |
* @throws IOException | |
*/ | |
static String convertInputStreamToString(InputStream is) throws IOException { | |
InputStreamReader reader = new InputStreamReader(is); | |
StringBuilder builder = new StringBuilder(); | |
char[] buf = new char[BUFFER_SIZE]; | |
int numRead; | |
while (0 <= (numRead = reader.read(buf))) { | |
builder.append(buf, 0, numRead); | |
} | |
return builder.toString(); | |
} | |
/** | |
* thincaさんが教えてくれたコードをCharset指定できるようにしたメソッド | |
* @param is | |
* @return String | |
* @throws IOException | |
*/ | |
static String convertInputStreamToStringWithCharset(InputStream is, Charset charset) throws IOException { | |
InputStreamReader reader = new InputStreamReader(is, charset); | |
StringBuilder builder = new StringBuilder(); | |
char[] buf = new char[BUFFER_SIZE]; | |
int numRead; | |
while (0 <= (numRead = reader.read(buf))) { | |
builder.append(buf, 0, numRead); | |
} | |
return builder.toString(); | |
} | |
/** | |
* NIO使うようにしたメソッド(`allocate()`版) | |
* @param is | |
* @return String | |
* @throws IOException | |
*/ | |
static String convertInputStreamToStringNio(InputStream is) throws IOException { | |
ByteArrayOutputStream result = new ByteArrayOutputStream(); | |
ReadableByteChannel channel = Channels.newChannel(is); | |
ByteBuffer buf = ByteBuffer.allocate(BUFFER_SIZE); | |
buf.mark(); | |
int numRead; | |
while (0 <= (numRead = channel.read(buf))) { | |
result.write(buf.array(), 0, numRead); | |
buf.reset(); | |
} | |
return result.toString(); | |
} | |
/** | |
* NIO使うようにしたメソッド(`allocate()`版)をCharset指定できるようにしたメソッド | |
* @param is | |
* @param charset | |
* @return String | |
* @throws IOException | |
*/ | |
static String convertInputStreamToStringNioWithCharset(InputStream is, Charset charset) throws IOException { | |
ByteArrayOutputStream result = new ByteArrayOutputStream(); | |
ReadableByteChannel channel = Channels.newChannel(is); | |
ByteBuffer buf = ByteBuffer.allocate(BUFFER_SIZE); | |
buf.mark(); | |
int numRead; | |
while (0 <= (numRead = channel.read(buf))) { | |
result.write(buf.array(), 0, numRead); | |
buf.reset(); | |
} | |
return result.toString(charset.name()); | |
} | |
/** | |
* NIO使うようにしたメソッド(`allocateDirect()`版) | |
* @param is | |
* @return String | |
* @throws IOException | |
*/ | |
static String convertInputStreamToStringNioDirectBuffer(InputStream is) throws IOException { | |
ByteArrayOutputStream result = new ByteArrayOutputStream(); | |
ReadableByteChannel channel = Channels.newChannel(is); | |
ByteBuffer buf = ByteBuffer.allocateDirect(BUFFER_SIZE); | |
buf.mark(); | |
byte[] rawBuf = new byte[BUFFER_SIZE]; | |
int numRead; | |
while (0 <= (numRead = channel.read(buf))) { | |
buf.reset(); | |
buf.get(rawBuf, 0, numRead); | |
result.write(rawBuf, 0, numRead); | |
buf.reset(); | |
} | |
return result.toString(); | |
} | |
/** | |
* NIO使うようにしたメソッド(`allocateDirect()`版)をCharset指定できるようにしたメソッド | |
* @param is | |
* @param charset | |
* @return String | |
* @throws IOException | |
*/ | |
static String convertInputStreamToStringNioDirectBufferWithCharset(InputStream is, Charset charset) throws IOException { | |
ByteArrayOutputStream result = new ByteArrayOutputStream(); | |
ReadableByteChannel channel = Channels.newChannel(is); | |
ByteBuffer buf = ByteBuffer.allocateDirect(BUFFER_SIZE); | |
buf.mark(); | |
byte[] rawBuf = new byte[BUFFER_SIZE]; | |
int numRead; | |
while (0 <= (numRead = channel.read(buf))) { | |
buf.reset(); | |
buf.get(rawBuf, 0, numRead); | |
result.write(rawBuf, 0, numRead); | |
buf.reset(); | |
} | |
return result.toString(charset.name()); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.io.FileNotFoundException; | |
import java.io.FileOutputStream; | |
import java.io.IOException; | |
import java.nio.file.Files; | |
import java.nio.file.Paths; | |
public class CreateTestFile { | |
// Please modify the following constants. | |
// private static final int BUFFER_SIZE = 128 * 1024 * 1024; /* 128MiB */ | |
// private static final int FILE_SIZE = 1024 * 1024 * 1024; /* 1GiB */ | |
private static final int BUFFER_SIZE = 8 * 1024 * 1024; /* 8MiB */ | |
private static final int FILE_SIZE = 32 * 1024 * 1024; /* 32MiB */ | |
public static void main(String[] args) { | |
// Create test data. | |
byte[] buf = new byte[BUFFER_SIZE]; | |
for (int i = 0; i < buf.length; i++) { | |
buf[i] = '0'; // file is filled with character '0' | |
} | |
// Delete test file if it exists. | |
try { | |
Files.deleteIfExists(Paths.get(BenchmarkMain.TEST_FILE_NAME)); | |
} catch (IOException e) { | |
e.printStackTrace(); | |
System.exit(1); | |
} | |
final int loopWriteNum = FILE_SIZE / BUFFER_SIZE; | |
final int lastWriteNum = FILE_SIZE % BUFFER_SIZE; | |
final boolean append = true; | |
try (FileOutputStream out = new FileOutputStream(BenchmarkMain.TEST_FILE_NAME, append)) { | |
for (int i = 0; i < loopWriteNum; i++) { | |
out.write(buf); | |
System.out.printf("Written %d/%d byte(s).\n", (i + 1) * BUFFER_SIZE, FILE_SIZE); | |
} | |
if (lastWriteNum > 0) { | |
out.write(buf, 0, lastWriteNum); | |
System.out.printf("Written %d/%d byte(s).\n", FILE_SIZE, FILE_SIZE); | |
} | |
System.out.println("Done."); | |
} catch (FileNotFoundException e) { | |
e.printStackTrace(); | |
} catch (IOException e) { | |
e.printStackTrace(); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment