Подтвердить что ты не робот

Быстрее ли BitArray в С# для получения битового значения, чем простое соединение с побитовым сдвигом?

1). var bitValue = (byteValue & (1 << bitNumber)) != 0;

2). используя System.Collections.BitArray с помощью Get(int index)

  • Что быстрее?
  • В каких ситуациях для .NET-проектов BitArray может быть более полезным, чем простая связь с побитовым сдвигом?
4b9b3361

Ответ 1

BitArray сможет обрабатывать произвольное количество логических значений, тогда как a byte будет содержать только 8, int всего 32 и т.д. Это будет самая большая разница между ними.

Кроме того, BitArray реализует IEnumerable, где интегрального типа, очевидно, нет. Так что все зависит от требований вашего проекта; если вам нужен IEnumerable или подобный массиву интерфейс, перейдите к BitArray.

Я бы использовал bool[] для любого решения, просто потому, что он более явный в том, какие данные вы отслеживаете. Т

BitArray или bitfield будет использовать приблизительно 1/8-е пространство a bool[], потому что они "упаковывают" 8 булевых значений в один байт, тогда как a bool сам возьмет на себя весь 8- бит. Космическое преимущество использования битового поля или BitArray не имеет значения, пока вы не сохраните лоты bools. (Математика остается до читателя:-))


Benchmark

Результаты. Для моей примитивной тестовой среды кажется, что BitArray немного быстрее, но находится на том же уровне, что и сам с интегральным типом. Также был протестирован bool[], что было неудивительно самым быстрым. Доступ к одному байту в памяти будет менее сложным, чем доступ к отдельным битам в разных байтах.

Testing with 10000000 operations:
   A UInt32 bitfield took 808 ms.
   A BitArray (32) took 574 ms.
   A List<bool>(32) took 436 ms.

код:

class Program
{
    static void Main(string[] args)
    {
        Random r = new Random();
        r.Next(1000);

        const int N = 10000000;

        Console.WriteLine("Testing with {0} operations:", N);

        Console.WriteLine("   A UInt32 bitfield took {0} ms.", TestBitField(r, N));
        Console.WriteLine("   A BitArray (32) took {0} ms.", TestBitArray(r, N));
        Console.WriteLine("   A List<bool>(32) took {0} ms.", TestBoolArray(r, N));

        Console.Read();
    }


    static long TestBitField(Random r, int n)
    {
        UInt32 bitfield = 0;

        var sw = Stopwatch.StartNew();
        for (int i = 0; i < n; i++) {

            SetBit(ref bitfield, r.Next(32), true);
            bool b = GetBit(bitfield, r.Next(32));
            SetBit(ref bitfield, r.Next(32), b);
        }
        sw.Stop();
        return sw.ElapsedMilliseconds;
    }

    static bool GetBit(UInt32 x, int bitnum) {
        if (bitnum < 0 || bitnum > 31)
            throw new ArgumentOutOfRangeException("Invalid bit number");

        return (x & (1 << bitnum)) != 0;
    }

    static void SetBit(ref UInt32 x, int bitnum, bool val)
    {
        if (bitnum < 0 || bitnum > 31)
            throw new ArgumentOutOfRangeException("Invalid bit number");

        if (val)
            x |= (UInt32)(1 << bitnum);
        else
            x &= ~(UInt32)(1 << bitnum);
    }



    static long TestBitArray(Random r, int n)
    {
        BitArray b = new BitArray(32, false);     // 40 bytes

        var sw = Stopwatch.StartNew();
        for (int i = 0; i < n; i++) {

            b.Set(r.Next(32), true);
            bool v = b.Get(r.Next(32));
            b.Set(r.Next(32), v);
        }
        sw.Stop();
        return sw.ElapsedMilliseconds;
    }



    static long TestBoolArray(Random r, int n)
    {
        bool[] ba = new bool[32];

        var sw = Stopwatch.StartNew();
        for (int i = 0; i < n; i++) {

            ba[r.Next(32)] = true;
            bool v = ba[r.Next(32)];
            ba[r.Next(32)] = v;
        }
        sw.Stop();
        return sw.ElapsedMilliseconds;
    }
}

Ответ 2

@Джонатон Рейнхарт,

Ваш тест, к сожалению, неубедительный. Он не учитывает эффекты возможной ленивой загрузки, кэширования и/или предварительной выборки (CPU, ОС хоста и/или среды выполнения .NET).

Перемешайте порядок тестов (или несколько раз вызовите методы тестирования), и вы можете заметить различные измерения времени.

Я сделал свой оригинальный тест, построенный с целевой платформой "Любой процессор" и профилем клиента .NET 4.0, работающим на моей машине с процессором i7-3770 и 64-разрядной версией Windows 7.

Что я получил, это:

Testing with 10000000 operations:
   A UInt32 bitfield took 484 ms.
   A BitArray (32) took 459 ms.
   A List<bool>(32) took 393 ms.

что в значительной степени соответствует вашим наблюдениям.

Однако выполнение теста BitArray перед тестом UInt32 дало следующее:

Testing with 10000000 operations:
   A BitArray (32) took 513 ms.
   A UInt32 bitfield took 456 ms.
   A List<bool>(32) took 417 ms.

Изучив время тестов UInt32 и BitArray, вы заметите, что измеренное время, похоже, не связано с самими тестами, а скорее с порядком, в котором выполняются тесты.

Чтобы облегчить эти побочные эффекты, по крайней мере, немного, я дважды выполнил тестовые методы в каждой программе со следующими результатами.

Тестовый заказ UInt32, BitArray, BoolArray, UInt32, BitArray, BoolArray:

Testing with 10000000 operations:
   A UInt32 bitfield took 476 ms.
   A BitArray (32) took 448 ms.
   A List<bool>(32) took 367 ms.

   A UInt32 bitfield took 419 ms.  <<-- Watch this.
   A BitArray (32) took 444 ms.    <<-- Watch this.
   A List<bool>(32) took 388 ms.

Тестовый заказ BitArray, UInt32, BoolArray, BitArray, UInt32, BoolArray:

Testing with 10000000 operations:
   A BitArray (32) took 514 ms.
   A UInt32 bitfield took 413 ms.
   A List<bool>(32) took 379 ms.

   A BitArray (32) took 444 ms.    <<-- Watch this.
   A UInt32 bitfield took 413 ms.  <<-- Watch this.
   A List<bool>(32) took 381 ms.

Глядя на второстепенные вызовы методов тестирования, кажется, что по крайней мере на i7 процессорах с современной средой .NET, тест UInt32 быстрее, чем тест BitArray, в то время как Тест BoolArray по-прежнему является самым быстрым.

(Я прошу прощения, что мне пришлось написать свой ответ на тестовый тест Jonathon в качестве ответа, но, как новый пользователь SO, мне не разрешено комментировать...)

EDIT:

Вместо того, чтобы перетасовывать порядок тестовых методов, вы можете попробовать поставить Thread.Sleep(5000) или подобное прямо перед вызовом первого теста...

Также исходный тест, по-видимому, ставит тест UInt32 в невыгодное положение, поскольку он включает в себя проверку границы "if (bitnum < 0 || bitnum > 31)", которая выполняется 30 миллионов раз. Ни один из двух других тестов не включает такую ​​проверку границ. Однако на самом деле это не вся правда, так как и BitArray, и массив bool выполняют пограничные проверки внутри.

Хотя я не тестировал, я ожидаю, что устранение пограничных проверок сделает тесты UInt32 и BoolArray аналогичным образом, но это не будет хорошим предложением для публичного API.