Tek bir değeri olan bir C # dizisini nasıl doldurur / başlatırsınız?


205

C # değer türlerinin somutlaştırılmış dizilerinin türün varsayılan değeri ile otomatik olarak doldurulduğunu biliyorum (örneğin bool için false, int için 0, vb.).

Bir diziyi varsayılan olmayan bir tohum değeriyle otomatik olarak doldurmanın bir yolu var mı? Ya yaratılışta ya da daha sonra yerleşik bir yöntemde (Java Arrays.fill () gibi )? Diyelim ki yanlış yerine varsayılan olarak doğru olan bir boole dizisi istedim. Bunu yapmanın yerleşik bir yolu var mı, yoksa sadece for döngüsü ile dizi üzerinden yineleme yapmak zorunda mısınız?

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

Dizi üzerinden yineleme yapmak ve her değeri true değerine "sıfırlamak" verimsiz görünüyor. Bunun etrafında yine de var mı? Belki de tüm değerleri çevirerek?

Bu soruyu yazıp düşündükten sonra, varsayılan değerlerin C # 'ın bu nesnelerin sahne arkasındaki bellek tahsisini nasıl işlediğinin bir sonucu olduğunu tahmin ediyorum, bu yüzden muhtemelen bunun mümkün olmadığını hayal ediyorum. Ama yine de kesin olarak bilmek istiyorum!


Genellikle adı is_found yerine is_still_hiding olarak değiştiririm. Ama cevapları seviyorum, ben bir test durumda int dizi için benzer yapmak gerekiyordu. (iyi soru)
ctrl-alt-delor

Yanıtlar:


146

Bir çerçeve yöntemi bilmiyorum ama sizin için bunu yapmak için hızlı bir yardımcı yazabilirsiniz.

public static void Populate<T>(this T[] arr, T value ) {
  for ( int i = 0; i < arr.Length;i++ ) {
    arr[i] = value;
  }
}

3
Kopyaya ihtiyacınız yoksa i ++ yerine ++ i tercih edin.
void.pointer

24
i ++, i'yi kopyalar, i'yi artırır ve orijinal değerini döndürür. ++ i sadece artan değeri döndürür. Bu nedenle ++ i daha hızlıdır, burada konuştuğumuz gibi büyük döngülerde önemli olabilir.
tenpn

57
@RobertDailey: Bu bir derleyici optimizasyonu ve artık doğru değil. Sadece inancımı doğrulamak için test ettim: i ++ dönüş değeri hiçbir şey için kullanılmazsa, derleyici sizin için otomatik olarak ++ i olarak derleyecektir. Ayrıca, dönüş değerini kullandığımda bile, performans farkı o kadar küçük ki ölçmek için aşırı bir durum yapmam gerekiyordu. O zaman bile, sadece yüzde birkaç farklı çalışma süresine neden oldu.
Edward Ned Harvey

8
Böyle bir uzatma yöntemi yazdım ama böyle yöntem zincirleme izin vermek için orijinal dizi dönmek vardı:int[] arr = new int[16].Populate(-1);
Gutblender

2
Değişim voidiçin T[]ve sonra yapabileceğinizvar a = new int[100].Polupate(1)
orad

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

70
Bu işe yarıyor olsa da, gerçekten iyi bir çözüm değil, çünkü çok yavaş; aslında bir for döngüsü ile yinelemekten yaklaşık 4 kat daha yavaştır.
patjbs

4
evet bu doğrudur, performansı düşündüğümüzde for döngüsü daha hızlıdır
Rony

6
Bazı gerçek ölçütleri görmek için C # Initialize Array'a bakın .
theknut

4
Enumerable.ToArraynumaralandırılabilir dizinin boyutunu bilmiyor, bu yüzden dizi boyutu hakkında tahmin etmek zorunda. Bu, ToArrayarabellek her aşıldığında dizi ayırmaları ve ayrıca kırpma için sonunda bir ayırma daha alacağınız anlamına gelir . Sayılabilir nesne ile ilgili ek yük de var.
Edward Brey

