Comment remplir / instancier un tableau C # avec une seule valeur?

205

Je sais que les tableaux instanciés de types de valeurs en C # sont automatiquement remplis avec la valeur par défaut du type (par exemple, false pour bool, 0 pour int, etc.).

Existe-t-il un moyen de remplir automatiquement un tableau avec une valeur de départ qui n'est pas la valeur par défaut? Soit lors de la création ou d'une méthode intégrée par la suite (comme Arrays.fill () de Java )? Disons que je voulais un tableau booléen qui était vrai par défaut, au lieu de faux. Existe-t-il un moyen intégré de le faire, ou avez-vous juste à parcourir le tableau avec une boucle for?

 // Example pseudo-code:
 bool[] abValues = new[1000000];
 Array.Populate(abValues, true);

 // Currently how I'm handling this:
 bool[] abValues = new[1000000];
 for (int i = 0; i < 1000000; i++)
 {
     abValues[i] = true;
 }

Devoir parcourir le tableau et "réinitialiser" chaque valeur à true semble inefficace. Y a-t-il de toute façon autour de ça? Peut-être en inversant toutes les valeurs?

Après avoir tapé cette question et y avoir réfléchi, je suppose que les valeurs par défaut sont simplement le résultat de la façon dont C # gère l'allocation de mémoire de ces objets en arrière-plan, donc j'imagine que ce n'est probablement pas possible de le faire. Mais j'aimerais quand même savoir avec certitude!

patjbs
la source
Je change généralement le nom de is_found en is_still_hiding. J'adore les réponses, j'avais besoin de faire la même chose pour le tableau d'int dans un cas de test. (bonne question)
ctrl-alt-delor

Réponses:

146

Je ne connais pas de méthode de framework mais vous pouvez écrire une aide rapide pour le faire pour vous.

public static void Populate<T>(this T[] arr, T value ) {
  for ( int i = 0; i < arr.Length;i++ ) {
    arr[i] = value;
  }
}
JaredPar
la source
3
Préférez ++ i au lieu d'i ++ si vous n'avez pas besoin de la copie.
void.pointer
24
i ++ copie i, incrémente i et renvoie la valeur d'origine. ++ i renvoie juste la valeur incrémentée. Par conséquent ++ i est plus rapide, ce qui peut être significatif dans les grandes boucles comme nous en parlons ici.
tenpn
57
@RobertDailey: C'est une optimisation du compilateur, et ce n'est plus vrai. Je viens de tester pour vérifier ma croyance: si la valeur de retour de i ++ n'est utilisée pour rien, le compilateur la compilera automatiquement en ++ i pour vous. De plus, même lorsque j'utilise la valeur de retour, la différence de performance est si faible que je devais faire un cas extrême pour la mesurer. Même alors, cela n'a entraîné que quelques pour cent d'exécution différente.
Edward Ned Harvey
8
J'ai écrit une méthode d'extension comme celle-ci mais je l'ai fait renvoyer le tableau d'origine pour permettre le chaînage de méthodes telles que:int[] arr = new int[16].Populate(-1);
Gutblender
2
Passez voidà T[]et ensuite vous pouvez le fairevar a = new int[100].Polupate(1)
orad
198
Enumerable.Repeat(true, 1000000).ToArray();
Rony
la source
70
Bien que cela fonctionne, ce n'est pas vraiment une bonne solution car c'est très lent; c'est environ 4 fois plus lent que l'itération avec une boucle for en fait.
patjbs
4
oui c'est vrai, quand on considère les performances, la boucle for est plus rapide
Rony
6
Pour voir un véritable benchmark, jetez un œil à C # Initialize Array .
theknut
4
Enumerable.ToArrayne connaît pas la taille de la séquence énumérable, il doit donc deviner la taille du tableau. Cela signifie que vous obtiendrez des allocations de tableau chaque fois que ToArrayle tampon est dépassé, plus une allocation de plus à la fin pour le découpage. Il y a aussi des frais généraux impliqués avec l'objet énumérable.
Edward Brey
5
Juste une note, qu'avec les types de référence, cela remplira tout le tableau avec toutes les références au même objet unique. Si ce n'est pas ce que vous voulez et que vous souhaitez réellement générer des objets différents pour chaque élément du tableau, voir stackoverflow.com/a/44937053/23715 .
Alex Che
74

