std :: atomic <T> :: is_lock_free()がconstexprと同様に静的ではないのはなぜですか?


9

std :: atomic :: is_lock_free()がconstexprと同様に静的でないかどうか誰かに教えてもらえますか?それを非静的および/または非constexprとして持つことは私には意味がありません。


3
知っていis_always_lock_freeますか?
Mike van Dyke

3
「アラインメント」をそこに捨てます。
Max Langhof

@MaxLanghofすべてのインスタンスが同じように配置されるわけではないということですか?
curiousguy

1
マイク、いや、私は知らなかったが、このヒントをありがとう。それは私にとって本当に役に立ちます。しかし、私はなぜis_lock_free()とis_always_lock_freeの間に決定があるのか​​と自問しています。いずれにせよ、言語が未調整のアクセスを定義して未定義の動作をするように定義しているため、他の人がここで提案しているように、未調整のアトミックが原因である可能性はありません。
Bonita Montero

回答:


10

cppreferenceで説明したように

std :: atomic_flagを除くすべてのアトミック型は、ロックフリーのアトミックCPU命令を使用するのではなく、ミューテックスまたは他のロック操作を使用して実装できます。アトミックタイプはロックフリーになることもできます。たとえば、特定のアーキテクチャーでアラインされたメモリアクセスのみが自然にアトミックである場合、同じタイプのミスアラインされたオブジェクトはロックを使用する必要があります。

C ++標準では、ロックフリーアトミック操作もアドレスフリーであること、つまり共有メモリを使用するプロセス間の通信に適していることを推奨しています(ただし必須ではありません)。

他の複数の人がstd::is_always_lock_free述べたように、あなたが本当に探しているものかもしれません。


編集:明確にするために、C ++オブジェクトタイプには、インスタンスのアドレスを2の累乗の特定の倍数([basic.align])のみに制限するアライメント値があります。これらの位置合わせ値は、基本型の実装定義であり、型のサイズと同じである必要はありません。また、ハードウェアが実際にサポートできるものよりも厳しい場合もあります。

たとえば、x86は(ほとんど)非境界整列アクセスをサポートしています。ただし、alignof(double) == sizeof(double) == 8境界整列されていないアクセスには多くの欠点(速度、キャッシング、原子性など)があるため、ほとんどのコンパイラーはx86用です。しかし、たとえば#pragma pack(1) struct X { char a; double b; };alignas(1) double x;「アラインメントされていない」doubles を使用できます。したがって、cppreferenceが「アライメントされたメモリアクセス」について話すとき、それはおそらくハードウェアの型の自然なアライメントの観点からそうであり、そのアライメント要件(UBである)と矛盾する方法でC ++型を使用しません。

詳細は次のとおりです。x86でのアライメントされていないアクセスの成功の実際の影響は何ですか?

以下の@Peter Cordesによる洞察に満ちたコメントもご覧ください。


1
32ビットx86は、ABIをで見つける良い例ですalignof(double)==4。しかし、実行時にアライメントをチェックする代わりに、std::atomic<double>まだ持っていalignof() = 8ます。アトミックにアンダーアラインされるパックされた構造体を使用すると、ABIが壊れ、サポートされません。(32ビットx86のGCCは、8バイトのオブジェクトを自然に配置することを好みますが、構造体パッキングルールはそれをオーバーライドし、alignof(T)i386 System V などにのみ基づいています。G++にはatomic<int64_t>、構造体の内部がアトミックでない可能性があるバグがありましたGCC(C ++ではなくCの場合)にはまだこのバグがあります!)
Peter Cordes

2
ただし、C ++ 20の正しい実装でstd::atomic_ref<double>は、アンダーアラインをdouble完全に拒否するか、プラットフォーム上で実行時にアラインメントをチェックし、プレーンでdoubleありint64_t、自然にアラインされていないことが合法です。(atomic_ref<T>プレーンとして宣言されたオブジェクトを操作するため、追加の配置を行う機会Talignof(T)ない最小の配置しかありません。)
Peter Cordes

2
参照してくださいgcc.gnu.org/bugzilla/show_bug.cgi?id=62259今、固定のlibstdc ++のバグのため、およびgcc.gnu.org/bugzilla/show_bug.cgi?id=65146を A含め、まだ壊れたCのバグのために_Atomic int64_tcurrentでコンパイルしたときにの破損を示す純粋なISO C11テストケースgcc -m32。とにかく、私の要点は、実際のコンパイラーは整列されていないアトミックをサポートしておらず、実行時チェックを(まだ?)していないということです。そうし#pragma packない__attribute__((packed))と、非原子性につながるだけです。オブジェクトは、それがまだであることを報告しますlock_free
Peter Cordes