5
Sadece bir not, referans türleriyle bunun tüm diziyi aynı tek nesneye yapılan tüm referanslarla dolduracağını unutmayın. İstediğiniz bu değilse ve aslında her dizi öğesi için farklı nesneler oluşturmak istiyorsanız, bkz. Stackoverflow.com/a/44937053/23715 .
Alex Che

74

Bin truedeğerli yeni bir dizi oluşturun :

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

Benzer şekilde, tamsayı dizileri de oluşturabilirsiniz:

var items = Enumerable.Range(0, 1000).ToArray();  // 0..999

8
Fena değil, ama yine de bir döngü için yaklaşık 4 kat daha yavaş
patjbs

1
gelecekte teorik patjbs Enumerable.Repeat paralel bir uygulama kullanacağından daha hızlı performans gösterecektir.
Petar Petrov

1
@PetarPetrov Bu hiçbir zaman önbellek çökmesinden dolayı olmayacak. CPU önbelleğinin doğası gereği, bilgisayar eşzamanlı çalışma beklediğinden ve verileri uygun şekilde yüklediğinden, tek bir dizi üzerinde paralel olarak çalışmanın her zaman daha yavaş olacağından oldukça eminim.
üçlü üçlü

amaçlanan kötümserlik! = erken optimizasyon eksikliği.
Denis Gladkiy

24

Büyük diziler veya değişken boyutlu diziler için şunları kullanmalısınız:

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

Küçük dizi için koleksiyon başlatma sözdizimini C # 3'te kullanabilirsiniz:

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

Koleksiyon başlatma sözdiziminin avantajı, her bir yuvada aynı değeri kullanmanız gerekmemesi ve bir alanı başlatmak için ifadeler veya işlevler kullanabilmenizdir. Ayrıca, dizi yuvasını varsayılan değere başlatma maliyetinden kaçındığını düşünüyorum. Yani mesela:

bool[] vals = new bool[]{ false, true, false, !(a ||b) && c, SomeBoolMethod() };

Ve bir kayan nokta [] dizisini başlatmak için:float[] AlzCalDefault = new float[] {(float) 0.5, 18, 500, 1, 0};
Jim Lahman

Bir dizinin FWIW başlatma C # herhangi bir sürümünde yapılabilir:bool[] vals = { false, true, false, !(a || b) && c, SomeBoolMethod() };
Peter van der Heijden

24

Diziniz çok büyükse BitArray kullanmalısınız. Bir bayt yerine (her bir bools dizisinde olduğu gibi) her bool için 1 bit kullanır, ayrıca bit işleçleriyle tüm bitleri true değerine ayarlayabilirsiniz. Ya da sadece doğruya sıfırlayın. Sadece bir kez yapmanız gerekiyorsa, sadece daha pahalıya mal olacaktır.

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

17

Sen kullanabilirsiniz Array.FillNET Çekirdek 2.0+ ve .NET Standard 2.1+ içinde.


Mükemmel! Yine de bunun nispeten yeni bir yöntem olduğunu unutmayın. Bu ancak spesifik olarak, .NET Çekirdek 2.0+ ve .NET Standard 2.1 mevcuttur değil .NET Framework sürümleri herhangi birinde. (.NET Framework ve .NET Core'u bir araya getiren .NET 5.0'da olacaktır).
Abel

9

ne yazık ki doğrudan bir yol olduğunu düşünmüyorum, ancak bunu yapmak için dizi sınıfı için bir uzantı yöntemi yazabilirsiniz düşünüyorum

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

Uzatma fikrini beğeniyorum, daha çok kazarım. Bazen açık ve basit bir çözüm gerçekten en iyisi!
patjbs

8

Biraz daha googling ve okumadan sonra bunu buldum:

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

Bu kesinlikle aradığım şeylere daha yakın. Ancak bunun bir for-loop'taki orijinal diziyi yinelemekten ve sadece değerleri değiştirmekten daha iyi olup olmadığından emin değilim. Aslında hızlı bir testten sonra, yaklaşık 5 kat daha yavaş görünüyor. O zaman gerçekten iyi bir çözüm değil!