Créez un nouveau tableau avec mille truevaleurs:

var items = Enumerable.Repeat<bool>(true, 1000).ToArray();  // Or ToList(), etc.

De même, vous pouvez générer des séquences entières:

var items = Enumerable.Range(0, 1000).ToArray();  // 0..999
bytebender
la source
8
Pas mal, mais c'est toujours plus lent qu'une boucle for d'un facteur 4x
environ
1
patjbs en théorie dans le futur Enumerable.Repeat fonctionnera plus rapidement car il utilisera une implémentation parallèle.
Petar Petrov
1
@PetarPetrov Cela ne se produira jamais en raison de la destruction du cache. Je suis assez certain qu'en raison de la nature du cache du processeur, effectuer un travail en parallèle sur une seule baie sera toujours plus lent, peu importe quoi, car l'ordinateur attend un travail synchrone et charge les données de manière appropriée.
TernaryTopiary
pessimisation prévue! = manque d'optimisation prématurée.
Denis Gladkiy
24

Pour les grands tableaux ou les tableaux de taille variable, vous devriez probablement utiliser:

Enumerable.Repeat(true, 1000000).ToArray();

Pour les petits tableaux, vous pouvez utiliser la syntaxe d'initialisation de la collection en C # 3:

bool[] vals = new bool[]{ false, false, false, false, false, false, false };

L'avantage de la syntaxe d'initialisation de la collection est que vous n'avez pas à utiliser la même valeur dans chaque emplacement et que vous pouvez utiliser des expressions ou des fonctions pour initialiser un emplacement. De plus, je pense que vous évitez le coût de l'initialisation de l'emplacement de la baie à la valeur par défaut. Ainsi, par exemple:

bool[] vals = new bool[]{ false, true, false, !(a ||b) && c, SomeBoolMethod() };
LBushkin
la source
Et pour initialiser un tableau float []:float[] AlzCalDefault = new float[] {(float) 0.5, 18, 500, 1, 0};
Jim Lahman
L'initialisation FWIW d'un tableau peut être effectuée dans n'importe quelle version de C # comme:bool[] vals = { false, true, false, !(a || b) && c, SomeBoolMethod() };
Peter van der Heijden
24

Si votre tableau est si grand, vous devez utiliser BitArray. Il utilise 1 bit pour chaque booléen au lieu d'un octet (comme dans un tableau de booléens), vous pouvez également définir tous les bits sur true avec les opérateurs de bits. Ou tout simplement initialiser sur vrai. Si vous ne devez le faire qu'une seule fois, cela ne coûtera que plus.

System.Collections.BitArray falses = new System.Collections.BitArray(100000, false);
System.Collections.BitArray trues = new System.Collections.BitArray(100000, true);

// Now both contain only true values.
falses.And(trues);
MrFox
la source
17

Vous pouvez utiliser Array.Filldans .NET Core 2.0+ et .NET Standard 2.1+.

juFo
la source
Excellent! Mais sachez qu'il s'agit d'une méthode relativement nouvelle. Il est disponible dans .NET de base 2.0+ et Standard .NET 2.1, mais en particulier pas dans l' une des versions de .NET Framework. (il sera dans .NET 5.0, qui mélange .NET Framework et .NET Core ensemble).
Abel
9

malheureusement, je ne pense pas qu'il existe un moyen direct, mais je pense que vous pouvez écrire une méthode d'extension pour la classe de tableau pour ce faire

class Program
{
    static void Main(string[] args)
    {
        int[] arr = new int[1000];
        arr.Init(10);
        Array.ForEach(arr, Console.WriteLine);
    }
}

public static class ArrayExtensions
{
    public static void Init<T>(this T[] array, T defaultVaue)
    {
        if (array == null)
            return;
        for (int i = 0; i < array.Length; i++)
        {
            array[i] = defaultVaue;
        }
    }
}
bashmohandes
la source
J'aime l'idée d'extension plus j'y approfondis. Parfois, la solution initiale et simple est vraiment la meilleure!
patjbs
8

Eh bien, après un peu plus de recherche sur Google et de lecture, j'ai trouvé ceci:

bool[] bPrimes = new bool[1000000];
bPrimes = Array.ConvertAll<bool, bool>(bPrimes, b=> b=true);

Ce qui est certainement plus proche de ce que je recherche. Mais je ne sais pas si c'est mieux que d'itérer à travers le tableau d'origine dans une boucle for et de simplement changer les valeurs. Après un test rapide en fait, il semble plus lent d'environ un facteur 5. Donc ce n'est pas vraiment une bonne solution!

patjbs
la source
4
c'est similaire à ce que vous essayez de faire, sauf qu'il fait un appel de fonction pour chaque élément de votre tableau. Cela peut sembler beaucoup plus agréable syntaxiquement, mais cela fait beaucoup plus de travail ...
Nader Shirazie
ouais, ça ressemble à une simple boucle pour faire le travail à peu près aussi bien qu'autre chose
patjbs
Il crée un nouveau tableau (ne change pas l'instance d'origine).
Jeppe Stig Nielsen
7

Qu'en est-il d'une implémentation parallèle

public static void InitializeArray<T>(T[] array, T value)
{
    var cores = Environment.ProcessorCount;

    ArraySegment<T>[] segments = new ArraySegment<T>[cores];

    var step = array.Length / cores;
    for (int i = 0; i < cores; i++)
    {
        segments[i] = new ArraySegment<T>(array, i * step, step);
    }
    var remaining = array.Length % cores;
    if (remaining != 0)
    {
        var lastIndex = segments.Length - 1;
        segments[lastIndex] = new ArraySegment<T>(array, lastIndex * step, array.Length - (lastIndex * step));
    }

    var initializers = new Task[cores];
    for (int i = 0; i < cores; i++)
    {
        var index = i;
        var t = new Task(() =>
        {
            var s = segments[index];
            for (int j = 0; j < s.Count; j++)
            {
                array[j + s.Offset] = value;
            }
        });
        initializers[i] = t;
        t.Start();
    }

    Task.WaitAll(initializers);
}

Lors de l'initialisation d'un tableau uniquement, la puissance de ce code ne peut pas être vue, mais je pense que vous devez absolument oublier le "pur" pour.

Petar Petrov
la source
Cela risque de poser un problème de faux partage, dans lequel différents threads rivalisent pour les lignes de cache du processeur et donc de réduire les performances par rapport à une implémentation à un seul thread. Que cela se produise dépend de la taille des blocs de mémoire par thread et de l'architecture du processeur.
Eric
7

Le code ci-dessous combine une itération simple pour les petites copies et Array.Copy pour les grandes copies

    public static void Populate<T>( T[] array, int startIndex, int count, T value ) {
        if ( array == null ) {
            throw new ArgumentNullException( "array" );
        }
        if ( (uint)startIndex >= array.Length ) {
            throw new ArgumentOutOfRangeException( "startIndex", "" );
        }
        if ( count < 0 || ( (uint)( startIndex + count ) > array.Length ) ) {
            throw new ArgumentOutOfRangeException( "count", "" );
        }
        const int Gap = 16;
        int i = startIndex;

        if ( count <= Gap * 2 ) {
            while ( count > 0 ) {
                array[ i ] = value;
                count--;
                i++;
            }
            return;
        }
        int aval = Gap;
        count -= Gap;

        do {
            array[ i ] = value;
            i++;
            --aval;
        } while ( aval > 0 );

        aval = Gap;
        while ( true ) {
            Array.Copy( array, startIndex, array, i, aval );
            i += aval;
            count -= aval;
            aval *= 2;
            if ( count <= aval ) {
                Array.Copy( array, startIndex, array, i, count );
                break;
            }
        }
    }

Les repères pour différentes longueurs de tableau à l'aide d'un tableau int [] sont:

         2 Iterate:     1981 Populate:     2845
         4 Iterate:     2678 Populate:     3915
         8 Iterate:     4026 Populate:     6592
        16 Iterate:     6825 Populate:    10269
        32 Iterate:    16766 Populate:    18786
        64 Iterate:    27120 Populate:    35187
       128 Iterate:    49769 Populate:    53133
       256 Iterate:   100099 Populate:    71709
       512 Iterate:   184722 Populate:   107933
      1024 Iterate:   363727 Populate:   126389
      2048 Iterate:   710963 Populate:   220152
      4096 Iterate:  1419732 Populate:   291860
      8192 Iterate:  2854372 Populate:   685834
     16384 Iterate:  5703108 Populate:  1444185
     32768 Iterate: 11396999 Populate:  3210109

