단일 값으로 C # 배열을 채우거나 인스턴스화하는 방법은 무엇입니까?


205

C #에서 인스턴스화 된 값 유형의 배열은 유형의 기본값 으로 자동으로 채워진다는 것을 알고 있습니다 (예 : bool의 경우 false, int의 경우 0 등).

기본값이 아닌 시드 값으로 배열을 자동으로 채우는 방법이 있습니까? 나중에 Java의 Arrays.fill () 과 같은 생성 또는 내장 메소드가 있습니까? 기본적으로 false 대신 부울 배열을 원한다고 가정 해보십시오. 이 작업을 수행하는 기본 제공 방법이 있습니까, 아니면 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;
 }

배열을 반복하고 각 값을 true로 "재설정"하는 것은 비효율적입니다. 어쨌든 주위에 있습니까? 어쩌면 모든 가치를 뒤집어서?

이 질문을 입력하고 그것에 대해 생각한 후에, 기본값은 C #이 장면 뒤에서 이러한 객체의 메모리 할당을 처리하는 방법의 결과 일 뿐이라고 생각합니다. 그러나 나는 아직도 확실하게 알고 싶다!


일반적으로 이름을 is_found에서 is_still_hiding으로 변경합니다. 답변을 좋아하지만 테스트 사례에서 int 배열에 대해서도 비슷한 작업을 수행해야했습니다. (좋은 질문)
ctrl-alt-delor

답변:


146

프레임 워크 방법을 모르지만 빠른 도우미를 작성하면됩니다.

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

3
사본이 필요하지 않은 경우 i ++ 대신 ++ i를 선호하십시오.
void.pointer

24
i ++는 i를 복사하고 i를 증가시키고 원래 값을 반환합니다. ++ i는 증가 된 값을 반환합니다. 따라서 ++ i는 더 빠르며 여기에서 이야기하는 것처럼 큰 루프에서 중요 할 수 있습니다.
tenpn

57
@RobertDailey : 이것은 컴파일러 최적화이며 더 이상 사실이 아닙니다. 방금 내 믿음을 검증하기 위해 테스트했습니다 .i ++의 반환 값이 아무것도 사용되지 않으면 컴파일러가 자동으로 ++ i로 컴파일합니다. 또한 반환 값을 사용하더라도 성능 차이가 너무 작아서 측정하기 위해 극단적 인 경우를 만들어야했습니다. 그럼에도 불구하고, 단지 몇 퍼센트의 다른 런타임을 초래했습니다.
Edward Ned Harvey

8
나는 이와 같은 확장 메소드를 작성했지만 다음과 같은 메소드 체인을 허용하기 위해 원래 배열을 리턴했다.int[] arr = new int[16].Populate(-1);
Gutblender

2
변경 void으로 T[]다음을 수행 할 수 있습니다var a = new int[100].Polupate(1)
orad

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

70
이것이 작동하는 동안 그것은 매우 느리기 때문에 실제로 좋은 해결책은 아닙니다. 실제로 for 루프로 반복하는 것보다 약 4 배 느립니다.
patjbs 2016 년

4
맞습니다. 성능을 고려할 때 for 루프가 더 빠릅니다.
Rony

6
실제 벤치 마크를 보려면 C # Initialize Array를 살펴보십시오 .
theknut

4
Enumerable.ToArray열거 가능한 시퀀스의 크기를 모르므로 배열 크기에 대해 추측해야합니다. 즉 ToArray, 버퍼가 초과 될 때마다 배열 할당 과 트림 종료시 하나 이상의 할당이 제공됩니다. 열거 가능한 객체와 관련된 오버 헤드도 있습니다.
Edward Brey

5
참고로 참조 유형을 사용하면 동일한 단일 객체에 대한 모든 참조로 전체 배열을 채 웁니다. 이것이 원하는 것이 아니며 실제로 각 배열 항목에 대해 서로 다른 객체를 생성하려면 stackoverflow.com/a/44937053/23715를 참조하십시오 .
Alex Che

74

천 개의 true값 으로 새 배열을 만듭니다 .

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

마찬가지로 정수 시퀀스를 생성 할 수 있습니다.

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

8
나쁘지는 않지만 여전히 for 루프보다 4 배 정도 느립니다.
patjbs

1
미래에 이론적으로 patjbs Enumerable. Repeat는 병렬 구현을 사용하기 때문에 더 빠르게 수행됩니다.
Petar Petrov

1
@PetarPetrov 이것은 캐시 스 래싱으로 인해 발생하지 않습니다. CPU 캐시의 특성으로 인해 단일 어레이에서 병렬로 작업을 수행하면 컴퓨터가 동기 작업을 기대하고 데이터를 적절히로드하기 때문에 항상 속도가 느려질 것입니다.
TernaryTopiary