4
Bu, dizinizdeki her öğe için bir işlev çağrısı yapmak dışında yapmaya çalıştığınız şeye benzer. Sözdizimsel olarak çok daha hoş görünebilir, ama çok daha fazla iş yapıyor ...
Nader Shirazie

evet, sadece döngü için iş gibi başka bir şey yapıyor gibi görünüyor
patjbs

Yeni bir dizi oluşturur (orijinal örneği değiştirmez).
Jeppe Stig Nielsen

7

Paralel bir uygulamaya ne dersiniz?

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

Sadece bir dizi başlatırken bu kodun gücü görülemez ama kesinlikle "saf" için unutmak gerektiğini düşünüyorum.


Bu, farklı iş parçacıklarının CPU önbellek satırları için rekabet ettiği ve dolayısıyla tek iş parçacıklı bir uygulamaya kıyasla performansı düşürdüğü yanlış paylaşım sorununu riske atar. Bunun olup olmadığı, iş parçacığı başına bellek bloklarının boyutuna ve CPU mimarisine bağlıdır.
Eric J.

7

Aşağıdaki kod, küçük kopyalar için basit yinelemeyi ve büyük kopyalar için Array'ı kopyalar.

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

Bir int [] dizisi kullanan farklı dizi uzunlukları için karşılaştırmalar:

         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

İlk sütunlar dizi boyutudur, ardından basit bir yineleme (@JaredPared uygulaması) kullanılarak kopyalama zamanı gelir. Bu yöntemin zamanı bundan sonradır. Bunlar, dört tamsayıdan oluşan bir yapı dizisi kullanan karşılaştırmalardır

         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

7

Veya ... tersine çevrilmiş mantığı kullanabilirsiniz. Let falseortalama trueve tersi.

Kod örneği

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

komik bir çözüm, tüm bunlar örneğin ints ile çok daha zor olurdu, çünkü
0.'yı

1
değişken adında "mantığı tersine çevirirseniz" bu geçerli bir seçenektir: bool[] isVisibleyapmak yerinebool[] isHidden
Markus Hütter

1
İnsanlar bunun bir tür komik hack olduğu gibi tepki veriyor gibi görünüyor. Bu yaygın bir optimizasyon tekniğidir. Eğer şanslıysanız, derleyici bunu sizin için yapar.
l33t

4

bu da işe yarıyor ... ama gereksiz olabilir

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

4

Burada sunulan cevapların çoğu, bir kerede bir diziyi başlatan bir döngüye kadar kaynar, bu da bir kerede bir bellek bloğunda çalışmak için tasarlanmış CPU talimatlarından faydalanmaz.

.Net Standard 2.1 (bu yazının önizlemesinde ) , çalışma zamanı kitaplığında yüksek performanslı bir uygulamaya katkıda bulunan Array.Fill () işlevini sağlar ( şu andan itibaren .NET Core bu olasılıktan yararlanmıyor gibi görünmektedir ). .

Önceki platformlardakiler için, aşağıdaki genişletme yöntemi, dizi boyutu önemli olduğunda önemsiz bir döngüden önemli bir farkla daha iyi performans gösterir. Bir çevrimiçi kod zorluğuna yönelik çözümüm, ayrılan zaman bütçesinden% 20 civarında olduğunda yarattım. Çalışma süresini yaklaşık% 70 oranında azalttı. Bu durumda, dizi dolgusu başka bir döngü içinde gerçekleştirildi. BLOCK_SIZE, deneyden ziyade bağırsak hissi ile ayarlandı. Bazı optimizasyonlar mümkündür (örneğin, önceden ayarlanmış tüm baytların sabit boyutlu bir blok yerine istenen değere kopyalanması).

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

3

Dizideki değerlerden yalnızca birkaçını ayarlamayı planlıyorsanız, ancak (özel) varsayılan değeri çoğu zaman elde etmek istiyorsanız, şöyle bir şey deneyebilirsiniz:

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

