从任何内存地址读取 UInt32 最有效的方法是什么?

发布于 2024-12-28 23:16:21 字数 603 浏览 1 评论 0原文

从 C++ 中的任意内存地址读取 UInt32 值的最有效方法是什么? (假设 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 包含未对齐的地址,则可能需要多个内存周期,这会减慢代码速度。有没有更快的方法从非对齐地址读取值?

What would be the most efficient way to read a UInt32 value from an arbitrary memory address in C++? (Assuming Windows x86 or Windows x64 architecture.)

For example, consider having a byte pointer that points somewhere in memory to block that contains a combination of ints, string data, etc., all mixed together. The following sample shows reading the various fields from this block in a loop.

typedef unsigned char* BytePtr;
typedef unsigned int UInt32;

...

BytePtr pCurrent = ...;

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

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

        ...
    }

    pCurrent += ...;
}

If at line A, pPtr happens to contain a 4-byte-aligned address, reading the UInt32 should be a single memory read. If pPtr contains a non-aligned address, more than one memory cycles my be needed which slows the code down. Is there a faster way to read the value from non-aligned addresses?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

々眼睛长脚气 2025-01-04 23:16:21

我建议将 memcpy 放入循环中的 UInt32 类型的临时文件中。

这利用了这样一个事实:在启用优化的情况下构建时,编译器将内联四字节 memcpy,并且还有一些其他好处:

  • 如果您所在的平台需要对齐(hpux、solaris sparc...)代码不会陷入陷阱。
  • 在对齐很重要的平台上,可能值得对对齐进行地址检查,然后检查常规对齐加载或一组 4 字节加载和位或。您的编译器的 memcpy 很可能会以最佳方式执行此操作。
  • 如果您所在的平台允许未对齐访问并且不会影响性能(x86、x64、powerpc...),那么您几乎可以保证这样的 memcpy 将是执行此操作的最便宜的方法使用权。
  • 如果您的内存最初是指向某些其他数据结构的指针,则您的代码可能会由于别名问题而未定义,因为您正在转换为另一种类型并取消引用该转换。由于与别名相关的优化问题而导致的运行时问题很难追踪!假设您可以弄清楚它们,在已建立的代码中修复也可能非常困难,您可能必须使用晦涩的编译选项,例如 -fno-strict-aliasing 或 -qansialias,这会显着限制编译器的优化能力。

I'd recommend memcpy into a temporary of type UInt32 within your loop.

This takes advantage of the fact that a four byte memcpy will be inlined by the compiler when building with optimization enabled, and has a few other benefits:

  • If you are on a platform where alignment matters (hpux, solaris sparc, ...) your code isn't going to trap.
  • On a platform where alignment matters there it may be worthwhile to do an address check for alignment then one of a regular aligned load or a set of 4 byte loads and bit ors. Your compiler's memcpy very likely will do this the optimal way.
  • If you are on a platform where an unaligned access is allowed and doesn't hurt performance (x86, x64, powerpc, ...), you are pretty much guarenteed that such a memcpy is then going to be the cheapest way to do this access.
  • If your memory was initially a pointer to some other data structure, your code may be undefined because of aliasing problems, because you are casting to another type and dereferencing that cast. Run time problems due to aliasing related optimization issues are very hard to track down! Presuming that you can figure them out, fixing can also be very hard in established code and you may have to use obscure compilation options like -fno-strict-aliasing or -qansialias, which can limit the compiler's optimization ability significantly.
负佳期 2025-01-04 23:16:21

您的代码是未定义的行为。

几乎唯一“正确”的解决方案是仅将某些内容读取为类型 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;

:例如,您想要读取一个整数,唯一的方法就是拥有一个整数。如果您希望它包含某种二进制表示形式,则需要将该数据复制到从变量开头开始的地址。

Your code is undefined behaviour.

Pretty much the only "correct" solution is to only read something as a type T if it is a type T, as follows:

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;

In this example, you want to read an integer, and the only way to do that is to have an integer. If you want it to contain a certain binary representation, you need to copy that data to the address starting at the beginning of the variable.

沫离伤花 2025-01-04 23:16:21

让编译器来做优化!

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

Let the compiler do the optimizing!

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);
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文