계획된 비관! = 조기 최적화 부족.
Denis Gladkiy

24

가변 크기의 대형 배열 또는 배열의 경우 다음을 사용해야합니다.

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

작은 배열의 경우 C # 3의 컬렉션 초기화 구문을 사용할 수 있습니다.

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

컬렉션 초기화 구문의 장점은 각 슬롯에서 동일한 값을 사용할 필요가 없으며 표현식 또는 함수를 사용하여 슬롯을 초기화 할 수 있다는 것입니다. 또한 배열 슬롯을 기본값으로 초기화하는 비용을 피한다고 생각합니다. 예를 들어,

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

: 그리고 플로트 [] 배열 초기화float[] AlzCalDefault = new float[] {(float) 0.5, 18, 500, 1, 0};
짐 Lahman

배열의 FWIW 초기화 같은 C #의 모든 버전에서 수행 할 수 있습니다 :bool[] vals = { false, true, false, !(a || b) && c, SomeBoolMethod() };
피터 밴을 Heijden 데르

24

배열이 너무 큰 경우 BitArray를 사용해야합니다. 부울 배열과 같이 바이트 대신 모든 부울에 1 비트를 사용하며 비트 연산자로 모든 비트를 true로 설정할 수 있습니다. 또는 그냥 true로 초기화하십시오. 한 번만 수행하면 비용이 더 많이 듭니다.

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

Array.Fill.NET Core 2.0 이상 및 .NET Standard 2.1 이상에서 사용할 수 있습니다 .


우수한! 비교적 새로운 방법이라는 점에 유의하십시오. 그것은 그러나 특히, .NET 코어 2.0 및 .NET 표준 2.1에서 사용할 수 없습니다 닷넷 프레임 워크 버전 중입니다. .NET 5.0과 .NET Framework 및 .NET Core를 혼합 한 .NET 5.0에 있습니다.
Abel

9

불행히도 직접적인 방법은 없다고 생각하지만 배열 클래스의 확장 메소드를 작성하여이를 수행 할 수 있다고 생각합니다.

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

확장 아이디어를 더 많이 파고 들었습니다. 때로는 선불적이고 간단한 해결책이 실제로 최고입니다!
patjbs 2016 년

8

조금 더 인터넷 검색하고 읽은 후에 나는 이것을 발견했다.

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

내가 찾고있는 것에 확실히 더 가깝습니다. 그러나 for 루프에서 원래 배열을 반복하고 값을 변경하는 것보다 낫다는 것이 확실하지 않습니다. 실제로 빠른 테스트를 한 후에는 약 5 배 정도 느려 보입니다. 따라서 좋은 해결책은 아닙니다!


4
배열의 각 요소에 대한 함수 호출을 제외하고는 수행하려는 작업과 유사합니다. 문법적으로는 훨씬 더 멋지게 보일지 모르지만 훨씬 더 많은 일을하고 있습니다.
Nader Shirazie

예, 그것은 단순히 for 루프가 다른 것뿐만 아니라 다른 일을하는 것처럼
보입니다

새 배열을 만듭니다 (원래 인스턴스를 변경하지 않음).
Jeppe Stig Nielsen 님이

7

병렬 구현은 어떻습니까?

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

배열을 초기화 할 때이 코드의 힘을 볼 수는 없지만 "순수"를 잊어 버려야한다고 생각합니다.


이로 인해 다른 스레드가 CPU 캐시 라인과 경쟁하여 단일 스레드 구현에 비해 성능이 저하되는 허위 공유 문제가 발생할 위험이 있습니다. 이것이 발생하는지 여부는 스레드 별 메모리 블록의 크기와 CPU 아키텍처에 따라 다릅니다.
Eric J.

7

아래 코드는 작은 복사본에 대한 간단한 반복과 큰 복사본에 대한 Array.Copy를 결합한 것입니다.

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

int [] 배열을 사용하는 다른 배열 길이에 대한 벤치 마크는 다음과 같습니다.

         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

첫 번째 열은 배열 크기이며 간단한 반복 (@JaredPared 구현)을 사용한 복사 시간입니다. 이 방법의 시간은 그 후입니다. 다음은 4 개의 정수로 구성된 구조체 배열을 사용하는 벤치 마크입니다.

         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

아니면 ... 간단히 역 논리를 사용할 수도 있습니다. false의미를 바꾸자 true.

코드 샘플

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

재미있는 해결책, 모두 0을 잃기 때문에 ints와 함께 훨씬 어려울 것입니다.
MrFox

