Wie fülle / instanziiere ich ein C # -Array mit einem einzelnen Wert?

205

Ich weiß, dass instanziierte Arrays von Werttypen in C # automatisch mit dem Standardwert des Typs gefüllt werden (z. B. false für bool, 0 für int usw.).

Gibt es eine Möglichkeit, ein Array automatisch mit einem Startwert zu füllen, der nicht der Standardwert ist? Entweder bei der Erstellung oder eine integrierte Methode danach (wie Java Arrays.fill () )? Angenommen, ich wollte ein boolesches Array, das standardmäßig true anstelle von false ist. Gibt es eine integrierte Möglichkeit, dies zu tun, oder müssen Sie das Array nur mit einer for-Schleife durchlaufen?

 // 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;
 }

Es scheint ineffizient zu sein, das Array zu durchlaufen und jeden Wert auf true zurückzusetzen. Gibt es das überhaupt? Vielleicht durch Umdrehen aller Werte?

Nachdem ich diese Frage abgetippt und darüber nachgedacht habe, schätze ich, dass die Standardwerte einfach darauf zurückzuführen sind, wie C # die Speicherzuweisung dieser Objekte hinter den Kulissen handhabt. Ich kann mir also vorstellen, dass dies wahrscheinlich nicht möglich ist. Aber ich würde es trotzdem gerne sicher wissen!

patjbs
quelle
Normalerweise ändere ich den Namen von is_found in is_still_hiding. Ich liebe die Antworten, aber ich musste in einem Testfall ähnliche Schritte für ein Array von int ausführen. (gute Frage)
Strg-Alt-Delor

Antworten:

146

Sie kennen keine Framework-Methode, aber Sie könnten einen schnellen Helfer schreiben, um dies für Sie zu tun.

public static void Populate<T>(this T[] arr, T value ) {
  for ( int i = 0; i < arr.Length;i++ ) {
    arr[i] = value;
  }
}
JaredPar
quelle
3
Bevorzugen Sie ++ i anstelle von i ++, wenn Sie die Kopie nicht benötigen.
void.pointer
24
i ++ kopiert i, erhöht i und gibt den ursprünglichen Wert zurück. ++ Ich gebe nur den inkrementierten Wert zurück. Daher ist ++ i schneller, was in großen Schleifen, wie wir hier sprechen, von Bedeutung sein kann.
Tenpn
57
@ RobertDailey: Das ist eine Compiler-Optimierung und nicht mehr wahr. Ich habe gerade getestet, um meine Überzeugung zu bestätigen: Wenn der Rückgabewert von i ++ für nichts verwendet wird, kompiliert der Compiler ihn automatisch als ++ i für Sie. Selbst wenn ich den Rückgabewert verwende, ist der Leistungsunterschied so gering, dass ich einen Extremfall erstellen musste, um ihn zu messen. Selbst dann führte dies zu nur wenigen Prozent unterschiedlicher Laufzeit.
Edward Ned Harvey
8
Ich habe eine Erweiterungsmethode wie diese geschrieben, aber ich habe sie das ursprüngliche Array zurückgeben lassen, um Methodenverkettungen zu ermöglichen, wie zum Beispiel:int[] arr = new int[16].Populate(-1);
Gutblender
2
Wechseln Sie voidzu T[]und dann können Sie tunvar a = new int[100].Polupate(1)
orad
198
Enumerable.Repeat(true, 1000000).ToArray();
Rony
quelle
70
Während dies funktioniert, ist es keine wirklich gute Lösung, weil es sehr langsam ist; Es ist ungefähr viermal langsamer als das Iterieren mit einer for-Schleife.
Patjbs
4
Ja, das stimmt, wenn wir die Leistung betrachten, ist die for-Schleife schneller
Rony
6
Um einen echten Benchmark zu sehen, schauen Sie sich C # Initialize Array an .
theknut
4
Enumerable.ToArraykennt die Größe der aufzählbaren Sequenz nicht, muss also die Arraygröße erraten. Das bedeutet, dass Sie jedes Mal Array-Zuweisungen erhalten, wenn ToArrayder Puffer überschritten wird, plus eine weitere Zuweisung am Ende für das Trimmen. Mit dem aufzählbaren Objekt ist auch ein Overhead verbunden.
Edward Brey
5
Nur eine Anmerkung, dass bei Referenztypen das gesamte Array mit allen Referenzen auf dasselbe einzelne Objekt gefüllt wird. Wenn dies nicht das ist, was Sie möchten, und Sie tatsächlich unterschiedliche Objekte für jedes Array-Element generieren möchten, lesen Sie stackoverflow.com/a/44937053/23715 .
Alex Che
74

