【发布时间】:2013-06-25 06:06:11
【问题描述】:
原来的问题:
所以我写了一些代码来试验线程并做一些测试。
代码应该创建一些数字,然后找到这些数字的平均值。
我认为向您展示我目前所拥有的东西会更容易。我期望使用两个线程,代码的运行速度大约是原来的 2 倍。用秒表测量它,我认为它的运行速度慢了大约 6 倍!编辑:现在使用计算机和时钟()函数来告诉时间。
void findmean(std::vector<double>*, std::size_t, std::size_t, double*);
int main(int argn, char** argv)
{
// Program entry point
std::cout << "Generating data..." << std::endl;
// Create a vector containing many variables
std::vector<double> data;
for(uint32_t i = 1; i <= 1024 * 1024 * 128; i ++) data.push_back(i);
// Calculate mean using 1 core
double mean = 0;
std::cout << "Calculating mean, 1 Thread..." << std::endl;
findmean(&data, 0, data.size(), &mean);
mean /= (double)data.size();
// Print result
std::cout << " Mean=" << mean << std::endl;
// Repeat, using two threads
std::vector<std::thread> thread;
std::vector<double> result;
result.push_back(0.0);
result.push_back(0.0);
std::cout << "Calculating mean, 2 Threads..." << std::endl;
// Run threads
uint32_t halfsize = data.size() / 2;
uint32_t A = 0;
uint32_t B, C, D;
// Split the data into two blocks
if(data.size() % 2 == 0)
{
B = C = D = halfsize;
}
else if(data.size() % 2 == 1)
{
B = C = halfsize;
D = hsz + 1;
}
// Run with two threads
thread.push_back(std::thread(findmean, &data, A, B, &(result[0])));
thread.push_back(std::thread(findmean, &data, C, D , &(result[1])));
// Join threads
thread[0].join();
thread[1].join();
// Calculate result
mean = result[0] + result[1];
mean /= (double)data.size();
// Print result
std::cout << " Mean=" << mean << std::endl;
// Return
return EXIT_SUCCESS;
}
void findmean(std::vector<double>* datavec, std::size_t start, std::size_t length, double* result)
{
for(uint32_t i = 0; i < length; i ++) {
*result += (*datavec).at(start + i);
}
}
我不认为这段代码很精彩,如果你能提出改进它的方法,我也将不胜感激。
注册变量:
一些人建议为函数“findmean”创建一个局部变量。这就是我所做的:
void findmean(std::vector<double>* datavec, std::size_t start, std::size_t length, double* result)
{
register double holding = *result;
for(uint32_t i = 0; i < length; i ++) {
holding += (*datavec).at(start + i);
}
*result = holding;
}
我现在可以报告:代码运行的执行时间几乎与单线程相同。这是 6 倍的巨大改进,但肯定有办法让它快近两倍吗?
注册变量和 O2 优化:
我已将优化设置为“O2” - 我将创建一个包含结果的表格。
目前的结果:
没有优化或寄存器变量的原始代码: 1 个线程:4.98 秒,2 个线程:29.59 秒
添加了寄存器变量的代码: 1 线程:4.76 秒,2 线程:4.76 秒
使用 reg 变量和 -O2 优化: 1 个线程:0.43 秒,2 个线程:0.6 秒 2 个线程现在变慢了吗?
根据 Dameon 的建议,在两个结果变量之间放置一大块内存: 1 线程:0.42 秒,2 线程:0.64 秒
根据 TAS 的使用迭代器访问向量内容的建议: 1 线程:0.38 秒,2 线程:0.56 秒
Core i7 920(单通道内存 4GB)同上: 1 线程:0.31 秒,2 线程:0.56 秒
Core i7 920(双通道内存 2x2GB)同上: 1 线程:0.31 秒,2 线程:0.35 秒
【问题讨论】:
-
由于线程同时访问内存正好是 128 MiB 偏移量,这可能是错误共享。
-
听起来有点慢。在大约 1982 年的多处理器实验中,该应用在 2 个处理器上的运行速度仅比 1 个慢 30%。
-
@Damon,你能解释一下吗?您的意思是在 128MB 偏移处的总和之间存在重叠吗?什么是虚假分享?
-
@Edward Bird:这基本上意味着线程竞争相同的缓存行(这取决于关联性和缓存大小,这有点复杂......但我认为 Agner Fog 的网站上有一个综合公式,IIRC)。您可以尝试以不同的方式划分工作,例如以更小的(例如 128k 或 512k)块。如果这“神奇地”解决了性能问题,你就知道了。
-
@Damon 您能否向我解释一下为什么这可能会防止错误共享,因为 result[0] 和 result[1] 仍然是相邻的?
标签: c++ multithreading performance optimization