none
Увеличение скорости выполнения RRS feed

  • Вопрос

  • Есть массив порядка 300000 элементов из  структур типа

    struct Data
    {
      short value1;
      bool value2;
      short value3;
    }

    необходимо записать этот массив в массив byte . Вот реализация

    private void OnGetData(Data[] arr) { int count = arr.Length; byte[] cache = new byte[count * 5 + 2]; fixed(Data* data = arr) { Data* d = data; cache[0] = Main.GET_DATA; cache[1] = Main.SUCCESS; int offset = 2; for (int i = 0; i < count; i++) { short v1 = d->value1; bool v2 = d->value2; short v3 = d->value3; cache[offset] = (byte)v1; cache[offset + 1] = (byte)(v1 >> 8); cache[offset + 2] = *((byte*)(&v2)); cache[offset + 3] = (byte)(v3); cache[offset + 4] = (byte)(v3>> 8); offset += 5; d++; } }

    }

    Есть ли возможность ускорить выполнение?




    • Изменено woofer 11 февраля 2014 г. 12:02
    11 февраля 2014 г. 9:04

Ответы

  • Реализовал так 

    int count = arr.Length;
    byte[] cache = new byte[count * 5 + 2];
    fixed (byte* b = cache)
    fixed (void* pData = arr)
    {
      byte* pBytes = (byte*)pData;
      byte* bb = b;
      bb[0] = 0;
      bb[1] = 1;
    
      for (int i = 2, j = 0; i < count; i++,j++)
      {
         bb[i] = pBytes[j];
      }
    }
    Выполняется примерно в 3 раза быстрее.

    13 февраля 2014 г. 9:52

Все ответы

  • Посмотрите класс BitConverter и его методы GetBytes. Только, в этом случае, лучше заполнять не массив байтов, а список List<byte>, вызывая его метод AddRange. А когда список будет заполнен, получить из него массив методом ToArray.
    11 февраля 2014 г. 11:25
  • Изначально было сделано именно так. Работает раз в 30 медленнее.
    11 февраля 2014 г. 12:01
  • Тогда нужно понять, для каких целей Вам нужен этот массив? Если для передачи (в сеть, файл и т.п.), может воспользоваться сериализацией? А если для последующей обработки в неуправляемом коде, посмотрите это.
    11 февраля 2014 г. 12:40
  • Нужно использование указателей доводить до конца: и со вторым массивом работать через указатель. Что-то вроде:

    int count = arr.Length;
    byte[] cache = new byte[count * 5 + 2];
    cache[0] = 0;
    cache[1] = 1;
    
    fixed (Data* data = arr)
    fixed (byte* dest = cache)
    {
        Data* d = data;
        byte* p = dest + 2;
    
        for (int i = 0; i < count; i++)
        {
            short v1 = d->value1;
            bool v2 = d->value2;
            short v3 = d->value3;
    
            *p = (byte)v1;
            *(p + 1) = (byte)(v1 >> 8);
            *(p + 2) = *((byte*)(&v2));
            *(p + 3) = (byte)(v3);
            *(p + 4) = (byte)(v3 >> 8);
    
            p += 5;
            d++;
        }
    }

    По крайней мере, это гарантированно избавит от проверок границы массива.

    Далее. Вот эти присваивания:

    short v1 = d->value1;
    bool v2 = d->value2;
    short v3 = d->value3;

    замедляют работу. Можно попробовать использовать значения d->valueX без введения дополнительных переменных.

    -----

    Я присоединяюсь к вопросу kosuke904: зачем делается эта конвертация?

    Зачастую гораздо больший выигрыш в производительности можно получить за счёт изменения алгоритма на высоком уровне, чем от микрооптимизаций.

    Оператор fixed фиксирует массив данных в памяти, не позволяя сборщику мусора перемещать его. В итоге работа GC осложняется: ему приходится выполнять больше действий при сборке мусора, когда он встречает зафиксированные области памяти. Поэтому, теоретически, такой unsafe-код может замедлить работу приложения, если будет происходить сбор мусора во время его выполнения.

    11 февраля 2014 г. 14:28
  • Реализовал так 

    int count = arr.Length;
    byte[] cache = new byte[count * 5 + 2];
    fixed (byte* b = cache)
    fixed (void* pData = arr)
    {
      byte* pBytes = (byte*)pData;
      byte* bb = b;
      bb[0] = 0;
      bb[1] = 1;
    
      for (int i = 2, j = 0; i < count; i++,j++)
      {
         bb[i] = pBytes[j];
      }
    }
    Выполняется примерно в 3 раза быстрее.

    13 февраля 2014 г. 9:52