我知道C++ 中的"未定义行为"几乎可以让编译器做任何想做的事情.但是,我遇到了让我感到惊讶的崩溃,因为我认为代码足够安全.
在这种情况下,真正的问题仅发生在使用特定编译器的特定平台上,并且仅在启用了优化时才发生.
我尝试了几件事来重现问题并将其简化到最大程度.这是一个名为的函数的摘录Serialize,它将获取bool参数,并将字符串true或复制false到现有的目标缓冲区.
如果bool参数是未初始化的值,那么这个函数是否会在代码审查中,没有办法告诉它实际上可能会崩溃?
// Zero-filled global buffer of 16 characters
char destBuffer[16];
void Serialize(bool boolValue) {
// Determine which string to print based on boolValue
const char* whichString = boolValue ? "true" : "false";
// Compute the length of the string we selected
const size_t len = strlen(whichString);
// Copy string into destination buffer, which is zero-filled (thus already null-terminated)
memcpy(destBuffer, whichString, len);
}
Run Code Online (Sandbox Code Playgroud)
如果使用clang 5.0.0 +优化执行此代码,它将/可能崩溃.
boolValue ? "true" …
如果有两个线程访问全局变量,那么许多教程都说使变量volatile变为阻止编译器将变量缓存在寄存器中,从而无法正确更新.但是,访问共享变量的两个线程是通过互斥锁来调用保护的东西不是吗?但是在这种情况下,在线程锁定和释放互斥锁之间,代码处于一个关键部分,只有那个线程可以访问变量,在这种情况下变量不需要是volatile?
那么多线程程序中volatile的用途/目的是什么?
我编写了一个简单的多线程程序,如下所示:
static bool finished = false;
int func()
{
size_t i = 0;
while (!finished)
++i;
return i;
}
int main()
{
auto result=std::async(std::launch::async, func);
std::this_thread::sleep_for(std::chrono::seconds(1));
finished=true;
std::cout<<"result ="<<result.get();
std::cout<<"\nmain thread id="<<std::this_thread::get_id()<<std::endl;
}
Run Code Online (Sandbox Code Playgroud)
它通常表现在调试模式下在Visual Studio中或-O0在GC c和后打印出的结果1秒钟。但是它卡住了,在“ 释放”模式或中不打印任何内容-O1 -O2 -O3。
使用Java指令重新排序时,JVM会在编译时或运行时更改代码的执行顺序,这可能导致无关的语句无序执行.
所以我的问题是:
有人可以提供示例Java程序/片段,它可靠地显示指令重新排序问题,这也不是由其他同步问题(例如缓存/可见性或非原子r/w)引起的,就像我在这样的演示中尝试失败一样在我之前的问题中)
为了强调,我不是在寻找理论重新排序问题的例子.我正在寻找的是通过查看正在运行的程序的错误或意外结果来实际演示它们的方法.
除了一个错误的行为示例,只显示在一个简单程序的程序集中发生的实际重新排序也可能是好的.
java multithreading compiler-optimization instruction-reordering
我想将文本文件的内容读入C中的char数组中.必须保留新行.
我该如何做到这一点?我在网上找到了一些C++解决方案,但没有C解决方案.
编辑:我现在有以下代码:
void *loadfile(char *file, int *size)
{
FILE *fp;
long lSize;
char *buffer;
fp = fopen ( file , "rb" );
if( !fp ) perror(file),exit(1);
fseek( fp , 0L , SEEK_END);
lSize = ftell( fp );
rewind( fp );
/* allocate memory for entire content */
buffer = calloc( 1, lSize+1 );
if( !buffer ) fclose(fp),fputs("memory alloc fails",stderr),exit(1);
/* copy the file into the buffer */
if( 1!=fread( buffer , lSize, 1 , fp) )
fclose(fp),free(buffer),fputs("entire read fails",stderr),exit(1); …Run Code Online (Sandbox Code Playgroud) 我研究了Java内存模型并看到了重新排序问题.一个简单的例子:
boolean first = false;
boolean second = false;
void setValues() {
first = true;
second = true;
}
void checkValues() {
while(!second);
assert first;
}
Run Code Online (Sandbox Code Playgroud)
重新排序是非常不可预测和奇怪的.此外,它破坏了抽象.我认为处理器架构必须有充分的理由去做一些对程序员来说太不方便的事情. 这些原因是什么?
有很多关于如何处理重新排序的信息,但我找不到任何关于它为什么需要的信息.在任何地方,人们只会说"这是因为一些性能优势".例如,second之前存储的性能优势是first什么?
您能推荐一些关于此的文章,论文或书籍,或者自己解释一下吗?
java optimization multithreading cpu-architecture compiler-optimization
这个问题是对此的跟进/澄清:
MOV x86 指令是否实现了 C++11 memory_order_release 原子存储?
这表明MOV汇编指令足以在 x86 上执行获取-释放语义。我们不需要LOCK,围栏xchg等。但是,我很难理解这是如何工作的。
英特尔文档第 3A 卷第 8 章指出:
https://software.intel.com/sites/default/files/managed/7c/f1/253668-sdm-vol-3a.pdf
在单处理器(核心)系统中......
- 读取不会与其他读取重新排序。
- 写入不会与较旧的读取重新排序。
- 对内存的写入不会与其他写入重新排序,但以下情况除外:
但这是针对单核的。多核部分似乎没有提到如何强制执行负载:
在多处理器系统中,以下排序原则适用:
- 单个处理器使用与单处理器系统相同的排序原则。
- 所有处理器都以相同的顺序观察单个处理器的写入。
- 来自单个处理器的写入与来自其他处理器的写入无关。
- 记忆排序服从因果关系(记忆排序尊重传递可见性)。
- 除了执行存储的处理器之外的处理器以一致的顺序看到任何两个存储
- 锁定指令具有总顺序。
那么如何才能MOV单独促进获取释放呢?
我已经读过一些CPU重新排序指令,但这对于单线程程序来说不是问题(指令仍会在单线程程序中重新排序,但看起来好像指令是按顺序执行的),这只是一个问题用于多线程程序.
为了解决指令重新排序的问题,我们可以在代码中的适当位置插入内存屏障.
但x86 CPU是否重新排序指令?如果没有,那么就没有必要使用内存屏障了吧?
x86 assembly multithreading cpu-architecture memory-barriers
我已经阅读了很多关于内存排序的文章,并且所有这些文章都只说CPU重新加载和存储.
CPU(我对x86 CPU特别感兴趣)是否仅重新排序加载和存储,并且不重新排序它具有的其余指令?
c++ ×5
x86 ×4
atomic ×2
c ×2
concurrency ×2
java ×2
abi ×1
assembly ×1
data-race ×1
file-io ×1
llvm ×1
llvm-codegen ×1
memory-model ×1
optimization ×1
stdatomic ×1
text ×1
volatile ×1