コンピューティングクラスターで、コードの複数のインスタンス(2000インスタンス程度)を同時に実行しようとしています。それが機能する方法は、私がジョブを送信し、ノードがノードごとにいくつかのジョブで頻繁に開くときにクラスターがそれらを実行することです。これは、タイムシードを使用する乱数生成で、かなりの数のインスタンスに対して同じ値を生成するようです。
代わりに使用できる簡単な代替手段はありますか?再現性とセキュリティは重要ではなく、独自のシードを迅速に生成することが重要です。これに対する最も簡単なアプローチは何でしょうか。可能であれば、クロスプラットフォームアプローチが適しています。
rdtsc
命令は、かなり信頼できる(そしてランダムな)シードです。
Windowsでは、__rdtsc()
組み込み関数を介してアクセスできます。
GNU Cでは、次の方法でアクセスできます。
unsigned long long rdtsc(){
unsigned int lo,hi;
__asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi));
return ((unsigned long long)hi << 32) | lo;
}
この命令は、プロセッサの電源がオンになってからの合計疑似サイクルを測定します。今日のマシンの頻度が高いことを考えると、2つのプロセッサが同時に起動し、同じ速度でクロックされている場合でも、2つのプロセッサが同じ値を返す可能性はほとんどありません。
unsigned seed;
read(open("/dev/urandom", O_RDONLY), &seed, sizeof seed);
srand(seed); // IRL, check for errors, close the fd, etc...
また、より優れた乱数ジェネレーターをお勧めします。
他のプロセスを起動するプロセスがあると思います。使用するシードを渡してもらいます。次に、そのマスタープロセスに、各プロセスの乱数を渡してシードとして使用させることができます。そうすれば、選択された任意のシードは実際には1つだけです...そのために時間を使用できます。
他のプロセスを起動するマスタープロセスがない場合、各プロセスに少なくとも一意のインデックスがある場合は、1つのプロセスで一連の乱数をメモリ(共有メモリの場合)またはファイルに生成することができます。 (共有ディスクの場合)次に、各プロセスにインデックスの乱数を引き出してシードとして使用させます。
単一のシードからの一連の乱数ほど、シードの均一な分布を提供するものはありません。
PIDと時間の組み合わせは、一意のシードを取得するのに十分なはずです。 100%クロスプラットフォームではありませんが、* nixプラットフォームでは getpid(3)
、Windowsでは GetProcessId
の99.9%が得られます。そこへの道。このようなものが機能するはずです:
srand((time(NULL) & 0xFFFF) | (getpid() << 16));
* nixシステムでは/dev/urandom
からデータを読み取ることもできますが、Windowsでのデータに相当するものはありません。
C++ 11を使用できる場合は、std::random_device
を検討してください。包括的なガイドについては、 リンク をご覧になることをお勧めします。
ビデオリンクからessentialメッセージを抽出する:neversrand
&Rand
を使用しますが、代わりにstd::random_device
とstd::mt19937
を使用します-ほとんどの場合、次のようになりますあなたが欲しい:
#include <iostream>
#include <random>
int main() {
std::random_device rd;
std::mt19937 mt(rd());
std::uniform_int_distribution<int> dist(0,99);
for (int i = 0; i < 16; i++) {
std::cout << dist(mt) << " ";
}
std::cout << std::endl;
}
C std lib time()関数から秒単位で測定される直線時間の代わりに、代わりにプロセッサのカウンタを使用できますか?ほとんどのプロセッサにはフリーランニングティックカウントがあります。たとえば、x86/x64には タイムスタンプカウンター :があります。
タイムスタンプカウンタは、Pentium以降のすべてのx86プロセッサに存在する64ビットレジスタです。リセット後のティック数をカウントします。
(このページには、さまざまなプラットフォームでこのカウンターにアクセスするための多くの方法もあります-gcc/msvisual c/etc)
タイムスタンプカウンターには欠陥がないわけではなく、プロセッサー間で同期されない可能性があることに注意してください(おそらくアプリケーションを気にしないでしょう)。また、省電力機能によってプロセッサのクロックがアップまたはダウンする場合があります(ここでもおそらく気にしないでしょう)。
ただのアイデア... GUID(16バイト)を生成し、その4バイトまたは8バイトのチャンクを合計して(シードの予想される幅に応じて)、整数の折り返しを可能にします-結果をシードとして使用します。
GUIDは通常、それらを生成したコンピューターの特性(MACアドレスなど)をカプセル化します。これにより、2つの異なるマシンが同じランダムシーケンスを生成する可能性はかなり低くなります。
これは明らかに移植性がありませんが、システムに適したAPI /ライブラリを見つけるのはそれほど難しいことではありません(例:Win32のUuidCreate
、uuid_generate
Linux上)。
CryptGenRandom()
およびRtlGenRandom()
を提供します。それらは、シードとして使用できるランダムバイトの配列を提供します。
OpenSSLのRand_bytes()
を使用して、Linuxでランダムなバイト数を取得できます。デフォルトでは/dev/random
を使用します。
#ifdef _WIN32
#include <NTSecAPI.h>
#else
#include <openssl/Rand.h>
#endif
uint32_t get_seed(void)
{
uint32_t seed = 0;
#ifdef _WIN32
RtlGenRandom(&seed, sizeof(uint32_t) );
#else
Rand_bytes(&seed, sizeof(uint32_t) );
#endif
return seed;
}
Opensslは、暗号化的に安全なPRNGをデフォルトで提供するため、直接使用できることに注意してください。詳細 ここ 。
一意性が重要な場合は、各ノードが他のノードによって要求されたIDを認識できるように調整する必要があります。 「誰かがIDxを要求しましたか?」と尋ねるプロトコルでこれを行うことができます。または、各ノードが他のノードに割り当てられていないIDを選択できるように事前に調整します。
(GUIDはマシンのMACを使用するため、「事前に手配する」カテゴリに分類されます。)
なんらかの形で合意がないと、2つのノードが同じIDをクライミングするリスクがあります。
適度にPOSIX風のシステムを使用していると仮定すると、clock_gettime
が必要です。これにより、現在の時刻がnanosecondsになります。つまり、すべての実用的な目的で、同じ値を2回取得することは不可能です。 (理論的には、悪い実装では解像度がはるかに低くなる可能性があります。たとえば、ミリ秒に100万を掛けるだけですが、Linuxのような半ばまともなシステムでも実際のナノ秒の結果が得られます。)