Erstellen Sie ein neues Array mit tausend trueWerten:

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

Ebenso können Sie ganzzahlige Sequenzen generieren:

var items = Enumerable.Range(0, 1000).ToArray();  // 0..999
Bytebender
quelle
8
Nicht schlecht, aber es ist immer noch langsamer als eine for-Schleife um den Faktor 4x
patjbs
1
Patjbs in der Theorie in der Zukunft Enumerable.Repeat wird schneller durchgeführt, weil es eine parallele Implementierung verwenden wird.
Petar Petrov
1
@PetarPetrov Dies wird aufgrund von Cache-Thrashing niemals passieren. Ich bin mir ziemlich sicher, dass aufgrund der Beschaffenheit des CPU-Caches die parallele Arbeit an einem einzelnen Array immer langsamer ist, egal was passiert, da der Computer synchrone Arbeit erwartet und Daten entsprechend lädt.
TernaryTopiary
beabsichtigte Pessimisierung! = Mangel an vorzeitiger Optimierung.
Denis Gladkiy
24

Für große Arrays oder Arrays mit variabler Größe sollten Sie wahrscheinlich Folgendes verwenden:

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

Für kleine Arrays können Sie die Syntax für die Initialisierung der Sammlung in C # 3 verwenden:

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

Der Vorteil der Syntax für die Sammlungsinitialisierung besteht darin, dass Sie nicht in jedem Slot denselben Wert verwenden müssen und Ausdrücke oder Funktionen zum Initialisieren eines Slots verwenden können. Ich denke auch, dass Sie die Kosten für die Initialisierung des Array-Steckplatzes auf den Standardwert vermeiden. Also zum Beispiel:

bool[] vals = new bool[]{ false, true, false, !(a ||b) && c, SomeBoolMethod() };
LBushkin
quelle
Und um ein float [] Array zu initialisieren:float[] AlzCalDefault = new float[] {(float) 0.5, 18, 500, 1, 0};
Jim Lahman
Die FWIW-Initialisierung eines Arrays kann in jeder Version von C # erfolgen, wie:bool[] vals = { false, true, false, !(a || b) && c, SomeBoolMethod() };
Peter van der Heijden
24

Wenn Ihr Array so groß ist, sollten Sie BitArray verwenden. Es wird 1 Bit für jeden Bool anstelle eines Bytes verwendet (wie in einem Array von Bools). Außerdem können Sie mit Bitoperatoren alle Bits auf true setzen. Oder initialisieren Sie einfach auf true. Wenn Sie es nur einmal tun müssen, kostet es jedoch nur mehr.

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
quelle
17

Sie können Array.Fillin .NET Core 2.0+ und .NET Standard 2.1+ verwenden.

juFo
quelle
Ausgezeichnet! Beachten Sie jedoch, dass es sich um eine relativ neue Methode handelt. Es ist in .NET Core 2.0+ und .NET Standard 2.1 verfügbar, jedoch nicht in einer der .NET Framework-Versionen. (Es wird in .NET 5.0 sein, das .NET Framework und .NET Core miteinander verbindet.)
Abel
9

Leider glaube ich nicht, dass es einen direkten Weg gibt, aber ich denke, Sie können eine Erweiterungsmethode für die Array-Klasse schreiben, um dies zu tun

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
quelle
Ich mag die Erweiterungsidee, je mehr ich mich damit beschäftige. Manchmal ist die einfache Lösung wirklich die beste!
Patjbs
8

Nun, nachdem ich ein bisschen mehr gegoogelt und gelesen hatte, fand ich Folgendes:

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

Welches ist sicherlich näher an dem, was ich suche. Ich bin mir jedoch nicht sicher, ob dies besser ist, als das ursprüngliche Array in einer for-Schleife zu durchlaufen und nur die Werte zu ändern. Nach einem kurzen Test erscheint es tatsächlich um den Faktor 5 langsamer. Dann also keine wirklich gute Lösung!