1
대신이 실제로 만약 당신이 변수 이름의 "논리 반전"실행 가능한 옵션입니다 bool[] isVisible메이크업이bool[] isHidden
마르쿠스 허터

1
사람들은 이런 종류의 재미있는 해킹처럼 반응하는 것 같습니다. 일반적인 최적화 기술입니다. 운이 좋으면 컴파일러가이를 수행합니다.
l33t

4

이것은 또한 작동하지만 불필요 할 수 있습니다

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

4

여기에 제시된 많은 답변은 한 번에 한 요소 씩 배열을 초기화하는 루프로 요약되며, 이는 한 번에 메모리 블록에서 작동하도록 설계된 CPU 명령을 이용하지 않습니다.

.Net Standard 2.1 (이 글을 쓰는 시점에서 미리보기)은 Array.Fill ( )을 제공합니다.이 라이브러리는 런타임 라이브러리의 고성능 구현에 적합합니다 (현재 .NET Core 는 그렇지 않습니다) 활용 가능성이) .

이전 플랫폼의 경우 다음 확장 방법은 배열 크기가 클 때 사소한 루프보다 실질적인 마진을 능가합니다. 온라인 코드 챌린지에 대한 솔루션이 할당 된 시간 예산보다 약 20 % 정도되었을 때 작성했습니다. 런타임을 약 70 % 줄였습니다. 이 경우 다른 루프 내에서 배열 채우기가 수행되었습니다. BLOCK_SIZE는 실험보다는 직감으로 설정되었습니다. 일부 최적화가 가능합니다 (예 : 이미 고정 된 크기의 블록이 아닌 원하는 값으로 이미 설정된 모든 바이트 복사).

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

배열에 몇 가지 값만 설정하려고하지만 대부분의 경우 (사용자 정의) 기본값을 얻으려면 다음과 같이 시도하십시오.

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

배열 자체의 인터페이스와 같이 다른 인터페이스를 유용하게 사용하려면 다른 인터페이스를 구현해야 할 수도 있습니다.


3

배열의 모든 요소를 ​​단일 작업 (UNLESS)으로 설정하는 방법은 없습니다. 해당 값은 요소 유형 기본값입니다.

예를 들어, 정수 배열이면 단일 조작으로 모두 0으로 설정할 수 있습니다. Array.Clear(...)


2

나는 파티에 늦었다는 것을 알고 있지만 여기에 아이디어가 있습니다. 랩핑 된 유형의 독립형으로 사용될 수 있도록 랩핑 된 값과의 변환 연산자가있는 랩퍼를 작성하십시오. 이것은 실제로 @ l33t의 바보 같은 대답에서 영감을 얻었습니다.

먼저 (C ++에서 제공) C #에서는 배열 요소가 생성 될 때 기본 ctor가 호출되지 않는다는 것을 깨달았습니다. 대신 사용자 정의 기본 생성자가있는 경우에도! -모든 배열 요소는 0으로 초기화됩니다. 그것은 나를 놀라게했다.

따라서 원하는 값으로 기본 ctor를 제공하는 래퍼 클래스는 C ++에서는 아니지만 C ++에서는 배열에서 작동합니다. 해결 방법은 변환시 랩퍼 유형이 원하는 시드 값에 0을 맵핑하도록하는 것입니다. 그렇게하면 0으로 초기화 된 값이 모든 실제 목적으로 시드로 초기화 된 것처럼 보입니다.

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

이 패턴은 모든 값 유형에 적용 가능합니다. 예를 들어 4로 초기화 해야하는 경우 int에 대해 0에서 4까지 매핑 할 수 있습니다.

C ++에서 가능한 템플릿을 만들고 시드 값을 템플릿 매개 변수로 제공하고 싶지만 C #에서는 불가능하다는 것을 알고 있습니다. 아니면 뭔가 빠졌습니까? (물론 C ++ 매핑에서는 배열 요소에 대해 호출되는 기본 ctor를 제공 할 수 있으므로 전혀 필요하지 않습니다.)

FWIW, https://ideone.com/wG8yEh C ++에 해당 합니다.


2

논리를 반전시킬 수있는 경우이 Array.Clear()메소드를 사용하여 부울 배열을 false로 설정할 수 있습니다 .

        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 또는 System.Memory 패키지에 의존하는 경우 다음 Span<T>.Fill()방법 을 사용할 수도 있습니다 .

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

Microsoft에서 포기한 Framework 사용자를위한 또 다른 버전이 있습니다. Panos Theof의 솔루션Eric JPetar Petrov의 병렬 솔루션Array.Clear 보다 4 배 빠릅니다. 최대 2 배 빠른 대형 배열로 -.

