以下でchar[]
よりCharBuffer
を優先する理由はありますか?
CharBuffer buf = CharBuffer.allocate(DEFAULT_BUFFER_SIZE);
while( in.read(buf) >= 0 ) {
out.append( buf.flip() );
buf.clear();
}
vs.
char[] buf = new char[DEFAULT_BUFFER_SIZE];
int n;
while( (n = in.read(buf)) >= 0 ) {
out.write( buf, 0, n );
}
(ここで、in
はReader
内のout
およびWriter
です)?
いいえ、この場合、CharBuffer
を選択する理由は本当にありません。
ただし、一般的には、CharBuffer
(およびByteBuffer
)を使用すると、APIを本当に簡略化し、正しい処理を促進できます。パブリックAPIを設計している場合は、バッファ指向のAPIを検討する価値があります。
この比較をミニベンチマークしたかったのです。
以下は私が書いたクラスです。
問題は、CharBufferのパフォーマンスがそれほど悪いとは思えないことです。何がいけないのですか?
編集:以下の11番目のコメント以降、コードと出力時間を編集しました。全体的にパフォーマンスは向上していますが、時間には大きな違いがあります。out2.append((CharBuffer)buff.flip())オプションも試しました。コメントに記載されていますが、以下のコードで使用されている書き込みオプションよりもはるかに低速でした。
結果:(ミリ秒単位の時間)
文字[]:3411
CharBuffer:5653
public class CharBufferScratchBox
{
public static void main(String[] args) throws Exception
{
// Some Setup Stuff
String smallString =
"1111111111222222222233333333334444444444555555555566666666667777777777888888888899999999990000000000";
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < 1000; i++)
{
stringBuilder.append(smallString);
}
String string = stringBuilder.toString();
int DEFAULT_BUFFER_SIZE = 1000;
int ITTERATIONS = 10000;
// char[]
StringReader in1 = null;
StringWriter out1 = null;
Date start = new Date();
for (int i = 0; i < ITTERATIONS; i++)
{
in1 = new StringReader(string);
out1 = new StringWriter(string.length());
char[] buf = new char[DEFAULT_BUFFER_SIZE];
int n;
while ((n = in1.read(buf)) >= 0)
{
out1.write(
buf,
0,
n);
}
}
Date done = new Date();
System.out.println("char[] : " + (done.getTime() - start.getTime()));
// CharBuffer
StringReader in2 = null;
StringWriter out2 = null;
start = new Date();
CharBuffer buff = CharBuffer.allocate(DEFAULT_BUFFER_SIZE);
for (int i = 0; i < ITTERATIONS; i++)
{
in2 = new StringReader(string);
out2 = new StringWriter(string.length());
int n;
while ((n = in2.read(buff)) >= 0)
{
out2.write(
buff.array(),
0,
n);
buff.clear();
}
}
done = new Date();
System.out.println("CharBuffer: " + (done.getTime() - start.getTime()));
}
}
バッファでこれを行うのがこれだけの場合は、このインスタンスではおそらく配列の方が適しています。
CharBufferには追加のchromeがたくさんありますが、この場合はどれも関係ありません-物事を遅くするだけです。
より複雑にする必要がある場合は、後でいつでもリファクタリングできます。
実際の違いは実際には10%未満であり、他のユーザーが報告している30%ではありません。
5MBのファイルを24回読み書きするために、私の番号はプロファイラーを使用して取得しました。彼らは平均していた:
char[] = 4139 ms
CharBuffer = 4466 ms
ByteBuffer = 938 (direct) ms
個別のテストでは、CharBufferを数回使用しました。
また、ファイルベースのIOをインメモリIOに置き換えたところ、パフォーマンスはほぼ同じでした。あるネイティブストリームから別のネイティブストリームに転送しようとしている場合、の場合は、「直接」のByteBufferを使用するほうがよいでしょう。
パフォーマンスの違いが10%未満の場合、実際には、CharBufferを使用します。構文がより明確になり、無関係な変数が少なくなり、より直接的な操作(CharSequenceを要求するものなど)を実行できます。
ベンチマークは下にあります... BufferedReaderはテストメソッドの外側ではなく内側に割り当てられるため、少し間違っています...ただし、以下の例では、IO時間を分離して要因を排除できます。文字列やバイトストリームのように、内部メモリバッファのサイズを変更します。
public static void main(String[] args) throws Exception {
File f = getBytes(5000000);
System.out.println(f.getAbsolutePath());
try {
System.gc();
List<Main> impls = new Java.util.ArrayList<Main>();
impls.add(new CharArrayImpl());
//impls.add(new CharArrayNoBuffImpl());
impls.add(new CharBufferImpl());
//impls.add(new CharBufferNoBuffImpl());
impls.add(new ByteBufferDirectImpl());
//impls.add(new CharBufferDirectImpl());
for (int i = 0; i < 25; i++) {
for (Main impl : impls) {
test(f, impl);
}
System.out.println("-----");
if(i==0)
continue; //reset profiler
}
System.gc();
System.out.println("Finished");
return;
} finally {
f.delete();
}
}
static int BUFFER_SIZE = 1000;
static File getBytes(int size) throws IOException {
File f = File.createTempFile("input", ".txt");
FileWriter writer = new FileWriter(f);
Random r = new Random();
for (int i = 0; i < size; i++) {
writer.write(Integer.toString(5));
}
writer.close();
return f;
}
static void test(File f, Main impl) throws IOException {
InputStream in = new FileInputStream(f);
File fout = File.createTempFile("output", ".txt");
try {
OutputStream out = new FileOutputStream(fout, false);
try {
long start = System.currentTimeMillis();
impl.runTest(in, out);
long end = System.currentTimeMillis();
System.out.println(impl.getClass().getName() + " = " + (end - start) + "ms");
} finally {
out.close();
}
} finally {
fout.delete();
in.close();
}
}
public abstract void runTest(InputStream ins, OutputStream outs) throws IOException;
public static class CharArrayImpl extends Main {
char[] buff = new char[BUFFER_SIZE];
public void runTest(InputStream ins, OutputStream outs) throws IOException {
Reader in = new BufferedReader(new InputStreamReader(ins));
Writer out = new BufferedWriter(new OutputStreamWriter(outs));
int n;
while ((n = in.read(buff)) >= 0) {
out.write(buff, 0, n);
}
}
}
public static class CharBufferImpl extends Main {
CharBuffer buff = CharBuffer.allocate(BUFFER_SIZE);
public void runTest(InputStream ins, OutputStream outs) throws IOException {
Reader in = new BufferedReader(new InputStreamReader(ins));
Writer out = new BufferedWriter(new OutputStreamWriter(outs));
int n;
while ((n = in.read(buff)) >= 0) {
buff.flip();
out.append(buff);
buff.clear();
}
}
}
public static class ByteBufferDirectImpl extends Main {
ByteBuffer buff = ByteBuffer.allocateDirect(BUFFER_SIZE * 2);
public void runTest(InputStream ins, OutputStream outs) throws IOException {
ReadableByteChannel in = Channels.newChannel(ins);
WritableByteChannel out = Channels.newChannel(outs);
int n;
while ((n = in.read(buff)) >= 0) {
buff.flip();
out.write(buff);
buff.clear();
}
}
}
CharBufferとByteBuffer(および他のすべてのxBuffer)は再利用性を目的としていたので、毎回再割り当てを行う代わりに、それらをbuf.clear()できます。
それらを再利用しない場合、それらの能力を最大限に活用できず、余分なオーバーヘッドが追加されます。ただし、この関数のスケーリングを計画している場合、これをそこに保持することをお勧めします
CharBufferバージョンはやや複雑ではなく(1つは変数が少ない)、バッファーサイズの処理をカプセル化し、標準APIを利用します。一般的に私はこれを好みます。
ただし、少なくとも場合によっては、アレイバージョンを選択する1つの正当な理由がまだあります。 CharBufferはJava 1.4でのみ導入されたため、以前のバージョンにデプロイする場合はできません Charbufferを使用します(自分のロール/バックポートを使用する場合を除く) )。
PSバックポートを使用する場合は、バックポートされたコードの"real"バージョンを含むバージョンに追いついたら、必ず削除してください。
最近のJava=バージョンではCharBuffer
を避けてください。#subsequence()
にはバグがあります。バッファの後半からサブシーケンスを取得することはできません。実装がcapacity
とremaining
を混同しています。Java 6-0-11および6-0-12でバグを確認しました。