patjbs
quelle
4
Dies ähnelt dem, was Sie versuchen, außer dass für jedes Element in Ihrem Array ein Funktionsaufruf ausgeführt wird. Es mag syntaktisch viel schöner aussehen, aber es macht viel mehr Arbeit ...
Nader Shirazie
Ja, es sieht so aus, als würde eine einfache for-Schleife den Job genauso gut
erledigen
Es wird ein neues Array erstellt (die ursprüngliche Instanz wird nicht geändert).
Jeppe Stig Nielsen
7

Was ist mit einer parallelen Implementierung?

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);
}

Wenn Sie nur ein Array initialisieren, kann die Leistung dieses Codes nicht gesehen werden, aber ich denke, Sie sollten auf jeden Fall das "reine" für vergessen.

Petar Petrov
quelle
Dies birgt das Risiko einer falschen Freigabe, bei der verschiedene Threads um CPU-Cache-Zeilen konkurrieren und daher die Leistung im Vergleich zu einer Single-Thread-Implementierung verringern. Ob dies geschieht, hängt von der Größe der Speicherblöcke pro Thread und der CPU-Architektur ab.
Eric J.
7

Der folgende Code kombiniert einfache Iteration für kleine Kopien und Array.Copy für große Kopien

    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;
            }
        }
    }

Die Benchmarks für unterschiedliche Array-Längen mit einem int [] -Array sind:

         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

Die erste Spalte gibt die Arraygröße an, gefolgt vom Zeitpunkt des Kopierens mithilfe einer einfachen Iteration (@ JaredPared-Implementierung). Die Zeit dieser Methode ist danach. Dies sind die Benchmarks, die ein Array aus einer Struktur von vier Ganzzahlen verwenden

         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
quelle
7

Oder ... Sie könnten einfach invertierte Logik verwenden. Lassen Sie falsemeinen trueund umgekehrt.

Codebeispiel

// 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
quelle
lustige Lösung, obwohl dies viel schwieriger wäre, zum Beispiel mit Ints, weil Sie die 0 verlieren.
MrFox
1
Dies ist tatsächlich eine praktikable Option, wenn Sie die Logik für den Variablennamen "invertieren": anstatt es zu bool[] isVisiblemachenbool[] isHidden
Markus Hütter
1
Die Leute scheinen so zu reagieren, dass es eine Art lustiger Hack ist. Es ist eine übliche Optimierungstechnik. Wenn Sie Glück haben, erledigt der Compiler dies für Sie.
l33t
4

das funktioniert auch ... könnte aber unnötig sein

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

Viele der hier vorgestellten Antworten beschränken sich auf eine Schleife, die das Array elementweise initialisiert und dabei nicht die CPU-Anweisungen nutzt, die für den gleichzeitigen Betrieb eines Speicherblocks ausgelegt sind.

.Net Standard 2.1 (in der Vorschau zum jetzigen Zeitpunkt ) bietet Array.Fill () , das sich für eine Hochleistungsimplementierung in der Laufzeitbibliothek eignet (obwohl .NET Core dies derzeit nicht zu tun scheint Möglichkeit nutzen ). .

Für diejenigen auf früheren Plattformen übertrifft die folgende Erweiterungsmethode eine triviale Schleife um einen erheblichen Vorsprung, wenn die Arraygröße signifikant ist. Ich habe es erstellt, als meine Lösung für eine Online-Code-Herausforderung etwa 20% über dem zugewiesenen Zeitbudget lag. Es reduzierte die Laufzeit um rund 70%. In diesem Fall wurde die Array-Füllung in einer anderen Schleife durchgeführt. BLOCK_SIZE wurde eher durch Bauchgefühl als durch Experiment eingestellt. Einige Optimierungen sind möglich (z. B. Kopieren aller bereits auf den gewünschten Wert eingestellten Bytes anstelle eines Blocks mit fester Größe).

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.
quelle
3

Wenn Sie nur einige der Werte im Array festlegen möchten, aber die meiste Zeit den (benutzerdefinierten) Standardwert erhalten möchten, können Sie Folgendes versuchen:

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; }
    }
}

Sie müssen wahrscheinlich andere Schnittstellen implementieren, um sie nützlich zu machen, z. B. die auf dem Array selbst.

Douglas
quelle
3