Büyük olasılıkla dizinin kendisinde olduğu gibi, kullanışlı olması için diğer arabirimleri uygulamanız gerekir .


3

Bir dizideki tüm öğeleri tek bir işlem (UNLESS) olarak ayarlamanın bir yolu yoktur, bu değer öğe türleri varsayılan değeridir.

Örneğin, bir tamsayı dizisi ise, tek bir işlemle hepsini sıfıra ayarlayabilirsiniz, örneğin: Array.Clear(...)


2

Partiye geç kaldığımı fark ettim ama işte bir fikir. Sarılmış tip için bir stand-in olarak kullanılabilmesi için, sarılmış değere ve oradan dönüşüm işleçleri olan bir sarıcı yazın. Bu aslında @ l33t'un aptalca yanıtından ilham aldı.

İlk (C ++ geliyor) C # bir dizi öğeleri inşa edildiğinde varsayılan bir ctor çağrıldığını fark ettim. Bunun yerine - kullanıcı tanımlı bir varsayılan kurucu varlığında bile! - tüm dizi öğeleri sıfırdan başlatılır. Bu beni şaşırttı.

Bu nedenle, istenen değere sahip varsayılan bir ctor sağlayan bir sarmalayıcı sınıfı, C ++ 'da diziler için işe yarayacaktır. Geçici çözüm, sargı tipinin 0 dönüşümden sonra istenen tohum değerine eşlenmesine izin vermektir. Bu şekilde sıfır başlangıç ​​değerlerinin tüm pratik amaçlar için tohumla ilklendirildiği görülür:

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

Bu kalıp tüm değer türleri için geçerlidir. Örneğin, 4 ile başlatma isteniyorsa, ints için 0 ila 4 eşlemesi yapılabilir.

Ben şablon parametresi olarak tohum değeri sağlayan C ++ mümkün olduğu gibi bir şablon yapmak isterdim, ama bu C # mümkün olmadığını anlıyorum. Yoksa bir şey mi kaçırıyorum? (Tabii ki C ++ eşlemesi hiç gerekli değildir, çünkü dizi elemanları için çağrılacak varsayılan bir ctor sağlayabilir.)

FWIW, işte bir C ++ eşdeğeri: https://ideone.com/wG8yEh .


2

Mantıklarınızı tersine çevirebiliyorsanız Array.Clear(), boolean dizisini false olarak ayarlamak için yöntemi kullanabilirsiniz .

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

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

2

.NET Core, .NET Standard> = 2.1 kullanıyorsanız veya System.Memory paketine bağlıysanız, Span<T>.Fill()yöntemi de kullanabilirsiniz :

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


2

İşte bizim için Microsoft tarafından terk edilen Framework kullanıcıları için başka bir sürüm. Panos Theof'un çözümü ile Eric J ve Petar Petrov'un paralel olanından 4 kat daha hızlı Array.Clearve daha hızlı - büyük diziler için iki kata kadar daha hızlıdır.

Öncelikle size işlevin atalarını sunmak istiyorum, çünkü bu kodu anlamayı kolaylaştırıyor. Performans açısından bu Panos Theof'un koduna eşittir ve zaten yeterli olabilecek bazı şeyler için:

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

Gördüğünüz gibi, bu zaten başlatılmış olan parçanın tekrarlanan iki katına dayanmaktadır. Bu basit ve verimlidir, ancak modern bellek mimarileri ile doludur. Bu nedenle, yalnızca önbellek dostu bir tohum bloğu oluşturmak için iki katına çıkaran ve daha sonra hedef alan üzerinde tekrarlanan şekilde patlatılan bir versiyon doğdu:

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

Not: (count + 1) >> 1son kopya işleminin kalan tüm öğeleri kapsayacak kadar yem içerdiğinden emin olmak için iki kat döngü için sınır olarak gerekli olan önceki kod . Bunun count >> 1yerine tek sayı sayılırsa durum böyle olmaz . Mevcut sürüm için bu önemli değildir, çünkü doğrusal kopya döngüsü herhangi bir boşluğu alır.