Les premières colonnes sont la taille du tableau, suivie du temps de copie à l'aide d'une simple itération (implémentation @JaredPared). Le temps de cette méthode est après cela. Ce sont les repères utilisant un tableau d'une structure de quatre entiers

         2 Iterate:     2473 Populate:     4589
         4 Iterate:     3966 Populate:     6081
         8 Iterate:     7326 Populate:     9050
        16 Iterate:    14606 Populate:    16114
        32 Iterate:    29170 Populate:    31473
        64 Iterate:    57117 Populate:    52079
       128 Iterate:   112927 Populate:    75503
       256 Iterate:   226767 Populate:   133276
       512 Iterate:   447424 Populate:   165912
      1024 Iterate:   890158 Populate:   367087
      2048 Iterate:  1786918 Populate:   492909
      4096 Iterate:  3570919 Populate:  1623861
      8192 Iterate:  7136554 Populate:  2857678
     16384 Iterate: 14258354 Populate:  6437759
     32768 Iterate: 28351852 Populate: 12843259
Panos Theof
la source
7

Ou ... vous pouvez simplement utiliser une logique inversée. Soit falseméchant trueet vice versa.

Exemple de code

// bool[] isVisible = Enumerable.Repeat(true, 1000000).ToArray();
bool[] isHidden = new bool[1000000]; // Crazy-fast initialization!

// if (isVisible.All(v => v))
if (isHidden.All(v => !v))
{
    // Do stuff!
}
l33t
la source
solution amusante, bien que ce soit beaucoup plus difficile avec des ints par exemple parce que vous perdez le 0.
MrFox
1
c'est en fait une option viable si vous "inversez la logique" sur le nom de la variable: au lieu de le bool[] isVisiblefairebool[] isHidden
Markus Hütter
1
Les gens semblent réagir comme si c'était une sorte de hack drôle. C'est une technique d'optimisation courante. Si vous avez de la chance, le compilateur le fera pour vous.
l33t
4

cela fonctionne aussi ... mais pourrait être inutile

 bool[] abValues = new bool[1000];
 abValues = abValues.Select( n => n = true ).ToArray<bool>();
Stan R.
la source
4

La plupart des réponses présentées ici se résument à une boucle qui initialise le tableau un élément à la fois, qui ne tire pas parti des instructions du processeur conçues pour fonctionner sur un bloc de mémoire à la fois.