Es gibt keine Möglichkeit, alle Elemente in einem Array als einzelne Operation festzulegen, AUSSER dieser Wert ist der Standardwert des Elementtyps.

Wenn es sich beispielsweise um ein Array von Ganzzahlen handelt, können Sie sie alle mit einer einzigen Operation auf Null setzen: Array.Clear(...)

James
quelle
2

Mir ist klar, dass ich zu spät zur Party komme, aber hier ist eine Idee. Schreiben Sie einen Wrapper mit Konvertierungsoperatoren zum und vom umschlossenen Wert, damit er als Ersatz für den umschlossenen Typ verwendet werden kann. Dies wurde tatsächlich von der albern klingenden Antwort von @ l33t inspiriert.

Zuerst (aus C ++) wurde mir klar, dass in C # ein Standard-Ctor nicht aufgerufen wird, wenn die Elemente eines Arrays erstellt werden. Stattdessen - auch bei Vorhandensein eines benutzerdefinierten Standardkonstruktors! - Alle Array-Elemente sind nullinitialisiert. Das hat mich überrascht.

Eine Wrapper-Klasse, die einfach einen Standard-Ctor mit dem gewünschten Wert bereitstellt, funktioniert also für Arrays in C ++, jedoch nicht in C #. Eine Problemumgehung besteht darin, den Wrapper-Typ bei der Konvertierung 0 dem gewünschten Startwert zuordnen zu lassen. Auf diese Weise scheinen null initialisierte Werte für alle praktischen Zwecke mit dem Startwert initialisiert zu werden:

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]);
}

Dieses Muster gilt für alle Werttypen. Man könnte zum Beispiel 0 bis 4 für Ints abbilden, wenn eine Initialisierung mit 4 gewünscht wäre usw.

Ich würde gerne eine Vorlage daraus erstellen, wie dies in C ++ möglich wäre, wobei der Startwert als Vorlagenparameter angegeben wird, aber ich verstehe, dass dies in C # nicht möglich ist. Oder fehlt mir etwas? (Natürlich ist in C ++ die Zuordnung überhaupt nicht erforderlich, da man einen Standard-Ctor bereitstellen kann, der für Array-Elemente aufgerufen wird.)

FWIW, hier ist ein C ++ - Äquivalent: https://ideone.com/wG8yEh .

Peter - Monica wieder einsetzen
quelle
2

Wenn Sie Ihre Logik invertieren können, können Sie Array.Clear()das boolesche Array mit der Methode auf false setzen.

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

        bool[] seiveContainer = new bool[upperLimit];
        Array.Clear(seiveContainer, 0, upperLimit);
Superstewie
quelle
2

Wenn Sie .NET Core, .NET Standard> = 2.1 verwenden oder vom System.Memory-Paket abhängig sind, können Sie auch die folgende Span<T>.Fill()Methode verwenden:

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
quelle
2

Hier ist eine weitere Version für uns Framework-Benutzer, die von Microsoft aufgegeben wurde. Es ist viermal so schnell Array.Clearund schneller als die Lösung von Panos Theof und die parallele Lösung von Eric J und Petar Petrov - bis zu zweimal so schnell für große Arrays.

Zuerst möchte ich Ihnen den Vorfahren der Funktion vorstellen, da dies das Verständnis des Codes erleichtert. In Bezug auf die Leistung entspricht dies ziemlich genau dem Code von Panos Theof, und für einige Dinge, die möglicherweise bereits ausreichen:

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);
}

Wie Sie sehen können, basiert dies auf der wiederholten Verdoppelung des bereits initialisierten Teils. Dies ist einfach und effizient, verstößt jedoch gegen moderne Speicherarchitekturen. Daher wurde eine Version geboren, die das Verdoppeln nur verwendet, um einen cachefreundlichen Startblock zu erstellen, der dann iterativ über den Zielbereich gestrahlt wird:

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);
}

Hinweis: Der frühere Code wird (count + 1) >> 1als Begrenzung für die Verdopplungsschleife benötigt, um sicherzustellen, dass der endgültige Kopiervorgang über genügend Futter verfügt, um alles verbleibende abzudecken. Dies wäre bei ungeraden Zählungen nicht der Fall, wenncount >> 1 stattdessen verwendet würde. Für die aktuelle Version ist dies nicht von Bedeutung, da die lineare Kopierschleife einen Durchhang aufnimmt.

