我正在试验流体动力学代码,在某些操作中降低浮点数的精度,以测试是否真的需要双精度。
为此,我编写了一个truncate函数,它将双精度向量的精度降低到单精度,而不转换数据。这使我可以评估某些函数的准确性,而无需将代码转换为单精度。由于这些评估在计算上很昂贵,我的目标是具有尽可能高的性能的截断功能。我试过以下,有没有办法提高truncate
函数的性能?
#include <vector>
#include <iostream>
#include <iomanip>
#include <chrono>
#include <random>
void truncate(std::vector<double>& v)
{
for (double& d : v)
{
float d_float = static_cast<float>(d);
d = static_cast<double>(d_float);
}
}
int main()
{
std::random_device rd;
std::mt19937 mt(rd());
std::uniform_real_distribution<double> dist(0., 1.);
const int n = 512*512*512;
std::vector<double>v(n);
for (double& d : v)
d = dist(mt);
std::cout << "Before: " << std::setprecision(15) << v[0] << std::endl;
auto start = std::chrono::high_resolution_clock::now();
truncate(v);
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::high_resolution_clock::now() - start);
std::cout << "After: " << std::setprecision(15) << v[0] << std::endl;
std::cout << "Duration in microseconds: " << duration.count() << std::endl;
return 0;
};
答案 0 :(得分:4)
对于truncate函数的绝对性能,您可能需要手动执行操作;假设您可以访问OpenMP和SSE内在函数,请查看_mm_cvtpd_ps(将2个双精度转换为2个浮点数)和_mm_cvtps_pd(转换回双精度数)。
类似的东西:
post_id
这是我会尝试的事情;你可以使用OpenMP选项,内在函数的确切形状(如果你有它可能使用AVX)等等。
编辑:AVX变体只是_mm256_cvtpd_ps等等,如果你可以实现这个建议,你也可以实现AVX版本。
答案 1 :(得分:2)
如果你只想快速截断双精度浮点数,那么有更快(更黑客)的方法。根据您已经知道或可以假设的数字,它可以更快或更慢。
对于这个解决方案,我假设你可以有零,但没有非正规,NaN或无穷大。换句话说,我可以掩盖浮子不具有的每一个位并得到足够接近的近似值:
for (double &d : doubles) { (*(uint64_t*)&d) &= 0xFFFF_FFFF_E000_0000; }
这保持你的符号位和指数,以及23位的尾数。为了完全准确,你还需要剪切指数 - 但它会导致非正规(我们假设不会发生)或无穷大(相同)。
请注意,确实通知处理器实际类型的解决方案可能更好,更准确。这是一个解释性帖子,用于说明浮点数和双精度数之间的实际差异是什么。
答案 2 :(得分:0)
您是否考虑过使用多线程版本的截断功能?类似的东西:
void truncate(std::vector<double>& v, const int n_threads = 1)
{
if(n_threads <= 1) {
for (double& d : v) {
float d_float = static_cast<float>(d);
d = static_cast<double>(d_float);
}
}
else {
std::vector<std::thread> threads;
for (size_t id = 0; id < n_threads; ++id) {
auto threadFunc = [=,&v]() {
size_t beg = id*v.size()/n_threads;
size_t end = std::min(v.size(), (id+1)*v.size()/n_threads + (id == n_threads-1)*(v.size() % n_threads));
for (size_t i=beg; i < end; ++i) {
float d = static_cast<float>(v[i]);
v[i] = static_cast<double>(d);
}
};
threads.push_back(std::thread(threadFunc));
}
for (auto & t : threads) t.join();
}
}
对于大型向量,如果你能负担得起使用很多线程,那么增益应该很重要。
答案 3 :(得分:0)
您是否考虑使用普通的旧typedef(我更喜欢使用C ++ 11的别名)作为using myType = float
,然后使用std::vector<myType>
作为您希望在代码中浮动的变量?这样可以准确地了解模拟的准确性和性能。
这里传播使用myType
需要一些时间,但IMO是值得的,因为如果你愿意,你可以翻回来加倍。同样正如@steiner所指出的那样,尽可能使用并行结构也会提高性能。