먼저 코드를 이해하기 쉽기 때문에 함수의 조상을 보여 드리고자합니다. 성능 측면에서 이것은 Panos Theof의 코드와 거의 비슷하며 이미 충분할 수도 있습니다.

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

보시다시피, 이것은 이미 초기화 된 부분의 반복되는 배가에 기초합니다. 이것은 간단하고 효율적이지만 현대 메모리 아키텍처를 무시합니다. 따라서 캐시 친화적 인 시드 블록을 만들기 위해 두 배만 사용하는 버전이 탄생 한 후 대상 영역에 반복적으로 분사됩니다.

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

참고 : 이전 코드 (count + 1) >> 1는 최종 복사 작업에 남은 모든 항목을 포괄 할 수있는 충분한 사료를 확보하기 위해 배가 루프의 제한으로 필요 했습니다. count >> 1대신 에 홀수 카운트 를 사용 하는 경우에는 그렇지 않습니다 . 현재 버전의 경우 선형 복사 루프가 느슨해지지 않기 때문에 이것은 중요하지 않습니다.

배열 셀의 크기는 매개 변수로 전달되어야합니다. 마인드 보글-제네릭은 앞으로 사용할 수 있거나 없을 수 sizeof있는 제약 조건 ( unmanaged) 을 사용 하지 않으면 사용할 수 없기 때문입니다. 잘못된 추정은 큰 문제는 아니지만 다음과 같은 이유로 값이 정확한 경우 성능이 가장 좋습니다.

  • 요소 크기를 과소 평가하면 L1 캐시의 절반보다 큰 블록 크기로 이어질 수 있으므로 L1에서 복사 소스 데이터가 제거되고 느린 캐시 레벨에서 다시 가져와야 할 가능성이 높아집니다.

  • 요소 크기를 과대 평가하면 CPU의 L1 캐시가 충분히 활용되지 않으므로 선형 블록 복사 루프가 최적의 활용보다 자주 실행됩니다. 따라서, 고정 루프 / 호출 오버 헤드의 많은 부분이 반드시 필요한 것보다 많이 발생합니다.

여기 내 코드를 훔치는 벤치 마크가 있습니다. Array.Clear 와 앞에서 언급 한 다른 세 가지 솔루션이 있습니다. 타이밍은 Int32[]주어진 크기의 정수 배열 ( ) 을 채우기위한 것 입니다. 캐시 차고 등으로 인한 변동을 줄이기 위해 각 테스트는 두 번 연속으로 실행되었으며 두 번째 실행에 대한 타이밍이 사용되었습니다.

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 

이 코드의 성능이 충분하지 않으면 유망한 길은 선형 복사 루프 (모든 스레드가 동일한 소스 블록을 사용함) 또는 우리의 좋은 친구 P / Invoke를 병렬화하는 것입니다.

참고 : 블록 지우기 및 채우기는 일반적으로 MMX / SSE 명령어를 사용하여 고도로 특수화 된 코드로 분기되는 런타임 루틴에 의해 수행되므로 적절한 환경에서는 해당하는 각각의 도덕적 수준을 호출하고 std::memset전문적인 성능 수준을 보장합니다. IOW는 권리에 따라 라이브러리 기능 Array.Clear은 모든 수동 버전을 먼지에 남겨 두어야합니다. 그것이 다른 방법이라는 사실은 실제로 얼마나 많은 것들이 있는지를 보여줍니다. Fill<>그것은 여전히 ​​핵심과 표준에만 있지만 프레임 워크에는 없기 때문에 처음부터 자신을 굴려야 합니다. .NET은 거의 20 년 동안 존재 해 왔으며 여전히 가장 기본적인 것들을 위해 좌우로 P / Invoke를해야합니다.



0

System.Collections.BitArray이러한 생성자 가있는 다른 접근법 이 있습니다.

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

또는

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

0

배열을 만드는 곳에 내부 클래스를 만들고 getter 및 setter를 갖습니다. 배열의 각 위치가 임의의 것과 같이 독특 해야하는 경우가 아니라면 int? 배열로 다음 위치가 null 인 경우 해당 위치를 채우고 새로운 임의의 값을 반환합니다.

IsVisibleHandler
{

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

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

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

또는 사용

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

세터로.


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

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

다른 사람들이 귀하의 솔루션을 더 잘 이해할 수 있도록 더 나은 형식을 지정하고 몇 가지 설명을 사용하십시오.
Gorgsenegger

1
이를 사용하여 대상 어레이를 분할하고 시드를 다양한 파티션으로 복사하여 초기화 성능을 향상시킬 수 있습니다. 이것은 단지 아이디어를 제공하기위한 것입니다-이것은 나의 첫 번째이며 내 마지막 게시물이었습니다.
ldsmithperrin
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.