1

少なくとも ARMv5 までの ARM CPU では、ワード アラインされていないメモリ アドレスへのランダム アクセスは許可されていません。この問題について詳しくは、http: //lecs.cs.ucla.edu/wiki/index.php/XScale_alignmentを参照してください。解決策の 1 つは、コードを書き直すか、最初からこの配置を検討することです。ただし、その方法は述べられていません。ストリーム内でワード整列されていない 2 バイトまたは 4 バイトの整数があるバイト ストリームを考えます。パフォーマンスを大幅に低下させることなく、スマートな方法でこのデータにアクセスするにはどうすればよいでしょうか?

問題を説明するコード スニペットがあります。

#include <stdio.h>
#include <stdlib.h>

#define BUF_LEN 17

int main( int argc, char *argv[] ) {
    unsigned char   buf[BUF_LEN];
    int             i;
    unsigned short  *p_short;
    unsigned long   *p_long;

    /*  fill array  */
    (void) printf( "filling buffer:" );
    for ( i = 0; i < BUF_LEN; i++ ) {
        /* buf[i] = 1 << ( i % 8 ); */
        buf[i] = i;
        (void) printf( " %02hhX", buf[i] );
    }
    (void) printf( "\n" );

    /*  testing with short  */
    (void) printf( "accessing with short:" );
    for ( i = 0; i < BUF_LEN - sizeof(unsigned short); i++ ) {
        p_short = (unsigned short *) &buf[i];
        (void) printf( " %04hX", *p_short );
    }
    (void) printf( "\n" );

    /*  testing with long   */
    (void) printf( "accessing with long:" );
    for ( i = 0; i < BUF_LEN - sizeof(unsigned long); i++ ) {
        p_long = (unsigned long *) &buf[i];
        (void) printf( " %08lX", *p_long );
    }
    (void) printf( "\n" );

    return EXIT_SUCCESS;
}

x86 CPU での出力は次のとおりです。

filling buffer: 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10
accessing with short: 0100 0201 0302 0403 0504 0605 0706 0807 0908 0A09 0B0A 0C0B 0D0C 0E0D 0F0E
accessing with long: 03020100 04030201 05040302 06050403 07060504 08070605 09080706 0A090807 0B0A0908 0C0B0A09 0D0C0B0A 0E0D0C0B 0F0E0D0C

ATMEL AT91SAM9G20 ARMv5 コアでは、次のようになります (注: これは、この CPU の予想される動作です!):

filling buffer: 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10
accessing with short: 0100 0100 0302 0302 0504 0504 0706 0706 0908 0908 0B0A 0B0A 0D0C 0D0C 0F0E
accessing with long: 03020100 00030201 01000302 02010003 07060504 04070605 05040706 06050407 0B0A0908 080B0A09 09080B0A 0A09080B 0F0E0D0C

したがって、アライメントされていないアドレスでバイトストリームにアクセスしたい、またはアクセスする必要がある場合:ARMで効率的に行うにはどうすればよいですか?

4

3 に答える 3

2

アラインされた変数とアラインされていないバイト ストリームとの間で変換を行う、独自のパッキング/アンパッキング関数を記述します。例えば、

void unpack_uint32(uint8_t* unaligned_stream, uint32_t* aligned_var)
{
  // copy byte-by-byte from stream to var, you can fill in the details
}
于 2012-06-20T17:11:17.900 に答える
1

あなたの例は、どのプラットフォームでも問題を示します。もちろん簡単な修正:

unsigned char   *buf;
int             i;
unsigned short  *p_short;
unsigned long   p_long[BUF_LEN>>2];

データをより適切な位置合わせで整理できない場合 (バイト数が多いほど、パフォーマンスが向上することもあります)、すべてを 32 ビットとしてアドレス指定し、そこから部分を切り取ると、オプティマイザーがショートとワード内のバイト(実際には、構造体であれメモリから選択されたバイトであれ、構造体にバイトとショートを含めると、すべてをワードとして渡す場合よりも余分な命令があるため、システムエンジニアリングを行う必要があります)。

整列されていない単語を抽出する例。(もちろん、エンディアンを管理する必要があります)

a = (lptr[offset]<<16)|(lptr[offset+1]>>16);

armv4 から現在までのすべての arm コアは、アライメントされていないアクセスを許可します。ほとんどの場合、デフォルトで例外がオンになっていますが、オフにすることができます。現在、古いものはワード内でローテーションしますが、私が間違っていなければ、他のものは他のバイト レーンを取得できます。

システム エンジニアリングを行い、パフォーマンス分析を行い、すべてを言葉として動かす方が速いか遅いかを判断します。データの実際の移動にはいくらかのオーバーヘッドがありますが、すべてが調整されていれば、両側のコードははるかに高速に実行されます。データの生成と受信を 2 倍から 4 倍改善するために、X 倍遅いデータ移動に耐えられますか?

于 2012-06-20T20:51:15.007 に答える
0

この関数は、常に整列された32ビットアクセスを使用します。

uint32_t fetch_unaligned_uint32 (uint8_t *unaligned_stream)
{
    switch (((uint32_t )unaligned_stream) & 3u)
    {
        case 3u:
            return ((*(uint32_t *)unaligned_stream[-3]) << 24)
                 | ((*(uint32_t *)unaligned_stream[ 1]) & 0xffffffu);
        case 2u:
            return ((*(uint32_t *)unaligned_stream[-2]) << 16)
                 | ((*(uint32_t *)unaligned_stream[ 2]) & 0x00ffffu);
        case 1u:
            return ((*(uint32_t *)unaligned_stream[-1]) <<  8)
                 | ((*(uint32_t *)unaligned_stream[ 3]) & 0x0000ffu);
        case 0u:
        default:
            return *(uint32_t *)unaligned_stream;
    }
}

4バイトすべてを個別に読み取ってシフトするよりも高速な場合があります。

于 2012-06-20T22:27:53.647 に答える