web-dev-qa-db-ja.com

boolean [] vs. BitSet:どちらがより効率的ですか?

booleansの配列またはBitSetの場合、メモリとCPU使用率の面でより効率的なものは何ですか?特定のBitSetメソッドは使用されず、get/set/clear(配列に対してそれぞれ==、=、Arrays.fill)のみが使用されます。

61
Maxim

ふるいを使用したSun JDK 1.6コンピューティングプライムのいくつかのベンチマークから(ウォームアップ、JITコンパイラーにチャンスを与え、ランダムスケジューリング遅延、Core 2 Duo T5600 1.83GHzを除外するための10回の反復のベスト):

BitSetは、非常に小さいサイズを除いて、boolean []よりもメモリ効率が高くなります。配列内の各ブール値は1バイトを取ります。 runtime.freeMemory()の数値は、BitSetには少し混乱しますが、それよりは少なくなります。

boolean []は、非常に大きなサイズを除いて、CPU効率が高く、ほぼ均等です。たとえば、サイズが100万の場合、boolean []は約4倍高速になります(6ミリ秒と27ミリ秒など)。1,000万と1億の場合、それらはほぼ均等です。

38
starblue
  • Boolean[]は、ブール値ごとに約4〜20バイトを使用します。
  • boolean[]は、ブール値ごとに約1バイトを使用します。
  • BitSetは、ブール値ごとに約1ビットを使用します。

メモリサイズは問題にならないかもしれませんが、その場合、boolean []の方がコーディングが簡単です。

40
Peter Lawrey

ここでは、boolean [] []三角行列とBitSet []三角行列を比較したMemory/Timeベンチマークを見ることができます。

(サイズ*(サイズ-1)/ 2)値を作成、設定、読み取り、メモリ使用量と時間を比較します...

enter image description here

enter image description here

この助けを願っています...

ここにコード...(ちょっと汚いテストコード、すみません;)

import Java.util.BitSet;
import Java.util.Date;

public class BooleanBitSetProfiler {

    Runtime runtime;
    int sum = 0;
    public void doIt() {

        runtime = Runtime.getRuntime();
        long[][] bitsetMatrix = new long[30][2];
        long[][] booleanMatrix = new long[30][2];
        int size = 1000;
        for (int i = 0; i < booleanMatrix.length; i++) {
            booleanMatrix[i] = testBooleanMatrix(size);
            bitsetMatrix[i] = testBitSet(size);
            size += 2000;
        }
        int debug = 1;
        for (int j = 0; j < booleanMatrix.length; j++){
            System.out.print(booleanMatrix[j][0] + ";");
        }
        System.out.println();
        for (int j = 0; j < booleanMatrix.length; j++){
            System.out.print(booleanMatrix[j][1] + ";");
        }
        System.out.println();
        for (int j = 0; j < bitsetMatrix.length; j++){
            System.out.print(bitsetMatrix[j][0] + ";");
        }
        System.out.println();
        for (int j = 0; j < bitsetMatrix.length; j++){
            System.out.print(bitsetMatrix[j][1] + ";");
        }
        System.out.println();
    }

    private long memory () {
        return runtime.totalMemory() - runtime.freeMemory();
    }
    private long[] testBooleanMatrix(int size) {
        runtime.gc();
        long startTime = new Date().getTime();
        long startMemory = memory();
        boolean[][] matrix = new boolean[size][];
        for (int i = 0; i < size; i++) {
            matrix[i] = new boolean[size - i - 1];
        }
        long creationMemory = memory();
        long creationTime = new Date().getTime();
        for (int i = 0; i < size; i++)  {
            for (int j = 0; j < matrix[i].length; j++) {
                matrix[i][j] = i % 2 == 0;
            }
        }
        long setMemory = memory();
        long setTime = new Date().getTime();
        for (int i = 0; i < size; i++)  {
            for (int j = 0; j < matrix[i].length; j++) {
                if (matrix[i][j]) sum++;
            }
        }
        long readTime = new Date().getTime();
        System.out.println("Boolean[][] (size " + size + ")");
        System.out.println("Creation memory " + printMem(creationMemory-startMemory) + ", set memory " + printMem(setMemory-startMemory));
        System.out.println("Creation time " + printTime(creationTime-startTime) + ", set time " + printTime(setTime - creationTime) + " read time " + printTime(readTime - setTime) + "\n");
        runtime.gc();
        return new long[]{(setMemory-startMemory)/(1024*1024), (readTime-startTime)};
    }
    private long[] testBitSet(int size) {
        runtime.gc();
        long startTime = new Date().getTime();
        long startMemory = memory();
        BitSet[] matrix = new BitSet[size];
        for (int i = 0; i < size; i++) {
            matrix[i] = new BitSet(size - i - 1);
        }
        long creationMemory = memory();
        long creationTime = new Date().getTime();
        for (int i = 0; i < size; i++)  {
            for (int j = 0; j < matrix[i].size(); j++) {
                matrix[i].set(j, (i % 2 == 0));
            }
        }
        long setMemory = memory();
        long setTime = new Date().getTime();
        for (int i = 0; i < size; i++)  {
            for (int j = 0; j < matrix[i].size(); j++) {
                if (matrix[i].get(j)) sum++;
            }
        }
        long readTime = new Date().getTime();
        System.out.println("BitSet[] (size " + size + ")");
        System.out.println("Creation memory " + printMem(creationMemory-startMemory) + ", set memory " + printMem(setMemory-startMemory));
        System.out.println("Creation time " + printTime(creationTime-startTime) + ", set time " + printTime(setTime - creationTime) + " read time " + printTime(readTime - setTime) + "\n");
        runtime.gc();
        return new long[]{(setMemory-startMemory)/(1024*1024), (readTime-startTime)};
    }

