質問
以下でCharBuffer
よりもchar[]
を好む理由はありますか:
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
を好む理由は本当にありません。
ただし、一般に、ByteBuffer
(および<=>)はAPIを実際に単純化し、正しい処理を促進できます。パブリックAPIを設計している場合、バッファ指向のAPIを検討する価値があります。
他のヒント
この比較をミニベンチマークしたかった。
以下は私が書いたクラスです。
問題は、CharBufferのパフォーマンスがそれほど悪いとは信じられないことです。何が間違っているのですか?
EDIT:以下の11番目のコメント以降、コードと出力時間を編集したので、全体的にパフォーマンスが向上しましたが、それでも時間には大きな違いがあります。また、コメントに記載されているout2.append((CharBuffer)buff.flip())オプションも試しましたが、以下のコードで使用されている書き込みオプションよりもはるかに低速でした。
結果:(ミリ秒単位の時間)
char []: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には追加のクロムがたくさんありますが、この場合は関係ありません-ほんの少しだけ遅くなります。
もっと複雑にする必要がある場合は、いつでも後でリファクタリングできます。
実際には、違いは実際には<!> lt; 10%であり、他の人が報告している30%ではありません。
5MBのファイルを24回読み書きするには、プロファイラーを使用して数字を取得しました。彼らは平均していた:
char[] = 4139 ms
CharBuffer = 4466 ms
ByteBuffer = 938 (direct) ms
個人テストでは、CharBufferが数回使用されました。
また、ファイルベースのIOをインメモリIOに置き換えようとしましたが、パフォーマンスは同様でした。あるネイティブストリームから別のネイティブストリームに転送しようとしている場合は、<!> quot; direct <!> quot; ByteBuffer。
実際には、10%未満のパフォーマンスの違いで、CharBufferを優先します。構文はより明確で、無関係な変数は少なく、より直接的な操作(つまり、CharSequenceを要求するもの)を行うことができます。
ベンチマークは以下です... BufferedReaderは外部ではなくテストメソッドの内部に割り当てられているため、少し間違っています...内部メモリバッファのサイズ変更など
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を使用することはできません(自分でロールを作成/バックポートを使用しない限り)。
P.Sバックポートを使用する場合、バックポートされたコードの <!> quot; real <!> quot; バージョンを含むバージョンに追いついたら、それを忘れずに削除してください。
最近のJavaバージョンではCharBuffer
を避けてください。#subsequence()
にはバグがあります。実装がcapacity
とremaining
を混同するため、バッファの後半からサブシーケンスを取得できません。 Java 6-0-11および6-0-12のバグを確認しました。