- mongodb - 在 MongoDB mapreduce 中,如何展平值对象?
- javascript - 对象传播与 Object.assign
- html - 输入类型 ="submit"Vs 按钮标签它们可以互换吗?
- sql - 使用 MongoDB 而不是 MS SQL Server 的优缺点
我用 C++ 进行了一些科学计算,并尝试利用 OpenMP 对某些循环进行并行化。
到目前为止,这运作良好,例如在具有 8 个线程的 Intel i7-4770 上。
设置
我们有一个小型工作站,它由一个主板上的两个 Intel CPU (E5-2680v2) 组成。
只要代码在 1 个 CPU 上运行,我喜欢多少线程就可以运行。但是一旦我使用第二个 CPU,我就会不时观察到错误的结果(大约每 50-100 次我运行代码一次)。
即使我只使用 2 个线程并将它们分配给两个不同的 CPU,也会发生这种情况。
由于我们有 5 个这样的工作站(都是相同的),我在每个工作站上运行了代码,都显示了这个问题。
工作站在 OpenSuse 13.1 内核 3.11.10-7 上运行。
该问题存在于 g++ 4.8.1 和 4.9.0 以及 Intel 的 icc 13.1.3.192(尽管该问题在 icc 中并不经常发生,但它仍然存在)。
症状
症状可描述如下:
std::complex<double>* mFourierValues;
mFourierValues[idx] = newValue;
mFourierValues[idx] == newValue
,此检查有时会失败(尽管并非每次结果都以错误告终)。 std::vector
中时(使用适当的
#pragma omp critical
),
complex<double> mAllElements[tensorIdx][kappa1][kappa2][kappa3]
. IE。我有 3 个 3 级张量( tensorIdx
)。每个张量代表一个 3 维数组,索引为 kappa1
, kappa2
和 kappa3
. kappa1
循环是并行化的(并且是最外层的)。他们位于 DoComputation()
. main()
,我打电话DoComputation()
一次得到一些引用值,然后我多次调用它并比较结果。它们应该完全匹配,但有时它们不匹配。 tensorIdx
上的循环是最里面的一个)(我知道,这远非最佳。)
nops()
的原因调用。没有它们,代码运行得更快,但到目前为止还没有显示出问题。
CalcElementIdx()
,再次,并认为它是正确的(每个元素都被访问一次)。我还运行了 valgrind 的 memcheck、helgrind 和 drd(使用正确的重新编译的 libgomp),并且所有三个都没有出现错误。
41 Is exactly 0
42 Is exactly 0
43 Is exactly 0
44 Is exactly 0
45 348496
46 Is exactly 0
47 Is exactly 0
48 Is exactly 0
49 Is exactly 0
g++ main.cc -O3 -Wall -Wextra -fopenmp
icc main.cc -O3 -Wall -Wextra -openmp
// File: main.cc
#include <cmath>
#include <iostream>
#include <fstream>
#include <complex>
#include <cassert>
#include <iomanip>
#include <omp.h>
using namespace std;
// If defined: We add some nops in certain places, to get the timing right.
// Without them, I haven't observed the bug.
#define ENABLE_NOPS
// The size of each of the 3 tensors is: GRID_SIZE x GRID_SIZE x GRID_SIZE
static const int GRID_SIZE = 60;
//=============================================
// Produces several nops. Used to get correct "timings".
//----
template<int N> __attribute__((always_inline)) inline void nop()
{
nop<N-1>();
asm("nop;");
}
//----
template<> inline void nop<0>() { }
//----
__attribute__((always_inline)) inline void nops()
{
nop<500>(); nop<500>(); nop<500>(); nop<500>(); nop<500>(); nop<500>(); nop<500>(); nop<500>(); nop<500>();
}
//=============================================
/*
Memory layout: We have 3 rank-3-tensors, i.e. 3 arrays of dimension 3.
The layout looks like this: complex<double> allElements[tensorIdx][kappa1][kappa2][kappa3];
The kappas represent the indices into a certain tensor, and are all in the interval [0; GRID_SIZE-1].
*/
class MemoryManagerFFTW
{
public:
//---------- Constructor ----------
MemoryManagerFFTW()
{
mAllElements = new complex<double>[GetTotalNumElements()];
}
//---------- Destructor ----------
~MemoryManagerFFTW()
{
delete[] mAllElements;
}
//---------- SetElement ----------
void SetElement(int tensorIdx, int kappa1, int kappa2, int kappa3, const complex<double>& newVal)
{
// Out-of-bounds error checks are done in this function.
const size_t idx = CalcElementIdx(tensorIdx, kappa1, kappa2, kappa3);
// These nops here are important to reproduce the bug.
#if defined(ENABLE_NOPS)
nops();
nops();
#endif
// A flush makes the bug appear more often.
// #pragma omp flush
mAllElements[idx] = newVal;
// This was never false, although the same check is false in DoComputation() from time to time.
assert(newVal == mAllElements[idx]);
}
//---------- GetElement ----------
const complex<double>& GetElement(int tensorIdx, int kappa1, int kappa2, int kappa3)const
{
const size_t idx = CalcElementIdx(tensorIdx, kappa1, kappa2, kappa3);
return mAllElements[idx];
}
//---------- CalcElementIdx ----------
size_t CalcElementIdx(int tensorIdx, int kappa1, int kappa2, int kappa3)const
{
// We have 3 tensors (index by "tensorIdx"). Each tensor is of rank 3. In memory, they are placed behind each other.
// tensorStartIdx is the index of the first element in the tensor.
const size_t tensorStartIdx = GetNumElementsPerTensor() * tensorIdx;
// Index of the element relative to the beginning of the tensor. A tensor is a 3dim. array of size GRID_SIZE x GRID_SIZE x GRID_SIZE
const size_t idxInTensor = kappa3 + GRID_SIZE * (kappa2 + GRID_SIZE * kappa1);
const size_t finalIdx = tensorStartIdx + idxInTensor;
assert(finalIdx < GetTotalNumElements());
return finalIdx;
}
//---------- GetNumElementsPerTensor & GetTotalNumElements ----------
size_t GetNumElementsPerTensor()const { return GRID_SIZE * GRID_SIZE * GRID_SIZE; }
size_t GetTotalNumElements()const { return NUM_TENSORS * GetNumElementsPerTensor(); }
public:
static const int NUM_TENSORS = 3; // The number of tensors.
complex<double>* mAllElements; // All tensors. An array [tensorIdx][kappa1][kappa2][kappa3]
};
//=============================================
void DoComputation(MemoryManagerFFTW& mSingleLayerManager)
{
// Parallize outer loop.
#pragma omp parallel for
for (int kappa1 = 0; kappa1 < GRID_SIZE; ++kappa1)
{
for (int kappa2 = 0; kappa2 < GRID_SIZE; ++kappa2)
{
for (int kappa3 = 0; kappa3 < GRID_SIZE; ++kappa3)
{
#ifdef ENABLE_NOPS
nop<50>();
#endif
const double k2 = kappa1*kappa1 + kappa2*kappa2 + kappa3*kappa3;
for (int j = 0; j < 3; ++j)
{
// Compute and set new result.
const complex<double> curElement = mSingleLayerManager.GetElement(j, kappa1, kappa2, kappa3);
const complex<double> newElement = exp(-k2) * k2 * curElement;
mSingleLayerManager.SetElement(j, kappa1, kappa2, kappa3, newElement);
// Check if the results has been set correctly. This is sometimes false, but _not_ always when the result is incorrect.
const complex<double> test = mSingleLayerManager.GetElement(j, kappa1, kappa2, kappa3);
if (test != newElement)
printf("Failure: (%g, %g) != (%g, %g)\n", test.real(), test.imag(), newElement.real(), newElement.imag());
}
}
}
}
}
//=============================================
int main()
{
cout << "Max num. threads: " << omp_get_max_threads() << endl;
// Call DoComputation() once to get a reference-array.
MemoryManagerFFTW reference;
for (size_t i = 0; i < reference.GetTotalNumElements(); ++i)
reference.mAllElements[i] = complex<double>((double)i, (double)i+0.5);
DoComputation(reference);
// Call DoComputation() several times, and each time compare the result to the reference.
const size_t NUM = 1000;
for (size_t curTry = 0; curTry < NUM; ++curTry)
{
MemoryManagerFFTW mSingleLayerManager;
for (size_t i = 0; i < mSingleLayerManager.GetTotalNumElements(); ++i)
mSingleLayerManager.mAllElements[i] = complex<double>((double)i, (double)i+0.5);
DoComputation(mSingleLayerManager);
// Get the max. difference. This *should* be 0, but isn't from time to time.
double maxDiff = -1;
for (size_t i = 0; i < mSingleLayerManager.GetTotalNumElements(); ++i)
{
const complex<double> curDiff = mSingleLayerManager.mAllElements[i] - reference.mAllElements[i];
maxDiff = max(maxDiff, max(curDiff.real(), curDiff.imag()));
}
if (maxDiff != 0)
cout << curTry << "\t" << maxDiff << endl;
else
cout << curTry << "\t" << "Is exactly 0" << endl;
}
return 0;
}
最佳答案
该问题是由于 Linux Kernel 内核 3.11.10-7 中的一个错误造成的。 The bug may be due to how the kernel handles invalidating the TLB cache正如赫里斯托·伊利耶夫所指出的那样。我猜测内核可能是问题所在,因为我读到 Linux Kernel 3.15 for NUMA systems 会有一些改进。所以我认为内核版本对 NUMA 系统很重要。
当 OP 将他的 NUMA 系统的 Linux 内核更新到 3.15.0-1 时,问题就消失了。
关于c++ - 2 插槽系统上的 OpenMP,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24219263/
#include using namespace std; class C{ private: int value; public: C(){ value = 0;
这个问题已经有答案了: What is the difference between char a[] = ?string?; and char *p = ?string?;? (8 个回答) 已关闭
关闭。此题需要details or clarity 。目前不接受答案。 想要改进这个问题吗?通过 editing this post 添加详细信息并澄清问题. 已关闭 7 年前。 此帖子已于 8 个月
除了调试之外,是否有任何针对 c、c++ 或 c# 的测试工具,其工作原理类似于将独立函数复制粘贴到某个文本框,然后在其他文本框中输入参数? 最佳答案 也许您会考虑单元测试。我推荐你谷歌测试和谷歌模拟
我想在第二台显示器中移动一个窗口 (HWND)。问题是我尝试了很多方法,例如将分辨率加倍或输入负值,但它永远无法将窗口放在我的第二台显示器上。 关于如何在 C/C++/c# 中执行此操作的任何线索 最
我正在寻找 C/C++/C## 中不同类型 DES 的现有实现。我的运行平台是Windows XP/Vista/7。 我正在尝试编写一个 C# 程序,它将使用 DES 算法进行加密和解密。我需要一些实
很难说出这里要问什么。这个问题模棱两可、含糊不清、不完整、过于宽泛或夸夸其谈,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开,visit the help center . 关闭 1
有没有办法强制将另一个 窗口置于顶部? 不是应用程序的窗口,而是另一个已经在系统上运行的窗口。 (Windows, C/C++/C#) 最佳答案 SetWindowPos(that_window_ha
假设您可以在 C/C++ 或 Csharp 之间做出选择,并且您打算在 Windows 和 Linux 服务器上运行同一服务器的多个实例,那么构建套接字服务器应用程序的最明智选择是什么? 最佳答案 如
你们能告诉我它们之间的区别吗? 顺便问一下,有什么叫C++库或C库的吗? 最佳答案 C++ 标准库 和 C 标准库 是 C++ 和 C 标准定义的库,提供给 C++ 和 C 程序使用。那是那些词的共同
下面的测试代码,我将输出信息放在注释中。我使用的是 gcc 4.8.5 和 Centos 7.2。 #include #include class C { public:
很难说出这里问的是什么。这个问题是含糊的、模糊的、不完整的、过于宽泛的或修辞性的,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开它,visit the help center 。 已关
我的客户将使用名为 annoucement 的结构/类与客户通信。我想我会用 C++ 编写服务器。会有很多不同的类继承annoucement。我的问题是通过网络将这些类发送给客户端 我想也许我应该使用
我在 C# 中有以下函数: public Matrix ConcatDescriptors(IList> descriptors) { int cols = descriptors[0].Co
我有一个项目要编写一个函数来对某些数据执行某些操作。我可以用 C/C++ 编写代码,但我不想与雇主共享该函数的代码。相反,我只想让他有权在他自己的代码中调用该函数。是否可以?我想到了这两种方法 - 在
我使用的是编写糟糕的第 3 方 (C/C++) Api。我从托管代码(C++/CLI)中使用它。有时会出现“访问冲突错误”。这使整个应用程序崩溃。我知道我无法处理这些错误[如果指针访问非法内存位置等,
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 我们不允许提问寻求书籍、工具、软件库等的推荐。您可以编辑问题,以便用事实和引用来回答。 关闭 7 年前。
已关闭。此问题不符合Stack Overflow guidelines 。目前不接受答案。 要求我们推荐或查找工具、库或最喜欢的场外资源的问题对于 Stack Overflow 来说是偏离主题的,因为
我有一些 C 代码,将使用 P/Invoke 从 C# 调用。我正在尝试为这个 C 函数定义一个 C# 等效项。 SomeData* DoSomething(); struct SomeData {
这个问题已经有答案了: Why are these constructs using pre and post-increment undefined behavior? (14 个回答) 已关闭 6
我是一名优秀的程序员,十分优秀!