1
しかし、はい、の目的は、is_lock_free()することです許可の実装は、現在のものは実際に行う方法とは異なる動作します。HWがサポートするアトミック命令を使用するため、またはロックを使用するために、実際のアライメントに基づくランタイムチェックを使用します。
Peter Cordes

3

あなたは使うかもしれません std::is_always_lock_free

is_lock_free 実際のシステムに依存し、コンパイル時に決定することはできません。

関連する説明:

アトミックタイプはロックフリーになることもできます。たとえば、特定のアーキテクチャーで整列されたメモリアクセスのみが自然にアトミックである場合、同じタイプの整列されていないオブジェクトはロックを使用する必要があります。


1
std::numeric_limits<int>::maxアーキテクチャに依存しますが、静的であり、constexprです。答えは間違っていないと思いますが、私は推論の最初の部分は購入しません
idclev 463035818

1
いずれにしても、言語の非境界整列アクセスを未定義の動作に定義して、実行時のロックフリー性の評価が意味をなさないようにしないのですか?
Bonita Montero

1
言語が後者を未定義の動作と定義しているため、境界整列と非境界整列のアクセスを決定することは意味がありません。
Bonita Montero

@BonitaMontero「C ++オブジェクトの配置で整列されていない」という意味と「ハードウェアが好むもので整列されていない」という意味があります。それらは必ずしも同じではありませんが、実際には頻繁に同じです。あなたが示す例は、コンパイラが明らかに2つ同じであるという組み込みの仮定を持っているそのようなインスタンスの1つです。これis_lock_free、そのコンパイラでは意味がないことを意味ます。
Max Langhof

1
アラインメント要件がある場合、アトミックが適切なアラインメントを持っていることはかなり確実です。
Bonita Montero

1

Windows PCにVisual Studio 2019をインストールしましたが、このdevenvにもARMv8コンパイラがあります。ARMv8は非境界整列アクセスを許可しますが、比較とスワップ、ロックされた追加などは、境界整列が義務付けられています。また、ldpor stp(32ビットレジスタのロードペアまたはストアペア)を使用した純粋なロード/純粋なストアは、それらが自然にアライメントされている場合にのみアトミックであることが保証されます。

そこで、任意のアトミックポインターに対してis_lock_free()が何を返すかを確認する小さなプログラムを書きました。だからここにコードがあります:

#include <atomic>
#include <cstddef>

using namespace std;

bool isLockFreeAtomic( atomic<uint64_t> *a64 )
{
    return a64->is_lock_free();
}

そして、これはisLockFreeAtomicの逆アセンブリです

|?isLockFreeAtomic@@YA_NPAU?$atomic@_K@std@@@Z| PROC
    movs        r0,#1
    bx          lr
ENDP

これはreturns true別名1です。

この実装はalignof( atomic<int64_t> ) == 8、すべてatomic<int64_t>が正しく整列するように使用することを選択します。これにより、すべてのロードとストアで実行時のアライメントチェックが不要になります。

(編集者注:これは一般的です。ほとんどの実際のC ++実装はこのように機能します。これstd::is_always_lock_freeが非常に便利な理由です。通常、これはtrueである型に対してtrueであるためis_lock_free()です。)


1
はい、ほとんどの実装は与えることatomic<uint64_t>を選択するalignof() == 8ので、実行時にアライメントをチェックする必要はありません。この古いAPIには、そうしないという選択肢がありますが、現在のハードウェアでは、アラインメントを要求するだけの方が理にかなっています(そうでなければ、非原子性などのUB)。int64_t4バイトアライメントしかない可能性がある32ビットコードでも、atomic<int64_t>8バイトが必要です。別の回答
Peter Cordes

別の言葉に:場合は、コンパイラを選択したが、作るためにalignof、ハードウェアの「良い」アライメントと同じ基本的なタイプの値を、そして is_lock_free常になりますtrue(とそうなりますis_always_lock_free)。ここのコンパイラはまさにこれを行います。ただし、APIが存在するため、他のコンパイラはさまざまなことを実行できます。
Max Langhof

1
境界整列されていないアクセスには未定義の動作があると言語が言っている場合、すべてのアトミックが適切に整列されなければならないことはかなり確実です。そのため、実装は実行時チェックを行いません。
Bonita Montero

@BonitaMonteroはい、ただしalignof(std::atomic<double>) == 1、ハードウェアがdouble4または8バイト境界。コンパイラは、アライメントされていない場合にロックを使用する必要is_lock_freeがあります(オブジェクトインスタンスのメモリの場所に応じて、適切なブール値をから返します)。
Max Langhof
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.