Gum*_*een 4 c++ multithreading
我有一些非常简单的C ++代码,可以肯定的是,使用多线程可以更快地运行3倍,但是以某种方式在Windows 10上的GCC和MSVC上只能运行3%(或更少)。
有没有互斥锁并没有共享资源。而且我看不到错误的共享或缓存颠簸如何发挥作用,因为每个线程仅修改数组的不同部分,该部分的int值超过十亿。我意识到类似这样的问题很多,但我还没有发现任何似乎可以解决这个特殊谜团的问题。
一个提示可能是,在多线程与单线程(〜885ms与〜2650ms)相比,将数组初始化移入add()函数循环确实使函数快了3倍。
请注意,只有该add()功能才被计时,在我的机器上大约需要600毫秒。我的机器有4个超线程内核,因此我正在将代码threadCount设置为8,然后设置为1。
知道会发生什么吗?有没有办法(适当时)关闭处理器中的功能,这些功能会导致诸如错误共享(可能就像我们在此处看到的那样)之类的事情发生?
#include <chrono>
#include <iostream>
#include <thread>
void startTimer();
void stopTimer();
void add(int* x, int* y, int threadIdx);
namespace ch = std::chrono;
auto start = ch::steady_clock::now();
const int threadCount = 8;
int itemCount = 1u << 30u; // ~1B items
int itemsPerThread = itemCount / threadCount;
int main() {
int* x = new int[itemCount];
int* y = new int[itemCount];
// Initialize arrays
for (int i = 0; i < itemCount; i++) {
x[i] = 1;
y[i] = 2;
}
// Call add() on multiple threads
std::thread threads[threadCount];
startTimer();
for (int i = 0; i < threadCount; ++i) {
threads[i] = std::thread(add, x, y, i);
}
for (auto& thread : threads) {
thread.join();
}
stopTimer();
// Verify results
for (int i = 0; i < itemCount; ++i) {
if (y[i] != 3) {
std::cout << "Error!";
}
}
delete[] x;
delete[] y;
}
void add(int* x, int* y, int threadIdx) {
int firstIdx = threadIdx * itemsPerThread;
int lastIdx = firstIdx + itemsPerThread - 1;
for (int i = firstIdx; i <= lastIdx; ++i) {
y[i] = x[i] + y[i];
}
}
void startTimer() {
start = ch::steady_clock::now();
}
void stopTimer() {
auto end = ch::steady_clock::now();
auto duration = ch::duration_cast<ch::milliseconds>(end - start).count();
std::cout << duration << " ms\n";
}
Run Code Online (Sandbox Code Playgroud)
您可能只是在提高计算机的内存传输速率,您正在执行8GB的读取和4GB的写入。
在我的机器上,您的测试将在500ms内完成,即24GB / s(这类似于内存带宽测试仪给出的结果)。
当您通过一次读取和一次写入访问每个内存地址时,缓存就没有多大用处,因为您不会重用内存。
您的问题不是处理器。您遇到了RAM读写延迟。由于您的缓存能够保存一些兆字节的数据,因此您远远超出了此存储空间。只要您可以将数据输入到处理器中,多线程就非常有用。与RAM相比,处理器中的缓存非常快。当您超出缓存存储空间时,这将导致RAM延迟测试。
如果要查看多线程的优势,则必须在缓存大小范围内选择数据大小。
编辑
另一件事是为内核创建更高的工作负载,因此无法识别存储延迟。
旁注:请记住,您的核心有几个执行单元。每种运算类型都可以选择一个或多个-整数,浮点数,移位等。这就是说,一个内核每步执行的命令数不能超过一个。特别地,每个执行单元一个操作。您可以保留测试数据的数据大小,并用它做更多的工作-富有创意=)仅用整数运算填充队列,将为您带来多线程的优势。如果您可以在代码中进行变化,何时何地执行不同的操作,那么这样做也会对加速产生影响。如果您希望在多线程上看到不错的加速效果,请避免使用它。
为了避免任何优化,您应该使用随机测试数据。因此编译器和处理器本身都无法预测操作的结果。
还要避免像if和while这样做分支。处理器必须预测和执行的每个决定都会减慢您的速度并改变结果。使用分支预测,您将永远无法获得确定性的结果。稍后在“真实”程序中,成为我的客人,做您想做的事。但是,当您想探索多线程世界时,这可能会导致您得出错误的结论。
BTW
请delete在每次new使用时使用A,以避免内存泄漏。甚至更好,请避免使用普通的指针new和delete。您应该使用RAII。我建议使用std::array或std::vector简单的STL容器。这将为您节省大量的调试时间和麻烦。
| 归档时间: |
|
| 查看次数: |
138 次 |
| 最近记录: |