    private String printMem(long mem) {
        mem = mem / (1024*1024);
        return mem + "MB";
    }
    private String printTime(long milis) {
        int seconds = (int) (milis / 1000);
        milis = milis % 1000;
        return seconds > 0 ? seconds + "s " + milis + "ms" : milis + "ms";
    }
}
4
tagore84

あなたの質問の少し左のフィールドですが、ストレージが懸念される場合は、 ハフマン圧縮 を検討することをお勧めします。たとえば、_00000001_は、頻度によって{(7)0, (1)1}と同等のものに絞り込めます。より「ランダム化された」文字列_00111010_には、より複雑な表現が必要です。 {(2)0, (3)1, (1)0, (1)1, (1)0}、さらにスペースを取ります。ビットデータの構造によっては、BitSetを超えて使用すると、ストレージの利点が得られる場合があります。

4
Alex Reynolds

それはいつものように依存します。はい、BitSetはよりメモリ効率が高くなりますが、マルチスレッドアクセスが必要になったらすぐにboolean []を選択することをお勧めします。たとえば、素数を計算する場合は、ブール値をtrueに設定するだけなので、実際には同期は必要ありません。 Hans Boehm はこれについていくつかの論文を書いており、同じ手法をグラフのノードのマーキングに使用できます。

3
kohlerm

メモリに関しては、 BitSet のドキュメントにはかなり明確な意味があります。特に:

すべてのビットセットには現在のサイズがあります。これは、ビットセットが現在使用しているスペースのビット数です。サイズはビットセットの実装に関連しているため、実装によって変わる可能性があることに注意してください。ビットセットの長さはビットセットの論理長に関係し、実装とは無関係に定義されます。

Javaライブラリクラスのソースは公開されており、簡単に これを確認してください 。特に:

The internal field corresponding to the serialField "bits".
89 
90     private long[] words;

速度に関しては;それは何をしているのかに依存します。一般に、速度について事前に考えないでください。意味的に最も意味のあるツールを使用し、最も明確なコードを導きます。パフォーマンス要件が満たされていないことを観察し、ボトルネックを特定した後にのみ最適化します。

SOにアクセスして、AがBより速いかどうかを尋ねるのは、以下を含むがこれに限定されない確かな理由である。

  1. それはアプリケーションに依存しますが、通常は応答する誰もアクセスできません。使用されているコンテキストで分析し、プロファイルします。実際に最適化する価値のあるボトルネックであることを確認してください。
  2. このような速度についての質問は、一般に、OPは効率を重視しているが、プロファイリングを望んでおらず、パフォーマンス要件を定義していなかったことを示しています。水面下では、それは通常、OPが間違ったパスに向かっていることを示す赤い旗です。

これは古い質問ですが、最近出てきました。これは追加する価値があると思います。

3
Jason C

JavaからCPUへの移行は完全にVM特定です。たとえば、ブール値は実際には32ビット値として実装されていました(おそらくこの日に当てはまります)。

重要であることがわからない限り、コードを明確に記述し、プロファイルを作成してから、低速または大量のメモリを消費している部分を修正することをお勧めします。

あなたが行くようにこれを行うことができます。たとえば、一度プロファイラでコードを実行すると、(メモリの使用量が少なくても)遅くなりすぎたため、Stringsで.intern()を呼び出さないことにしました。

1
TofuBeer