herumi / emcjp

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Effective Modern C++勉強会

資料置き場

本家errata

第1回 2015/1/28

第2回 2015/2/25

第3回 2015/3/25

第4回 2015/4/22

第5回 2015/5/20

第6回 2015/6/17

第7回 2015/7/15

第8回 2015/8/19

第9回 2015/9/30

疑問やコメントなど(随時思い出したら書く)

memo.md

Item 39.

  • ポーリングのtips Intelにはbusy loopを検知してCPUパフォーマンスをあげて電力消費を減らすpause命令がある。

参考:Benefitting Power and Performance Sleep Loops

  • 条件変数のwaitは、イベントが本当に発生したらtrueを返すラムダ式と一緒に使うべき。 偽の起動(spurious wakeup)があるため。

参考:条件変数とspurious wakeup

Q. vector vtにemplace_backしている途中で例外が発生したらset_valueするまえに抜けるのでブロックする可能性がある?

A. scope_exitをthreadを生成するループの前に置くとよいだろう。

Item 40.

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の式

Item 41.

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の最後に上げられている参考文献へのリンク

About


Languages

Language:C++ 100.0%