.Net Standard 2.1 (dans l'aperçu au moment de la rédaction de cet article) fournit Array.Fill () , qui se prête à une implémentation hautes performances dans la bibliothèque d'exécution (bien que pour l'instant, .NET Core ne semble pas tirer parti de cette possibilité) .

Pour ceux sur les plates-formes antérieures, la méthode d'extension suivante surpasse une boucle triviale d'une marge substantielle lorsque la taille du tableau est importante. Je l'ai créé lorsque ma solution pour un défi de code en ligne était d'environ 20% supérieure au budget alloué. Il a réduit le temps d'exécution d'environ 70%. Dans ce cas, le remplissage du tableau a été effectué dans une autre boucle. BLOCK_SIZE a été défini par l'intestin plutôt que par l'expérience. Certaines optimisations sont possibles (par exemple, copier tous les octets déjà définis à la valeur souhaitée plutôt qu'un bloc de taille fixe).

internal const int BLOCK_SIZE = 256;
public static void Fill<T>(this T[] array, T value)
{
    if (array.Length < 2 * BLOCK_SIZE)
    {
        for (int i = 0; i < array.Length; i++) array[i] = value;
    }
    else
    {
        int fullBlocks = array.Length / BLOCK_SIZE;
        // Initialize first block
        for (int j = 0; j < BLOCK_SIZE; j++) array[j] = value;
        // Copy successive full blocks
        for (int blk = 1; blk < fullBlocks; blk++)
        {
            Array.Copy(array, 0, array, blk * BLOCK_SIZE, BLOCK_SIZE);
        }

        for (int rem = fullBlocks * BLOCK_SIZE; rem < array.Length; rem++)
        {
            array[rem] = value;
        }
    }
}
Eric J.
la source
3

Si vous prévoyez de ne définir que quelques-unes des valeurs dans le tableau, mais que vous souhaitez obtenir la valeur par défaut (personnalisée) la plupart du temps, vous pouvez essayer quelque chose comme ceci:

public class SparseArray<T>
{
    private Dictionary<int, T> values = new Dictionary<int, T>();

    private T defaultValue;

    public SparseArray(T defaultValue)
    {
        this.defaultValue = defaultValue;
    }

    public T this [int index]
    {
      set { values[index] = value; }
      get { return values.ContainsKey(index) ? values[index] ? defaultValue; }
    }
}

Vous devrez probablement implémenter d'autres interfaces pour le rendre utile, telles que celles du tableau lui-même.

Douglas
la source
3

Il n'y a aucun moyen de définir tous les éléments d'un tableau comme une seule opération, À MOINS QUE cette valeur soit la valeur par défaut des types d'élément.

Par exemple, s'il s'agit d'un tableau d'entiers, vous pouvez tous les mettre à zéro avec une seule opération, comme ceci: Array.Clear(...)

James
la source
2

Je me rends compte que je suis en retard à la fête mais voici une idée. Écrivez un encapsuleur qui a des opérateurs de conversion vers et depuis la valeur encapsulée afin qu'il puisse être utilisé comme remplaçant pour le type encapsulé. Cela a en fait été inspiré par la réponse stupide de @ l33t.

Tout d'abord (venant de C ++), j'ai réalisé qu'en C #, un ctor par défaut n'est pas appelé lorsque les éléments d'un tableau sont construits. Au lieu de cela - même en présence d'un constructeur par défaut défini par l'utilisateur! - tous les éléments du tableau sont initialisés à zéro. Cela m'a surpris.

Ainsi, une classe wrapper qui fournit simplement un ctor par défaut avec la valeur souhaitée fonctionnerait pour les tableaux en C ++ mais pas en C #. Une solution de contournement consiste à laisser le type de wrapper mapper 0 à la valeur de départ souhaitée lors de la conversion. De cette façon, les valeurs initialisées nulles semblent être initialisées avec la graine à toutes fins pratiques:

public struct MyBool
{
    private bool _invertedValue;

    public MyBool(bool b) 
    {   
        _invertedValue = !b;
    }

    public static implicit operator MyBool(bool b)
    {
        return new MyBool(b);
    }

    public static implicit operator bool(MyBool mb)
    {
        return !mb._invertedValue;
    }

}

static void Main(string[] args)
{
        MyBool mb = false; // should expose false.
        Console.Out.WriteLine("false init gives false: " 
                              + !mb);

        MyBool[] fakeBoolArray = new MyBool[100];

        Console.Out.WriteLine("Default array elems are true: " 
                              + fakeBoolArray.All(b => b) );

        fakeBoolArray[21] = false;
        Console.Out.WriteLine("Assigning false worked: " 
                              + !fakeBoolArray[21]);

        fakeBoolArray[21] = true;
        // Should define ToString() on a MyBool,
        // hence the !! to force bool
        Console.Out.WriteLine("Assigning true again worked: " 
                              + !!fakeBoolArray[21]);
}

Ce modèle est applicable à tous les types de valeurs. On pourrait par exemple mapper 0 à 4 pour les entiers si l'initialisation avec 4 était souhaitée, etc.

J'adorerais en faire un modèle comme cela serait possible en C ++, en fournissant la valeur de départ comme paramètre de modèle, mais je comprends que ce n'est pas possible en C #. Ou est-ce que je manque quelque chose? (Bien sûr, en mappage C ++ n'est pas du tout nécessaire car on peut fournir un ctor par défaut qui sera appelé pour les éléments du tableau.)

FWIW, voici un équivalent C ++: https://ideone.com/wG8yEh .

Peter - Rétablir Monica
la source
2

Si vous pouvez inverser votre logique, vous pouvez utiliser la Array.Clear()méthode pour définir le tableau booléen sur false.

        int upperLimit = 21;
        double optimizeMe = Math.Sqrt(upperLimit);

        bool[] seiveContainer = new bool[upperLimit];
        Array.Clear(seiveContainer, 0, upperLimit);
superstewie
la source
2

Si vous êtes sur .NET Core, .NET Standard> = 2.1, ou dépendez du package System.Memory, vous pouvez également utiliser la Span<T>.Fill()méthode:

var valueToFill = 165;
var data = new int[100];

data.AsSpan().Fill(valueToFill);

// print array content
for (int i = 0; i < data.Length; i++)
{
    Console.WriteLine(data[i]);
}

https://dotnetfiddle.net/UsJ9bu

Apollo3zehn
la source
2

Voici une autre version pour nous, les utilisateurs du Framework abandonnés par Microsoft. Il est 4 fois plus vite que Array.Clearet plus rapide que la solution de Panos Theof et Eric J de et un parallèle Petar Petrov - jusqu'à deux fois plus rapide pour les grands tableaux.

Je veux d'abord vous présenter l'ancêtre de la fonction, car cela facilite la compréhension du code. En termes de performances, cela correspond à peu près au code de Panos Theof, et pour certaines choses qui peuvent déjà être suffisantes:

public static void Fill<T> (T[] array, int count, T value, int threshold = 32)
{
    if (threshold <= 0)
        throw new ArgumentException("threshold");

    int current_size = 0, keep_looping_up_to = Math.Min(count, threshold);

    while (current_size < keep_looping_up_to)
        array[current_size++] = value;

    for (int at_least_half = (count + 1) >> 1; current_size < at_least_half; current_size <<= 1)
        Array.Copy(array, 0, array, current_size, current_size);

    Array.Copy(array, 0, array, current_size, count - current_size);
}

Comme vous pouvez le voir, cela est basé sur le doublement répété de la partie déjà initialisée. C'est simple et efficace, mais cela va à l'encontre des architectures de mémoire modernes. D'où est née une version qui n'utilise le doublage que pour créer un bloc de départ compatible avec le cache, qui est ensuite dynamité de manière itérative sur la zone cible:

const int ARRAY_COPY_THRESHOLD = 32;  // 16 ... 64 work equally well for all tested constellations
const int L1_CACHE_SIZE = 1 << 15;

public static void Fill<T> (T[] array, int count, T value, int element_size)
{
    int current_size = 0, keep_looping_up_to = Math.Min(count, ARRAY_COPY_THRESHOLD);

    while (current_size < keep_looping_up_to)
        array[current_size++] = value;

    int block_size = L1_CACHE_SIZE / element_size / 2;
    int keep_doubling_up_to = Math.Min(block_size, count >> 1);

    for ( ; current_size < keep_doubling_up_to; current_size <<= 1)
        Array.Copy(array, 0, array, current_size, current_size);

    for (int enough = count - block_size; current_size < enough; current_size += block_size)
        Array.Copy(array, 0, array, current_size, block_size);

    Array.Copy(array, 0, array, current_size, count - current_size);
}

Remarque: le code précédent devait (count + 1) >> 1servir de limite à la boucle de doublage pour garantir que l'opération de copie finale contienne suffisamment de fourrage pour couvrir tout ce qui reste. Ce ne serait pas le cas pour les dénombrements impairs sicount >> 1 devaient être utilisés à la place. Pour la version actuelle, cela n'a pas d'importance car la boucle de copie linéaire va prendre le moindre mou.

La taille d'une cellule de tableau doit être transmise en tant que paramètre car - l'esprit boggle - les génériques ne sont pas autorisés à utiliser sizeofsauf s'ils utilisent une contrainte ( unmanaged) qui peut ou non devenir disponible à l'avenir. Des estimations erronées ne sont pas un gros problème, mais les performances sont meilleures si la valeur est exacte, pour les raisons suivantes:

  • Sous-estimer la taille de l'élément peut entraîner des tailles de bloc supérieures à la moitié du cache L1, augmentant ainsi la probabilité que les données de la source de copie soient expulsées de L1 et doivent être récupérées à partir de niveaux de cache plus lents.

  • La surestimation de la taille de l'élément entraîne une sous-utilisation du cache L1 du processeur, ce qui signifie que la boucle de copie de bloc linéaire est exécutée plus souvent qu'elle ne le serait avec une utilisation optimale. Ainsi, la surcharge de boucle / appel fixe est engagée plus que strictement nécessaire.

Voici une référence comparant mon code Array.Clearet les trois autres solutions mentionnées précédemment. Les timings sont pour remplir des tableaux entiers ( Int32[]) des tailles données. Afin de réduire la variation causée par les caprices du cache, etc., chaque test a été exécuté deux fois de suite et les temporisations ont été prises pour la deuxième exécution.

array size   Array.Clear      Eric J.   Panos Theof  Petar Petrov   Darth Gizka
-------------------------------------------------------------------------------
     1000:       0,7 µs        0,2 µs        0,2 µs        6,8 µs       0,2 µs 
    10000:       8,0 µs        1,4 µs        1,2 µs        7,8 µs       0,9 µs 
   100000:      72,4 µs       12,4 µs        8,2 µs       33,6 µs       7,5 µs 
  1000000:     652,9 µs      135,8 µs      101,6 µs      197,7 µs      71,6 µs 
 10000000:    7182,6 µs     4174,9 µs     5193,3 µs     3691,5 µs    1658,1 µs 
100000000:   67142,3 µs    44853,3 µs    51372,5 µs    35195,5 µs   16585,1 µs 

Si les performances de ce code ne sont pas suffisantes, une avenue prometteuse serait de paralléliser la boucle de copie linéaire (avec tous les threads utilisant le même bloc source), ou notre bon vieil ami P / Invoke.

Remarque: l'effacement et le remplissage des blocs sont normalement effectués par des routines d'exécution qui se ramifient en code hautement spécialisé à l'aide d'instructions MMX / SSE et ainsi de suite, donc dans tout environnement décent, on appellerait simplement l'équivalent moral respectif std::memsetet serait assuré de niveaux de performance professionnels. IOW, de plein droit, la fonction de bibliothèque Array.Cleardevrait laisser toutes nos versions roulées à la main dans la poussière. Le fait que ce soit l'inverse montre à quel point les choses sont vraiment éloignées. Il en va de même pour avoir à rouler le sien Fill<>en premier lieu, car il n'est toujours que dans Core et Standard mais pas dans le Framework. .NET existe depuis près de vingt ans maintenant et nous devons encore P / Invoke gauche et droite pour les choses les plus élémentaires ou rouler les nôtres ...

DarthGizka
la source
0

Voici une autre approche avec System.Collections.BitArraylaquelle a un tel constructeur.

bool[] result = new BitArray(1000000, true).Cast<bool>().ToArray();

ou

bool[] result = new bool[1000000];
new BitArray(1000000, true).CopyTo(result, 0);
fubo
la source
0

Créez une classe privée à l'intérieur de laquelle vous créez le tableau et disposez d'un getter et d'un setter pour celui-ci. Sauf si vous avez besoin que chaque position du tableau soit unique, comme aléatoire, utilisez int? en tant que tableau, puis obtenez si la position est égale à null, remplissez cette position et retournez la nouvelle valeur aléatoire.

IsVisibleHandler
{

  private bool[] b = new bool[10000];

  public bool GetIsVisible(int x)
  {
  return !b[x]
  }

  public void SetIsVisibleTrueAt(int x)
  {
  b[x] = false //!true
  }
}

Ou utiliser

public void SetIsVisibleAt(int x, bool isTrue)
{
b[x] = !isTrue;
}

Comme setter.

Peter J
la source
-2
Boolean[] data = new Boolean[25];

new Action<Boolean[]>((p) => { BitArray seed = new BitArray(p.Length, true); seed.CopyTo(p, 0); }).Invoke(data);
ldsmithperrin
la source
Veuillez utiliser un meilleur formatage et peut-être quelques mots explicatifs pour que les autres comprennent mieux votre solution.
Gorgsenegger
1
Vous pouvez l'utiliser pour augmenter les performances de l'initialisation en partitionnant la baie cible et en copiant la graine dans les différentes partitions. C'était seulement pour donner une idée - C'est mon premier et mon dernier article.
ldsmithperrin