Dizi hücresinin boyutu parametre olarak iletilmelidir, çünkü - zihin boggles - jeneriklerin gelecekte kullanılabilir olabilecek veya olmayabilecek sizeofbir kısıt ( unmanaged) kullanmadıkları sürece kullanılmasına izin verilmez . Yanlış tahminler önemli değildir, ancak değer doğruysa performans aşağıdaki nedenlerle en iyisidir:

  • Eleman boyutunu küçümsemek, L1 önbelleğinin yarısından daha büyük blok boyutlarına yol açabilir, böylece kopya kaynak verilerinin L1'den çıkarılma ve daha yavaş önbellek düzeylerinden yeniden getirilmesi olasılığı artar.

  • Eleman boyutunun fazla tahmin edilmesi, CPU'nun L1 önbelleğinin gereğinden az kullanılmasıyla sonuçlanır, yani doğrusal blok kopya döngüsü en uygun kullanımdan daha sık yürütülür. Böylece, sabit döngü / çağrı ek yükü kesinlikle gerekenden daha fazla gerçekleşir.

Kodumu karşılayan bir kıyaslama Array.Clearve daha önce bahsedilen diğer üç çözüm. Zamanlamalar Int32[], verilen boyutlardaki tamsayı dizilerini ( ) doldurmak içindir . Önbellek değişkenlerinin vb. Neden olduğu varyasyonu azaltmak için her test arka arkaya iki kez yürütüldü ve ikinci uygulama için zamanlamalar yapıldı.

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 

Bu kodun performansı yeterli değilse, ümit verici bir yol doğrusal kopya döngüsünü (aynı kaynak bloğunu kullanan tüm iş parçacıklarıyla) veya eski dostumuz P / Invoke'u paralelleştirecektir.

Not: blokların temizlenmesi ve doldurulması normalde MMX / SSE talimatları ve ne olursa olsun, son derece özel bir koda dallanan çalışma zamanı rutinleri tarafından yapılır, bu nedenle herhangi bir iyi ortamda, sadece ilgili ahlaki eşdeğerini çağırır ve std::memsetprofesyonel performans seviyelerinden emin olabilirsiniz. IOW, haklar gereği kütüphane fonksiyonu Array.Cleartüm elle haddelenmiş versiyonlarımızı toz içinde bırakmalıdır. Bunun tam tersi olması, bu tür şeylerin ne kadar uzak olduğunu gerçekten gösterir. Aynı şey Fill<>ilk etapta kendi başına haddeleme yapmak için de geçerlidir , çünkü hala sadece Çekirdek ve Standarttadır, ancak Çerçevede değildir. .NET neredeyse yirmi yıldır var ve hala en temel şeyler için sol ve sağ P / Invoke veya kendi rulo ...



0

İşte böyle bir yapıcıya sahip System.Collections.BitArrayolan başka bir yaklaşım .

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

veya

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

0

Diziyi yaptığınız ve bunun için bir alıcı ve ayarlayıcıya sahip olduğunuz özel bir sınıf yapın. Rastgele gibi benzersiz bir şey olmak için dizideki her pozisyona ihtiyacınız yoksa, int? bir dizi olarak ve sonra konum eşitse boş olsun, bu konumu doldurun ve yeni rastgele değeri döndürün.

IsVisibleHandler
{

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

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

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

Veya kullan

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

Setter olarak.


-2
Boolean[] data = new Boolean[25];

new Action<Boolean[]>((p) => { BitArray seed = new BitArray(p.Length, true); seed.CopyTo(p, 0); }).Invoke(data);

Lütfen başkalarının çözümünüzü daha iyi anlayabilmesi için daha iyi biçimlendirme ve belki birkaç açıklayıcı kelime kullanın.
Gorgsenegger

1
Hedef diziyi bölümlere ayırarak ve çekirdeği çeşitli bölümlere kopyalayarak başlatma performansını artırmak için bunu kullanabilirsiniz. Bu sadece bir fikir vermekti - Bu benim ilk ve son gönderimdi.
ldsmithperrin
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.