Die Größe einer Array-Zelle muss als Parameter übergeben werden, da Generika - es ist ein Rätsel - nicht verwendet werden dürfen, es sizeofsei denn, sie verwenden eine Einschränkung ( unmanaged), die möglicherweise in Zukunft verfügbar wird oder nicht. Falsche Schätzungen sind keine große Sache, aber die Leistung ist aus folgenden Gründen am besten, wenn der Wert korrekt ist:

  • Eine Unterschätzung der Elementgröße kann zu Blockgrößen führen, die größer als die Hälfte des L1-Cache sind, wodurch die Wahrscheinlichkeit erhöht wird, dass Kopierquellendaten aus L1 entfernt werden und aus langsameren Cache-Ebenen erneut abgerufen werden müssen.

  • Das Überschätzen der Elementgröße führt zu einer Unterauslastung des L1-Cache der CPU, was bedeutet, dass die lineare Blockkopierschleife häufiger ausgeführt wird als bei optimaler Auslastung. Somit entsteht mehr Overhead für feste Schleifen / Anrufe als unbedingt erforderlich.

Hier ist ein Benchmark, an dem mein Code Array.Clearund die anderen drei zuvor genannten Lösungen verglichen werden. Die Timings dienen zum Füllen von Integer-Arrays ( Int32[]) der angegebenen Größen. Um die durch Cache-Abweichungen usw. verursachten Abweichungen zu verringern, wurde jeder Test zweimal hintereinander ausgeführt, und die Zeitpunkte wurden für die zweite Ausführung festgelegt.

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 

Sollte die Leistung dieses Codes nicht ausreichen, wäre ein vielversprechender Weg die Parallelisierung der linearen Kopierschleife (wobei alle Threads denselben Quellblock verwenden) oder unseres guten alten Freundes P / Invoke.

Hinweis: Das Löschen und Füllen von Blöcken erfolgt normalerweise über Laufzeitroutinen, die mithilfe von MMX / SSE-Anweisungen und so weiter zu hochspezialisiertem Code verzweigen. In jeder anständigen Umgebung würde man einfach das jeweilige moralische Äquivalent std::memseteines professionellen Leistungsniveaus nennen und sich dessen versichern. IOW, von Rechts wegen sollte die Bibliotheksfunktion Array.Clearalle unsere handgerollten Versionen im Staub liegen lassen. Die Tatsache, dass es umgekehrt ist, zeigt, wie weit die Dinge wirklich entfernt sind. Gleiches gilt für Fill<>das erstmalige Eigenrollen, da es sich immer noch nur um Core und Standard handelt, nicht aber um das Framework. .NET gibt es schon seit fast zwanzig Jahren und wir müssen immer noch links und rechts P / Invoke für die grundlegendsten Dinge oder unsere eigenen ...

DarthGizka
quelle
0

Hier ist ein weiterer Ansatz, mit System.Collections.BitArraydem ein solcher Konstruktor hat.

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

oder

bool[] result = new bool[1000000];
new BitArray(1000000, true).CopyTo(result, 0);
Fubo
quelle
0

Erstellen Sie eine private Klasse, in der Sie das Array erstellen und einen Getter und Setter dafür haben. Wenn Sie nicht möchten, dass jede Position im Array etwas Einzigartiges ist, wie zufällig, verwenden Sie int? als Array und dann auf get, wenn die Position gleich null ist, füllen Sie diese Position und geben Sie den neuen Zufallswert zurück.

IsVisibleHandler
{

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

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

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

Oder verwenden

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

Als Setter.

Peter J.
quelle
-2
Boolean[] data = new Boolean[25];

new Action<Boolean[]>((p) => { BitArray seed = new BitArray(p.Length, true); seed.CopyTo(p, 0); }).Invoke(data);
ldsmithperrin
quelle
Bitte verwenden Sie eine bessere Formatierung und möglicherweise einige erklärende Wörter, damit andere Ihre Lösung besser verstehen können.
Gorgsenegger
1
Sie können dies verwenden, um die Leistung der Initialisierung zu erhöhen, indem Sie das Zielarray partitionieren und den Startwert auf die verschiedenen Partitionen kopieren. Dies sollte nur eine Idee geben - Dies ist mein erster und mein letzter Beitrag überhaupt.
ldsmithperrin