web-dev-qa-db-fra.com

boolean [] vs BitSet: Quel est le plus efficace?

Quoi de plus efficace en termes d'utilisation de la mémoire et du processeur - un tableau de booleans ou un BitSet? Des méthodes BitSet spécifiques ne sont pas utilisées, obtenez uniquement/set/clear (==, =, Arrays.fill respectivement pour un tableau).

61
Maxim

A partir de certains benchmarks avec Sun JDK 1.6 calculant des nombres premiers avec un tamis (le meilleur des 10 itérations pour se réchauffer, donner au compilateur JIT une chance et exclure les retards de planification aléatoires, Core 2 Duo T5600 1,83 GHz):

BitSet est plus efficace en mémoire que booléen [], sauf pour les très petites tailles. Chaque booléen du tableau prend un octet. Les chiffres de runtime.freeMemory () sont un peu confus pour BitSet, mais moins.

boolean [] est plus efficace en termes de CPU, sauf pour les très grandes tailles, où elles sont à peu près égales. Par exemple, pour une taille 1 million booléen [] est environ quatre fois plus rapide (par exemple 6 ms contre 27 ms), pour dix et cent millions, ils sont à peu près égaux.

38
starblue
  • Boolean[] utilise environ 4-20 octets par valeur booléenne.
  • boolean[] utilise environ 1 octet par valeur booléenne.
  • BitSet utilise environ 1 bit par valeur booléenne.

La taille de la mémoire pourrait ne pas être un problème pour vous, auquel cas booléen [] pourrait être plus simple à coder.

40
Peter Lawrey

Ici, vous pouvez voir un benchmark Mémoire/Temps comparant une matrice triangulaire booléenne [] [] à une matrice triangulaire BitSet [].

Je crée, définit et lis les valeurs (taille * (taille-1)/2) et compare l'utilisation de la mémoire et le temps ...

enter image description here

enter image description here

J'espère que cette aide ...

Voici le code ... (juste un code de test très sale, désolé;)

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

Un peu à gauche de votre question, mais si le stockage est un problème, vous voudrez peut-être examiner compression Huffman . Par exemple, 00000001 Pourrait être réduit par fréquence à quelque chose d'équivalent à {(7)0, (1)1}. Une chaîne plus "randomisée" 00111010 Nécessiterait une représentation plus complexe, par ex. {(2)0, (3)1, (1)0, (1)1, (1)0}, et occupe plus d'espace. En fonction de la structure de vos données binaires, vous pouvez tirer un avantage du stockage de son utilisation, au-delà de BitSet.

4
Alex Reynolds

Cela dépend comme toujours. Oui, BitSet est plus efficace en mémoire, mais dès que vous avez besoin d'un accès multithread, le booléen [] peut être le meilleur choix. Par exemple, pour calculer des nombres premiers, vous définissez uniquement le booléen sur true et vous n'avez donc pas vraiment besoin de synchronisation. Hans Boehm a écrit un article à ce sujet et la même technique peut être utilisée pour marquer les nœuds dans le graphique.

3
kohlerm

Quant à la mémoire, la documentation pour un BitSet a des implications assez claires. En particulier:

Chaque ensemble de bits a une taille actuelle, qui est le nombre de bits d'espace actuellement utilisé par l'ensemble de bits. Notez que la taille est liée à l'implémentation d'un ensemble de bits, elle peut donc changer avec l'implémentation. La longueur d'un ensemble de bits se rapporte à la longueur logique d'un ensemble de bits et est définie indépendamment de l'implémentation.

La source de Java classes de bibliothèque est ouvertement disponible et on peut facilement vérifier par eux-mêmes . En particulier:

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

Quant à la vitesse; cela dépend de ce que l'on fait. En général, ne pensez pas à la vitesse à l'avance; utilisez l'outil qui a le plus de sens sur le plan sémantique et conduit au code le plus clair. Optimisez uniquement après avoir observé que les exigences de performances ne sont pas remplies et identifié les goulots d'étranglement.

Venir à SO et demander si A est plus rapide que B est idiot pour de nombreuses raisons, y compris mais sans s'y limiter:

  1. Cela dépend de l'application, à laquelle personne qui répond n'a généralement accès. Analysez et profilez-le dans le contexte dans lequel il est utilisé. Assurez-vous qu'il s'agit d'un goulot d'étranglement qui mérite d'être optimisé.
  2. Des questions comme celle-ci qui portent sur la vitesse montrent généralement que le PO pense qu'il se soucie de l'efficacité, mais qu'il n'était pas disposé à établir un profil et n'a pas défini d'exigences de performance. Sous la surface, c'est généralement un drapeau rouge indiquant que l'OP se dirige dans le mauvais sens.

Je sais que c'est une vieille question, mais elle a été soulevée récemment; et je crois que cela vaut la peine d'être ajouté.

3
Jason C

Passer de Java à CPU est totalement VM spécifique. Par exemple, autrefois, un booléen était en fait implémenté en tant que valeur 32 bits (très probablement est vrai à ce jour).

À moins que vous ne sachiez que cela aura de l'importance, vous feriez mieux d'écrire le code pour être clair, de le profiler, puis de corriger les parties lentes ou consommant beaucoup de mémoire.

Vous pouvez le faire au fur et à mesure. Par exemple, j'ai décidé une fois de ne pas appeler .intern () sur Strings parce que lorsque j'exécutais le code dans le profileur, il le ralentissait trop (malgré l'utilisation de moins de mémoire).

1
TofuBeer