良いコア相関を見つけるために、サーバーはコア間の待ち時間をベンチマークしています。
両方のスレッドのコアアフィニティを異なるCPUに設定し、スレッド間のメッセージ待ち時間を計算しようとしています。メッセージはを介して転送されますstd::atomic
。実行時間は次のように計算されます。https://github.com/fuatu/core-latency-atomic
割り当てによるコアアフィニティ(POSIX)
void set_affinity(long cpu_num) {
cpu_set_t cpuset;
CPU_ZERO(&cpuset);
CPU_SET(cpu_num, &cpuset);
pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset);
}
ランタイムは、2つのスレッドがアクセスする原子で測定されます。
enum State
{
Preparing,
Ready,
Ping,
Pong,
Finish,
};
class Sync
{
public:
State wait_as_long_as(State wait_state)
{
State loaded_state = state.load();
while (loaded_state == wait_state)
loaded_state = state.load();
return loaded_state;
}
void wait_until(State expected_state)
{
while (state.load() != expected_state)
{
}
}
void set(State new_state, State expected_state)
{
//state.store(new_state);
state.compare_exchange_strong(expected_state, new_state);
}
private:
std::atomic<State> state{Preparing};
};
static void set_affinity(unsigned int cpu_num)
{
cpu_set_t cpuset;
CPU_ZERO(&cpuset);
CPU_SET(cpu_num, &cpuset);
pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset);
}
struct LatencyBench
{
LatencyBench(long first_cpu_, long second_cpu_)
: first_cpu{first_cpu_}
, second_cpu{second_cpu_}
{
}
void operator()(nonius::chronometer meter) const
{
Sync sync;
set_affinity(first_cpu);
std::thread t([&] {
set_affinity(second_cpu);
sync.set(Ready,Preparing);
State state = sync.wait_as_long_as(Ready);
while (state != Finish)
{
//if (state == Ping)
sync.set(Pong,Ping);
state = sync.wait_as_long_as(Pong);
}
});
sync.wait_until(Ready);
// start timer
sync.set(Ping,Ready);
sync.wait_until(Pong);
// stop timer
sync.set(Finish,Pong);
t.join();
}
const long first_cpu;
const long second_cpu;
};
./a.out
ランタイム時間はスレッド通信中にのみ測定され、開始時間または2番目のスレッドを開始する時間は含まれません。
測定された待ち時間は、同じプログラム内で繰り返された実験に対して堅牢です。昼寝をしてmain
2回目の分析を実行しても、待ち時間は依然として最初の測定の約1ナノ秒以内です。ただし./a.out
、再実行すると、測定された待機時間が約40ナノ秒ほど変化する可能性があります。これにより、最良の待ち時間でコアペアを変更することもできます。
同じ実行ファイルを2回実行するときに、これらのランダムな待ち時間の変更の後に何があるかをご存知ですか?
追加の詳細:numactl -m 0 -N 0 ./a.out
NUMAノード0内のコアペアを使用して集中しても、この状況は軽減されません。
子NUMAノードで構成されたサーバーを使用し、子NUMAノード内に残っても、この内容は変更されません。 Xeon および EPYC プロセッサは、遅延時間のボラティリティを複製しました。
main
2番目の分析ではおそらくすべてをキャッシュするので、これらの分析の多くはこれがキャッシュの問題ではないことを示していることを願っていますmain
。