有没有更快的方法来按位连接两个整数?
Is there a faster way to concatenate two integers bit-wise?
对于我的 C++ 程序,我想按位将两个 32 位无符号整数连接成一个 64 位无符号整数。类似的问题已被问过多次,答案大多与此类似:
#include <cstdint>
#include <iostream>
int main()
{
std::uint32_t leftHalf = 1;
std::uint32_t rightHalf = 2;
std::uint64_t concatenated = ((std::uint64_t) leftHalf << 32) | secondHalf;
std::cout << "left=" << leftHalf << " and right=" << rightHalf << " concatenated into " << concatenated << std::endl;
}
因为我必须在我的程序中经常执行这种连接,所以我需要它非常高效。使用转换、移位和按位 |,使它看起来像另一种技术(例如使用 memcpy)可能会更快。
有没有比强制转换、移位和按位 | 更快的连接两个整数的方法?
为了完整起见,我的非编译方法:
#include <cstdint>
#include <iostream>
int main()
{
std::uint32_t leftHalf = 1;
std::uint32_t rightHalf = 2;
std::uint64_t concatenated;
std::uint32_t *halfIt = &concatenated;
*halfIt = leftHalf;
++halfIt;
*halfIt = rightHalf;
std::cout << "left=" << leftHalf << " and right=" << rightHalf << " concatenated into " << concatenated << std::endl;
}
如果您找到一种有效的方法来将一个值的一部分(视为 bitset)复制到另一个值的一部分(视为 bitset),则可以额外提高速度。但我想这会有点老套。
顺便说一句,在下面的代码中,函数 concat1
在编译后的代码中比 concat2
.
少了一个命令
#include <iostream>
using namespace std;
std::uint64_t concat1(const std::uint32_t& leftHalf, const std::uint32_t& rightHalf){
std::uint64_t concatenated = leftHalf;
concatenated <<= 32;
concatenated |= rightHalf;
return concatenated;
}
std::uint64_t concat2(const std::uint32_t& leftHalf, const std::uint32_t& rightHalf){
std::uint64_t concatenated = (static_cast<std::uint64_t>(leftHalf) << 32) | rightHalf;
return concatenated;
}
int main() {
cout << concat1(1,2) <<std::endl;
cout << concat2(1,2) <<std::endl;
}
您可以在例如https://godbolt.org/。函数 concat1
比 concat2
需要少 mov
次操作。但差异会很小。我估计大约 5% 的运行时间;
concat1(unsigned int const&, unsigned int const&):
push rbp
mov rbp, rsp
mov QWORD PTR [rbp-24], rdi
mov QWORD PTR [rbp-32], rsi
mov rax, QWORD PTR [rbp-24]
mov eax, DWORD PTR [rax]
mov eax, eax
mov QWORD PTR [rbp-8], rax
sal QWORD PTR [rbp-8], 32
mov rax, QWORD PTR [rbp-32]
mov eax, DWORD PTR [rax]
mov eax, eax
or QWORD PTR [rbp-8], rax
mov rax, QWORD PTR [rbp-8]
pop rbp
ret
concat2(unsigned int const&, unsigned int const&):
push rbp
mov rbp, rsp
mov QWORD PTR [rbp-24], rdi
mov QWORD PTR [rbp-32], rsi
mov rax, QWORD PTR [rbp-24]
mov eax, DWORD PTR [rax]
mov eax, eax
sal rax, 32
mov rdx, rax
mov rax, QWORD PTR [rbp-32]
mov eax, DWORD PTR [rax]
mov eax, eax
or rax, rdx
mov QWORD PTR [rbp-8], rax
mov rax, QWORD PTR [rbp-8]
pop rbp
ret
discussion 的组合信息导致原始问题的以下答案:
视情况而定。此外,更快的方法很可能不会有所作为。
根据周围的代码,可以采用以下技术:
- 可以将 32 位部分存储为数组中的两个元素,然后
memcpy
将该数组存储为 64 位整数。 (suggested by NathanOliver)
- 根据 32 位值的存储方式,可以使用并行化,例如使用 AVX 命令 (suggested by sgorozco)
但这可能没什么区别
其他操作,例如从 [=39= 获取数据的单次获取],比 多次 bit-wise 操作需要更多 run-time(pointed out by JulianH).因此,对于当前的 CPU 设计,bit-wise 操作可能 运行 在 加载操作期间,下一条指令等待 time-consuming加载操作即将完成。
最后,强烈建议使用一种方法(例如 https://godbolt.org/、gcc -S
、perf 等分析器)来确定代码的哪些部分花费的时间最多。
对于我的 C++ 程序,我想按位将两个 32 位无符号整数连接成一个 64 位无符号整数。类似的问题已被问过多次,答案大多与此类似:
#include <cstdint>
#include <iostream>
int main()
{
std::uint32_t leftHalf = 1;
std::uint32_t rightHalf = 2;
std::uint64_t concatenated = ((std::uint64_t) leftHalf << 32) | secondHalf;
std::cout << "left=" << leftHalf << " and right=" << rightHalf << " concatenated into " << concatenated << std::endl;
}
因为我必须在我的程序中经常执行这种连接,所以我需要它非常高效。使用转换、移位和按位 |,使它看起来像另一种技术(例如使用 memcpy)可能会更快。
有没有比强制转换、移位和按位 | 更快的连接两个整数的方法?
为了完整起见,我的非编译方法:
#include <cstdint>
#include <iostream>
int main()
{
std::uint32_t leftHalf = 1;
std::uint32_t rightHalf = 2;
std::uint64_t concatenated;
std::uint32_t *halfIt = &concatenated;
*halfIt = leftHalf;
++halfIt;
*halfIt = rightHalf;
std::cout << "left=" << leftHalf << " and right=" << rightHalf << " concatenated into " << concatenated << std::endl;
}
如果您找到一种有效的方法来将一个值的一部分(视为 bitset)复制到另一个值的一部分(视为 bitset),则可以额外提高速度。但我想这会有点老套。
顺便说一句,在下面的代码中,函数 concat1
在编译后的代码中比 concat2
.
#include <iostream>
using namespace std;
std::uint64_t concat1(const std::uint32_t& leftHalf, const std::uint32_t& rightHalf){
std::uint64_t concatenated = leftHalf;
concatenated <<= 32;
concatenated |= rightHalf;
return concatenated;
}
std::uint64_t concat2(const std::uint32_t& leftHalf, const std::uint32_t& rightHalf){
std::uint64_t concatenated = (static_cast<std::uint64_t>(leftHalf) << 32) | rightHalf;
return concatenated;
}
int main() {
cout << concat1(1,2) <<std::endl;
cout << concat2(1,2) <<std::endl;
}
您可以在例如https://godbolt.org/。函数 concat1
比 concat2
需要少 mov
次操作。但差异会很小。我估计大约 5% 的运行时间;
concat1(unsigned int const&, unsigned int const&):
push rbp
mov rbp, rsp
mov QWORD PTR [rbp-24], rdi
mov QWORD PTR [rbp-32], rsi
mov rax, QWORD PTR [rbp-24]
mov eax, DWORD PTR [rax]
mov eax, eax
mov QWORD PTR [rbp-8], rax
sal QWORD PTR [rbp-8], 32
mov rax, QWORD PTR [rbp-32]
mov eax, DWORD PTR [rax]
mov eax, eax
or QWORD PTR [rbp-8], rax
mov rax, QWORD PTR [rbp-8]
pop rbp
ret
concat2(unsigned int const&, unsigned int const&):
push rbp
mov rbp, rsp
mov QWORD PTR [rbp-24], rdi
mov QWORD PTR [rbp-32], rsi
mov rax, QWORD PTR [rbp-24]
mov eax, DWORD PTR [rax]
mov eax, eax
sal rax, 32
mov rdx, rax
mov rax, QWORD PTR [rbp-32]
mov eax, DWORD PTR [rax]
mov eax, eax
or rax, rdx
mov QWORD PTR [rbp-8], rax
mov rax, QWORD PTR [rbp-8]
pop rbp
ret
discussion 的组合信息导致原始问题的以下答案:
视情况而定。此外,更快的方法很可能不会有所作为。
根据周围的代码,可以采用以下技术:
- 可以将 32 位部分存储为数组中的两个元素,然后
memcpy
将该数组存储为 64 位整数。 (suggested by NathanOliver) - 根据 32 位值的存储方式,可以使用并行化,例如使用 AVX 命令 (suggested by sgorozco)
但这可能没什么区别
其他操作,例如从 [=39= 获取数据的单次获取],比 多次 bit-wise 操作需要更多 run-time(pointed out by JulianH).因此,对于当前的 CPU 设计,bit-wise 操作可能 运行 在 加载操作期间,下一条指令等待 time-consuming加载操作即将完成。
最后,强烈建议使用一种方法(例如 https://godbolt.org/、gcc -S
、perf 等分析器)来确定代码的哪些部分花费的时间最多。