ここに私の問題のいくつかの特徴があります:
私が現在実装している方法:
(プロセスがタイムスタンプを要求した場合)
while(true) { ... }
ループでは、値が「ロールオーバー」するまで、基になるクロックソースから値を取得し続けます(たとえば、(1)で取得した値から変更されます)時間ソースが単調である限り、それは一意性の制約を満たすはずです。予想通り、グローバルミューテックスの取得と解放(カーネルコンテキストの切り替えなど)に関連する実際のコストはまだあります。
上記のように問題に使用できる他のアプローチまたはアルゴリズムはありますか?
タイムスタンプを52ビットに圧縮してもよいとコメントで述べました。より小さなプロセスIDを交渉できると述べた人はほとんどいません。
マシン全体だけでなく、世界中でロックフリーの非常に高速なタイムスタンプを生成する1つの方法でこれらのアイデアを拡張しています。これは、プロセスが初期設定以外の状態を共有しないため、さまざまなマシンで簡単に実行できるためです。
次のように各タイムスタンプをエンコードします。
_ 8 bits -> shortPid, a generated "process id" during startup
52 bits -> shortTime, the compressed timestamp
4 bits -> localCounter, to resolve timestamp overlaps, and avoid waiting for the system clock
_
アルゴリズム:
shortPid
- sを生成するための中心的な「サービス」を用意します。擬似コードの例:_byte shortPid = 0;
byte getNewPid() {
lock {
shortPid++;
return shortPid;
}
}
_
shortPid
を取得します。_ulong processPidMasked = getNewPid() << (52 + 4); // get pid and move into first 8 bits of 64-bit ulong
_
ulong getCompressedTimestamp()
があるとすると、プロセスごとに一意のタイムスタンプを取得する方法は次のとおりです。_ulong localCounter = 0;
ulong lastCompressedTimestamp = 0;
ulong getUniqueTimestamp() {
while (1) {
ulong now = getCompressedTimestamp(); // assuming 52-bit timestamp
if (now != lastCompressedTimestamp) { // if we haven't generated any timestamp this tick
lastCompressedTimestamp = now;
localCounter = 0; // reset to time slot 0
return processPidMasked | (now << 4) | localCounter;
}
// we generated one or more unique stamps on this tick, see if there's any "free slots" left
if (localCounter < 15) { // if we have free slots for this tick
localCounter++;
return processPidMasked | (now << 4) | localCounter;
}
// worst case scenario: we exhausted our local counter for this tick:
// loop until we get to the next time slot
}
}
_
ノート:
さて、最初にロックを解除します。
ロックレスにするほうがおそらくはるかに効率的です。
それでも不十分な場合は、バッチ処理を使用してください。
各ローカルスレッドに最大n個の連続するタイムスタンプを蓄積します。備蓄が枯渇したら、共有されたグローバルソースから補充してください。
不利な点は、需要が不規則または少ない場合に、タイムスタンプが実際の時間よりも大幅に遅れて快適になることです。
共有グローバルソースと各スレッドの間にプロセスレベルの備蓄を追加する可能性があります。
ホールドアップを回避するために、しきい値を下回ったときに非同期で備蓄を非同期で補充する可能性があります。