Самый эффективный способ чтения UInt32 с любого адреса памяти? - PullRequest
3 голосов
/ 25 января 2012

Каков наиболее эффективный способ чтения значения UInt32 с произвольного адреса памяти в C ++? (Предполагая архитектуру Windows x86 или Windows x64.)

Например, рассмотрим наличие байтового указателя, который указывает где-то в памяти на блок, который содержит комбинацию целых чисел, строковых данных и т. Д., Все вместе. В следующем примере показано чтение различных полей из этого блока в цикле.

typedef unsigned char* BytePtr;
typedef unsigned int UInt32;

...

BytePtr pCurrent = ...;

while ( *pCurrent != 0 )
{
    ...

    if ( *pCurrent == ... )
    {
        UInt32 nValue = *( (UInt32*) ( pCurrent + 1 ) );    // line A

        ...
    }

    pCurrent += ...;
}

Если в строке A, pPtr содержит 4-байтовый выровненный адрес, чтение UInt32 должно быть чтением из одной памяти. Если pPtr содержит невыровненный адрес, может потребоваться более одного цикла памяти, что замедляет код. Есть ли более быстрый способ считывания значения с невыровненных адресов?

Ответы [ 3 ]

3 голосов
/ 26 января 2012

Я бы порекомендовал memcpy во временный тип UInt32 в вашем цикле.

Это использует тот факт, что четырехбайтовый memcpy будет встроен компилятором при сборке с включенной оптимизацией и имеетнесколько других преимуществ:

  • Если вы находитесь на платформе, где выравнивание имеет значение (hpux, solaris sparc, ...), ваш код не будет перехвачен.
  • На платформетам, где выравнивание имеет значение, может быть целесообразно выполнить проверку адреса для выравнивания, а затем одну из регулярных выровненных нагрузок или набора 4-байтовых нагрузок и битов или орбит.Скорее всего, memcpy вашего компилятора сделает это оптимальным образом.
  • Если вы находитесь на платформе, где разрешен не выровненный доступ и он не снижает производительность (x86, x64, powerpc, ...), выв значительной степени гарантировано, что такой memcpy будет самым дешевым способом сделать такой доступ.
  • Если ваша память изначально была указателем на какую-то другую структуру данных, ваш код может быть неопределенным из-за проблем с наложением, потому чтовы приводите к другому типу и разыменовываете его.Проблемы во время выполнения из-за связанных с оптимизацией проблем с псевдонимами очень трудно отследить!Предполагая, что вы можете их выяснить, исправление также может быть очень сложным в установленном коде, и вам, возможно, придется использовать неясные параметры компиляции, такие как -fno-strict-aliasing или -qansialias, которые могут значительно ограничить возможности оптимизации компилятора.
3 голосов
/ 25 января 2012

Ваш код поведения не определен.

Практически единственное «правильное» решение - это читать что-то только как тип T, если оно равно типу T, как показано ниже:

uint32_t n;
char * p = point_me_to_random_memory();

std::copy(p, p + 4, reinterpret_cast<char*>(&n));

std::cout << "The value is: " << n << std::endl;

В этом примере вы хотите прочитать целое число, и единственный способ сделать это - иметь целое число. Если вы хотите, чтобы оно содержало определенное двоичное представление, вам необходимо скопировать эти данные по адресу, начинающемуся с начала переменной.

0 голосов
/ 25 января 2012

Пусть компилятор выполняет оптимизацию!

UInt32 ReadU32(unsigned char *ptr)
{
    return  static_cast<UInt32>(ptr[0]) |
           (static_cast<UInt32>(ptr[1])<<8) |
           (static_cast<UInt32>(ptr[2])<<16) |
           (static_cast<UInt32>(ptr[3])<<24);
}
...