По непонятным причинам я был уверен, что static volatile int является атомарным счетчиком. Вроде бы достаточно логично — оптимизация запрещена, чтение и запись должны быть сразу в оперативную память, работать все должно быстро. Как оказалось нет.
Одно из выступлений с cppcon, где один из разработчиков clang доступно объясняет, что вообще никто не знает что делает volatile:
Пришлось разобраться и проверить.
#include <stdio.h> #include <thread> #include <atomic> std::atomic<int> acnt; static volatile int vcnt; const int ub_cnt = 0; int cnt; int f(int &lcnt, const int &local_const_cnt) { for(int i = 0; i < 100000; i++) { ++cnt; ++acnt; ++vcnt; ++lcnt; ++(*const_cast<int*>(&ub_cnt)); // this will crash the program with -O0 ++(*const_cast<int*>(&local_const_cnt)); } return 0; } int main(void) { int lcnt = 0; const int local_const_cnt = 0; std::thread* thr[1000]; for(int i = 0; i < 1000; i++) thr[i] = new std::thread(f, std::ref(lcnt), std::ref(local_const_cnt)); for (int i = 0; i < 1000; i++) thr[i]->join(); printf("The atomic counter is %u\n", acnt.load()); printf("The non-atomic counter is %u\n", cnt); printf("Volatile counter is %u\n", vcnt); printf("Local counter is %u\n", lcnt); printf("Local const counter is %u\n", local_const_cnt); printf("UB counter is %u\n", ub_cnt); // next line will crash the program because of undefined behaviour on any optimization level // printf("UB counter force load is %u\n", *const_cast<const volatile int*>(&ub_cnt)); }
Достаточно смешные результаты, плавающие от оптимизации (на -O0 ub_cnt я убрал):
vlad@vtyulb-thinkpad ~ % g++ main.cpp -lpthread -O0 vlad@vtyulb-thinkpad ~ % ./a.out The atomic counter is 100000000 The non-atomic counter is 993844 Volatile counter is 912750 Local counter is 1815379 Local const counter is 0 UB counter is 0 ./a.out 36,80s user 0,08s system 766% cpu 4,812 total vlad@vtyulb-thinkpad ~ % ./a.out The atomic counter is 100000000 The non-atomic counter is 1073648 Volatile counter is 938156 Local counter is 2031276 Local const counter is 0 UB counter is 0 ./a.out 37,19s user 0,19s system 757% cpu 4,933 total vlad@vtyulb-thinkpad ~ % g++ main.cpp -lpthread -O1 vlad@vtyulb-thinkpad ~ % ./a.out The atomic counter is 100000000 The non-atomic counter is 27314903 Volatile counter is 1159589 Local counter is 36943434 Local const counter is 0 UB counter is 0 ./a.out 36,77s user 0,14s system 764% cpu 4,829 total vlad@vtyulb-thinkpad ~ % ./a.out The atomic counter is 100000000 The non-atomic counter is 27600981 Volatile counter is 769927 Local counter is 35498305 Local const counter is 0 UB counter is 0 ./a.out 38,26s user 0,08s system 763% cpu 5,019 total vlad@vtyulb-thinkpad ~ % g++ main.cpp -lpthread -O2 vlad@vtyulb-thinkpad ~ % ./a.out The atomic counter is 100000000 The non-atomic counter is 24814898 Volatile counter is 857348 Local counter is 52322802 Local const counter is 0 UB counter is 0 ./a.out 41,11s user 0,10s system 765% cpu 5,385 total vlad@vtyulb-thinkpad ~ % ./a.out The atomic counter is 100000000 The non-atomic counter is 24785663 Volatile counter is 850257 Local counter is 51293639 Local const counter is 0 UB counter is 0 ./a.out 40,76s user 0,13s system 767% cpu 5,329 total vlad@vtyulb-thinkpad ~ % g++ main.cpp -lpthread -O3 vlad@vtyulb-thinkpad ~ % ./a.out The atomic counter is 100000000 The non-atomic counter is 25486880 Volatile counter is 800002 Local counter is 55676774 Local const counter is 0 UB counter is 0 ./a.out 39,96s user 0,11s system 761% cpu 5,263 total vlad@vtyulb-thinkpad ~ % ./a.out The atomic counter is 100000000 The non-atomic counter is 25704519 Volatile counter is 686848 Local counter is 54817634 Local const counter is 0 UB counter is 0 ./a.out 39,46s user 0,15s system 757% cpu 5,229 total
Вообще, недавно прочитал почти весь блог одного товарища, начав с этой статьи http://scrutator.me/post/2015/04/05/cpu_memory_inter_multiprocessor.aspx
Это практически полностью перевернуло мое понимание многопоточного доступа к памяти, потому что раньше я считал барьеры памяти теоретической бесполезной фигней, а они оказывается действительно существуют: https://en.cppreference.com/w/cpp/atomic/atomic_thread_fence