在此文章如何避免错误共享,剪断对准下面的代码是提出:
// C++ (using C++0x alignment syntax)
template<typename T>
struct cache_line_storage {
[[ align(CACHE_LINE_SIZE) ]] T data;
char pad[ CACHE_LINE_SIZE > sizeof(T)
? CACHE_LINE_SIZE - sizeof(T)
: 1 ];
};
Run Code Online (Sandbox Code Playgroud)
第4行是什么意思?我以前从未见过这种双括号语法.
考虑以下情况
// Global
int x = 0; // not atomic
// Thread 1
x = 1;
// Thread 2
if (false)
x = 2;
Run Code Online (Sandbox Code Playgroud)
根据标准,这是否构成数据竞争?[intro.races] 说:
如果两个表达式求值之一修改内存位置 (4.4),而另一个表达式求值读取或修改同一内存位置,则两个表达式求值会发生冲突。
如果程序的执行包含两个潜在并发冲突的操作,并且至少其中一个操作不是原子操作,并且两者都发生在另一个操作之前,则该程序的执行将包含数据争用,除了下面描述的信号处理程序的特殊情况之外。任何此类数据竞争都会导致未定义的行为。
从语言律师的角度来看是否安全,因为程序永远不能执行“表达式求值” x = 2;?
从技术角度来看,如果某个奇怪、愚蠢的编译器决定对该写入执行推测执行,并在检查实际情况后将其回滚,该怎么办?
激发这个问题的原因是(至少在标准 11 中),允许以下程序的结果完全取决于重新排序/推测执行:
// Thread 1:
r1 = y.load(std::memory_order_relaxed);
if (r1 == 42) x.store(r1, std::memory_order_relaxed);
// Thread 2:
r2 = x.load(std::memory_order_relaxed);
if (r2 == 42) y.store(42, std::memory_order_relaxed);
// This is allowed to result in r1==r2==42 in c++11
Run Code Online (Sandbox Code Playgroud)
在 C++20 的最终工作草案(以及下面链接的最新公开可用草案)中,关于如何允许对象替换其他对象,[basic.life] 的措辞已经改变,以便指针、引用和对象的name 自动引用新对象。为此,引入了“透明可替换”的关系。但是,我不确定我是否正确理解了这一点。考虑这个例子:
struct X {int a = 3; float b;};
X x;
new(&x.a) int(5);
x.a == 5 // true without std::launder?
Run Code Online (Sandbox Code Playgroud)
在 C++17 中这当然是正确的,因为旧的 int 既不是 const 对象,也不是具有 const 非静态成员的类。
然而,现在新的透明可替换关系可能不再允许这样做了。在考虑新旧 int 对象的关系时,满足条件(8.1)到(8.4),但条件(8.5)呢?
o1 (旧的 int)和 o2 (新的 int)都是完整的对象(旧的 int 肯定是一个子对象,所以这部分是假的)或者 o1 和 o2 分别是对象 p1 和 p2 的直接子对象,而 p1可以透明地替换为 p2。
new int 是一个完整的对象,因为它是“自己”构造的(我们只放置了一个 new int 而不是一个新的 X)?
或者是否可以认为由于[intro.object]\2的措辞,新 int 是 x 的子对象,就像旧 int 一样,(我高度怀疑这是预期的解释,tbh),因此 x 满足p1 和 p2 …
我偶然发现了一个reddit 线程,用户在其中发现了 C++ 标准的一个有趣细节。该线程没有产生太多建设性的讨论,因此我将在这里复述我对问题的理解:
memcpy符合标准的方式重新实现reinterpret_cast<char*>(&foo)这一点,这是严格别名限制的允许例外,其中允许重新解释 aschar访问对象的“对象表示”。static_cast<cv T*>(static_cast<cv void*>(v)),所以reinterpret_cast在这种情况下等价于 static_cast'ing first tovoid *然后 to char *。“指向 cv1 void 的指针”类型的纯右值可以转换为“指向 cv2 T 的指针”类型的纯右值,其中 T 是对象类型,而 cv2 与 cv1 具有相同的 cv 限定,或比 cv1 更高的 cv 限定。[...]如果原始指针值指向对象 a,并且存在 T 类型的对象 b(忽略 cv 限定)与 a 的指针可相互转换,则结果是指向 b 的指针。[...] [强调我的]
现在考虑以下联合类:
union Foo{
char c;
int i;
}; …Run Code Online (Sandbox Code Playgroud) 我已经构建了我的问题的以下最小示例:
#include <iostream>
struct Foo {
Foo() {
std::cout << "default" << std::endl;
}
Foo(Foo& f2) {
std::cout << "non-const" << std::endl;
}
Foo(const Foo& f2) {
std::cout << "const" << std::endl;
}
};
int main() {
std::pair<Foo, int> foop0(Foo(), 1);
std::cout << std::endl;
std::pair<const Foo, int>foop1(foop0);
}
Run Code Online (Sandbox Code Playgroud)
在我的 Ubuntu 机器上 g++ (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0 将打印出以下内容:
$ g++ -std=c++14 test.cpp -o test && ./test
default
const
const
Run Code Online (Sandbox Code Playgroud)
但是,我的 Mac 上的 Apple clang(版本 11.0.3 (clang-1103.0.32.62) Target: x86_64-apple-darwin19.4.0)将打印:
$ g++ -std=c++14 test.cpp …Run Code Online (Sandbox Code Playgroud) The following example of usage of placement-new was provided by an earlier version of the cppreference page:
char* ptr = new char[sizeof(T)]; // allocate memory
T* tptr = new(ptr) T; // construct in allocated storage ("place")
tptr->~T(); // destruct
delete[] ptr; // deallocate memory
Run Code Online (Sandbox Code Playgroud)
Inspired by this comment on an older SO thread I have come to the conclusion that this might be UB. However, in the talk page of that cppreference article there is a small discussion as …
https://www.tensorflow.org/versions/r0.12/api_docs/python/framework/tensor_types中列出的tf.float16和tf.bfloat16有什么区别?
另外,"量化整数"是什么意思?
考虑以下代码:
class Cat {
var name = "Tom"
}
class Globals {
var cat = Cat()
}
let glob = Globals()
func one () {
Task {glob.cat.name="Max"} // Expected Warning about some nonSendable moving into a different concurrency domain
}
Run Code Online (Sandbox Code Playgroud)
通常,-warn-concurrency启用后,Swift/Xcode 会警告不可发送的内容跨并发域。
根据我的理解,传递给的闭包Task必须始终是一个@Sendable闭包。闭包@Sendable只能捕获 Sendables。但是,Globals 不是可发送类型。我预计会出现类似以下的警告
@Sendable在闭包中捕获具有不可发送类型“Globals”的“glob”
不会发出此类警告。
cppreference 的 std::allocator 示例包含以下代码(为简单起见缩短):
// default allocator for ints
std::allocator<int> alloc1;
using traits_t1 = std::allocator_traits<decltype(alloc1)>; // The matching trait
p1 = traits_t1::allocate(alloc1, 1);
traits_t1::construct(alloc1, p1, 7); // construct the int
std::cout << *p1 << '\n';
Run Code Online (Sandbox Code Playgroud)
就分配器而言,相当简单。然而,标准保证中的哪些措辞p1实际上指向新对象呢?
根据std::allocate和[allocator.members]上的 cppreference 文档,默认分配器的函数allocate()
在存储中创建 T[n] 类型的数组并开始其生存期,但不开始其任何元素的生存期。
并返回
[指针] 指向 n 个 T 类型对象的数组的第一个元素,该数组的元素尚未构造。
Afaik,数组创建措辞已添加到标准中,以便指针上的指针算术有效。无论如何,这意味着返回的指针指向 的第一个元素,T[]并且该第一个元素的生命周期尚未开始。
construct()然后在此位置创建一个对象,但是,它不会返回指向该对象的指针。我们拥有的唯一指针仍然是返回的指针allocate。
通常,当一个对象被放置在过期对象的位置时,它可以在[basic.life]p8中规定的条件下“透明地替换”旧对象:(强调我的)
如果在一个对象的生命周期结束之后,在该对象所占用的存储被重用或释放之前,在原对象所占用的存储位置上创建了一个新对象,一个指向原对象的指针[... ] 将自动引用新对象,并且一旦新对象的生命周期开始[...]
该数组元素的生命周期从未开始,因此它不可能结束,因此这不适用。那么如何才能保证对新构造的对象的访问是明确定义的呢?每次通话后都std::launder应该使用吗construct …
语言文档指出:
&mut T是Send当且仅当T是Send
有人可以提供一个代码示例来说明这实际上是相关的吗?
thread::spawn要求其闭包在生命周期内借用'static,因此线程闭包只能借用静态变量,但安全 Rust 不允许static mut。因此,无论如何都不应该捕获线程闭包中的任何内容&mut。
&muta的能力在哪些用例中Send具有相关性?
当尝试将引用重新分配到闭包内部其他位置时,我注意到一个我无法解释的奇怪行为,如以下最小示例所示:
fn main() {
let mut foo: i32 = 5;
let mut foo2: i32 = 6;
let mut borrower = &mut foo; // compiles OK without mut here and below
let mut c = || {
borrower = &mut foo2; // compiles OK without mut here and above
};
}
Run Code Online (Sandbox Code Playgroud)
仅当引用为:时才会产生以下错误&mut:
error[E0521]: borrowed data escapes outside of closure
--> src/main.rs:25:9
|
23 | let mut borrower = &mut foo;
| ------------ `borrower` declared here, outside of the closure …Run Code Online (Sandbox Code Playgroud) 考虑以下两个代码片段:
fn main() {
let mut foo = 1;
let mut borrower = &mut foo;
let mut borrower2 = &mut foo; // error: cannot borrow `foo` as mutable more than once at a time
*borrower2 = 2;
*borrower = 3;
println!("{}", foo);
}
Run Code Online (Sandbox Code Playgroud)
这不会像预期的那样编译,因为不能对同一变量有两个可变引用。但是,如果我按如下所示替换有问题的行,它就会起作用:
fn main() {
let mut foo = 1;
let mut borrower = &mut foo;
let rr = &mut borrower;
let borrower2 = &mut (**rr);
*borrower2 = 2; // I can use both of these to mutate …Run Code Online (Sandbox Code Playgroud) c++ ×7
rust ×3
allocator ×1
brackets ×1
c++20 ×1
closures ×1
compilation ×1
concurrency ×1
memory-model ×1
swift ×1
syntax ×1
tensorflow ×1