- Item 7, 8 : @Talos208
- Item 9, 10の資料 : @uchan_nos
- Item 11の資料 : @keisukefukuda
- Item 12の資料 : @keisukefukuda
- Item 13, 14 : @pepshiso
- Item 15の資料 : @kariya_mitsuru
- Item 29, 30の資料 : @Linda_pp
- Item 31の資料 : @MitsutakaTakeda
- Item 32の資料 : @simizut22
- Item 33, 34の資料 : @herumi
- ポーリングのtips Intelにはbusy loopを検知してCPUパフォーマンスをあげて電力消費を減らすpause命令がある。
参考:Benefitting Power and Performance Sleep Loops
- 条件変数のwaitは、イベントが本当に発生したらtrueを返すラムダ式と一緒に使うべき。 偽の起動(spurious wakeup)があるため。
Q. vector vtにemplace_backしている途中で例外が発生したらset_valueするまえに抜けるのでブロックする可能性がある?
A. scope_exitをthreadを生成するループの前に置くとよいだろう。
Q. intとかの整数型はいつでもlockでatomicを実現するの?
A. アーキテクチャによって違う。Intel系でも32bit環境でint64_tだと結構複雑。
VCだと_InterlockedExchangeAdd64
, clangだと_atomic_fetch_add_8を呼ぶ。
gccだとinlineでlock cmpxchg8bとloopの組み合わせ.
.L2:
movl %eax, %ecx
movl %edx, %ebx
addl $1, %ecx
adcl $0, %ebx
movl %ebx, %ebp
movl %ecx, %ebx
movl %ebp, %ecx
lock cmpxchg8b (%esi)
jne .L2
volatileの細かい仕様は各コンパイラの処理系依存。 規格上CとC++でも違う。以下雑多なメモ。
void f(int *nv1, int *nv2, volatile int *v)
{
*nv1 = 1;
*nv2 = 2;
*v = 3;
*nv2 = 4;
*v = 5;
*nv1 = 6;
}
VC, clangでは1, 2, 3, 4, 5, 6全ての書き込み命令がその順序で生成された。
// VC2015
mov DWORD PTR [rcx], 1
mov DWORD PTR [rdx], 2
mov DWORD PTR [r8], 3
mov DWORD PTR [rdx], 4
mov DWORD PTR [r8], 5
mov DWORD PTR [rcx], 6
// clang 3.5
movl $1, (%rdi)
movl $2, (%rsi)
movl $3, (%rdx)
movl $4, (%rsi)
movl $5, (%rdx)
movl $6, (%rdi)
gcc-4.8では1, 2の書き込みは消えた。
movl $3, (%rdx)
movl $4, (%rsi)
movl $5, (%rdx)
movl $6, (%rdi)
- MSDN
- clang
- gcc volatileの最低限の要件は、シーケンスポイントにおいてvolatile変数より前の全てのアクセスが完了し、後続するアクセスは起こらないこと。 シーケンスポイント間のvolatileアクセスの順序入れ替えや結合は許可されている。 シーケンスポイントを越えてのその操作はできない。
非volatileへのアクセスはvolatileへのアクセスに関して順序づけられない。 volatile変数を非volatileメモリへの書き込みの順序づけのためのメモリバリアには使えない。
int *ptr = something;
volatile int vobj;
*ptr = something;
vobj = 1;
vobjへの書き込みが起こるまでに*ptrへの書き込みがあることは保証されない。
int *ptr = something;
volatile int vobj;
*ptr = something;
asm volatile ("" : : : "memory");
vobj = 1;
しないといけない。
void f(volatile int *v)
{
*v;
}
C++的には*v;をlvalueからrvalueへの変換をしない。脱参照された型は不完全かもしれない。 その変換がメモリアクセスを引き起こすかどうかは明記しない。 しかし、それだと大抵のプログラマが驚くのでg++ではvolatileオブジェクトの脱参照はCと同じに扱う。
movl (%rdi), %eax ; アクセス発生
ret
VCも同様に残す。 clangはCでbuildすると残す。C++では残さない。
clang -x c -S -Ofast t.c
f:
movl (%rdi), %eax
retq
clang -x c++ -S -Ofast t.c
f:
retq
cf. シーケンスポイント
&&
の左側, `||'の左側, コンマ演算子の左側, 関数の呼び出し, 条件演算子の最初のオペランド, 完全な初期化式の終わり, 式ステートメントの式,
if, switchないの制御式, while, doの制御式, forの3つの式, returnの式
Q. shared_ptrなどはconst&で渡す?
A. 生ポインタを渡すこともある。あとconst shared_ptrはconstと紛らわしいのであまり使わないかも。
#include <stdio.h>
#include <memory>
struct A {
A(int a) : a(a) { printf("cstr %d\n", a); }
~A() { printf("dstr %d\n", a); }
int a;
};
void f(const std::shared_ptr<A>& a)
{
a.get()->a = 5; // 書き換え可能
}
int main()
{
std::shared_ptr<A> a = std::make_shared<A>(3);
f(a);
}
* slideの最後に上げられている参考文献へのリンク
- Keynote: The Evolving Search for Effective C++ by Scott Meyers
- Item 42のpush_backとemplace_backの話もでている。
- Back to the Basics! Essentials of Modern C++ Style by Herb Sutter
- cstrのみ値渡しという手法の提案