小编Dan*_*ica的帖子

在C++ 11和OpenMP中对非原子内存位置的原子访问?

与C++ 11相比,OpenMP从内存操作的角度来看是原子性的,而不是变量.这允许例如对在编译时存储在具有未知大小的向量中的整数使用原子读/写:

std::vector<int> v;

// non-atomic access (e.g., in a sequential region):
v.resize(n);
...
v.push_back(i);
...

// atomic access in a multi-threaded region:
#pragma omp atomic write // seq_cst
v[k] = ...;
#pragma omp atomic read // seq_cst
... = v[k];
Run Code Online (Sandbox Code Playgroud)

在C++ 11中,这是不可能实现的.我们可以通过放松内存模型来将原子变量作为非原子变量进行访问,但是我们无法调整原子元素的向量.

我理解为什么C++不允许通过原子内存操作访问非原子变量.但我想知道,为什么这些原因也不适用于OpenMP.

例如,在N4013中,据说"没有合理的方法将原子操作完全可移植地应用于未声明为原子的数据." OpenMP如何能够保证这种可移植性和C++不是这样?

c++ atomic openmp nonatomic

6
推荐指数
1
解决办法
287
查看次数

在C++中转发引用和转换构造函数

假设我想创建一个函数,通过引用获取左值和右值字符串参数,将它们转换为大写,并将它们打印到标准输出:

void upper_print(std::string& s);
void upper_print(std::string&& s);
Run Code Online (Sandbox Code Playgroud)

这工作正常如下:

std::string s("Hello world");
upper_print(s);
upper_print(std::string("Hello world"));
upper_print("Hello world"); // converting ctor used
Run Code Online (Sandbox Code Playgroud)

但是,为避免冗余,我想使用转发引用:

template <typename T> upper_print(T&& s);
Run Code Online (Sandbox Code Playgroud)

不幸的是,我无法upper_print使用字符串文字参数调用:

std::string s("Hello world"); // OK
upper_print(s); // OK
upper_print(std::string("Hello world")); // OK
upper_print("Hello world"); // ERROR
Run Code Online (Sandbox Code Playgroud)

我知道可以限制std::string对象的参数,例如,使用std::enable_ifstatic_assert.但这并没有帮助.

在这个意义上,是否有任何选项可以结合转发引用和转换构造函数的功能?

c++ implicit-conversion forwarding-reference

6
推荐指数
1
解决办法
210
查看次数

为什么std :: sort构造对象?

我创建了以下类来理解以下行为std::sort:

class X {
public:
  X(int i) : i_(i) { }
  X(X&& rhs) noexcept : i_(std::move(rhs.i_)) { mc_++; }
  X& operator=(X&& rhs) noexcept {
    i_ = std::move(rhs.i_); ao_++; return *this;
  }
  void swap(X& rhs) noexcept { std::swap(i_, rhs.i_); sw_++; } 
  friend bool operator<(const X& lhs, const X& rhs) {
    return lhs.i_ < rhs.i_;
  }
  static void reset() { mc_ = ao_ = sw_ = 0; }
private:
  int i_;
  static size_t mc_, ao_, sw_; // function-call counters …
Run Code Online (Sandbox Code Playgroud)

c++ sorting swap move-constructor move-assignment-operator

6
推荐指数
1
解决办法
192
查看次数

如果预取了无效地址会怎样?

简单的MWE:

int* ptr = (int*)malloc(64 * sizeof(int));
_mm_prefetch((const char*)(ptr + 64), _MM_HINT_0);
Run Code Online (Sandbox Code Playgroud)
  1. 这是定义的还是未定义的行为?
  2. 这会引发信号并中止程序运行吗?

我问,因为我可以在编译器生成的代码中看到这样的预取,其中在循环预取内部完成而不检查地址(存储在rbx)中:

400e73:       49 83 c5 40             add    r13,0x40
400e77:       62 f1 f9 08 28 03       vmovapd zmm0,ZMMWORD PTR [rbx]
400e7d:       4d 3b ec                cmp    r13,r12
400e80:       62 d1 f9 08 eb 4d ff    vporq  zmm1,zmm0,ZMMWORD PTR [r13-0x40]
400e87:       90                      nop
400e88:       62 d1 78 08 29 4d ff    vmovaps ZMMWORD PTR [r13-0x40],zmm1
400e8f:       72 03                   jb     400e94 <main+0x244>
400e91:       49 89 c5                mov    r13,rax …
Run Code Online (Sandbox Code Playgroud)

c assembly caching prefetch disassembly

5
推荐指数
1
解决办法
325
查看次数

通过emplace_back实现push_back

假设我们想要设计一个C类似的容器std::vector.push_back通过调用实现是一个好主意emplace_back,例如:

template <typename T>
class C {
  public:
    ...
    template <typename Args...>
    void emplace_back(Args&&... args) { 
      ... // uses T(std::forward<Args>(args)...) internally
    }
    void push_back(T value) {
      emplace_back(std::move(value));
    }
    ...
};
Run Code Online (Sandbox Code Playgroud)

或者,或者:

    template <typename U>
    void push_back(U&& value) {
      emplace_back(std::forward(value));
    }
Run Code Online (Sandbox Code Playgroud)

c++ push move forward emplace

5
推荐指数
0
解决办法
396
查看次数

究竟什么是引用/指针的失效?

我找不到标准中指针/引用失效的任何定义.我问,因为我发现C++ 11禁止字符串的写时复制(COW).据我所知,如果应用了COW,那么在以下命令之后p仍然是一个有效的指针和r一个有效的引用:

std::string s("abc");
std::string s2(s);
char * p = &(s2[0]);
char & r = s2[0];
s2[1] = "B";
Run Code Online (Sandbox Code Playgroud)

只是他们不再指向/引用第一个字符s2,而只是指向第一个字符s.

在C++ 11标准中,据说非常量std::basic_string::operator[]可能不会使字符串元素的指针/引用(以及迭代器)无效.

哪些规则说上面显示的例子实际上会失效p,r如果实施了COW?

c++ pointers reference invalidation language-lawyer

5
推荐指数
1
解决办法
217
查看次数

为什么std :: launder是一个constexpr函数?

我想知道为什么std::launder是一个constexpr功能.是否有任何用例可以在编译时使用?

c++ pointers constexpr

5
推荐指数
1
解决办法
193
查看次数

仅当循环中更新的变量是本地变量时,才会优化计算

对于以下函数,带有优化的代码被向量化,并且计算在寄存器中执行(返回值返回eax).生成的机器代码例如是:https://godbolt.org/z/VQEBV4.

int sum(int *arr, int n) {
  int ret = 0;
  for (int i = 0; i < n; i++)
    ret += arr[i];
  return ret;
}
Run Code Online (Sandbox Code Playgroud)

但是,如果我创建ret变量global(或类型的参数int&),则不使用向量化,并且编译器将ret每次迭代中更新的内容存储到内存中.机器代码:https://godbolt.org/z/NAmX4t.

int ret = 0;

int sum(int *arr, int n) {
  for (int i = 0; i < n; i++)
    ret += arr[i];
  return ret;
}
Run Code Online (Sandbox Code Playgroud)

我不明白为什么在后一种情况下会阻止优化(寄存器中的矢量化/计算).没有线程,即使增量不是原子地执行的.此外,这种行为似乎在编译器(GCC,Clang,Intel)之间是一致的,所以我认为必须有一些原因.

c++ vectorization compiler-optimization

5
推荐指数
1
解决办法
180
查看次数

x86_64 和 ARM64 上对齐与未对齐内存访问的不同运行时

我创建了一个简单的演示来展示未对齐的内存存储/加载在 x86_64 和 ARM64 架构上通常不是原子的。该演示由一个 C++ 程序组成,该程序创建两个线程 \xe2\x80\x94,第一个线程 10 亿次调用名为 的函数store,第二个线程对名为 的函数执行相同的操作load。该程序的源代码在这里:

\n
#include <cstdint>\n#include <cstdlib>\n#include <iostream>\n#include <thread>\n\nextern "C" void store(void*);\nextern "C" uint16_t load(void*);\n\nalignas(64) char buf[65];\nchar* ptr;\n\nstatic long n = 1\'000\'000\'000L;\n\nvoid f1()\n{\n  for (long i = 0; i < n; i++)\n    store(ptr);\n}\n\nvoid f2()\n{\n  long v0x0000 = 0;\n  long v0x0101 = 0;\n  long v0x0100 = 0;\n  long v0x0001 = 0;\n  long other = 0;\n\n  for (long i = 0; i < n; i++)\n  {\n    uint16_t …
Run Code Online (Sandbox Code Playgroud)

performance x86-64 cpu-architecture memory-alignment arm64

5
推荐指数
0
解决办法
395
查看次数

C++ 中的内部实现 std::find

我注意到内部实现的 std::find 中的一些东西让我感到困惑;他们为什么这么做?

\n

std::find(begin,end,...)假设这一行,那么在文件的内部实现中stl_algobase.h,Line:2064是:

\n
\xc2\xa0 \xc2\xa0 \xc2\xa0 typename iterator_traits<_RandomAccessIterator>::difference_type\n\xc2\xa0 \xc2\xa0 __trip_count = (__last - __first) >> 2;\n
Run Code Online (Sandbox Code Playgroud)\n

这里发生了什么?为什么他们一起做减法然后使用移位运算符?我不明白他们为什么这样做?(抱歉,如果这是初学者的问题。)

\n

c++ libstdc++ c++17

5
推荐指数
1
解决办法
180
查看次数