Annotation of nono/vm/thread.cpp, revision 1.1.1.7

1.1       root        1: //
                      2: // nono
                      3: // Copyright (C) 2021 nono project
                      4: // Licensed under nono-license.txt
                      5: //
                      6: 
                      7: //
                      8: // スレッドを持つデバイス
                      9: //
                     10: 
                     11: #include "thread.h"
1.1.1.3   root       12: #include "mainapp.h"
1.1       root       13: #include "mythread.h"
1.1.1.6   root       14: #include <time.h>
                     15: #include <algorithm>
                     16: 
                     17: static void PrintSuggestion(int) __unused;
1.1       root       18: 
                     19: // コンストラクタ
1.1.1.3   root       20: ThreadDevice::ThreadDevice(uint objid_)
1.1.1.2   root       21:        : inherited(objid_)
1.1       root       22: {
1.1.1.5   root       23:        // SetName() はこのクラスでオーバーライドしているので
                     24:        // ここで(改めて)実行する必要がある。
                     25:        SetName(GetName());
1.1       root       26: }
                     27: 
                     28: // デストラクタ
                     29: ThreadDevice::~ThreadDevice()
                     30: {
                     31:        // ここで virtual の Terminate() は呼べないので、
                     32:        // 継承側のデストラクタで TerminateThread() をそれぞれ呼ぶこと。
                     33: }
                     34: 
1.1.1.5   root       35: // オブジェクト名を設定(更新)するとともにスレッド名も更新する。
                     36: // Object::SetName() のオーバーロード。
1.1       root       37: void
1.1.1.5   root       38: ThreadDevice::SetName(const std::string& newname)
                     39: {
                     40:        inherited::SetName(newname);
                     41:        SetThreadName(newname);
                     42: }
                     43: 
                     44: // スレッドにつけるスレッド名を覚えておく
                     45: // (このスレッドにスレッド名を設定する、ではない)。
                     46: void
                     47: ThreadDevice::SetThreadName(const std::string& threadname_)
1.1       root       48: {
                     49:        threadname = threadname_;
                     50: }
                     51: 
                     52: // スレッド開始
                     53: bool
                     54: ThreadDevice::StartThread()
                     55: {
1.1.1.5   root       56:        // ここまでに名前はセットしておくこと。
                     57:        assert(threadname.empty() == false);
                     58: 
1.1.1.6   root       59:        // スレッド起動。
1.1       root       60:        std::lock_guard<std::mutex> lock(thread_starter);
1.1.1.4   root       61:        try {
1.1.1.6   root       62:                thread.reset(new std::thread(&ThreadDevice::OnStart, this));
1.1.1.4   root       63:        } catch (...) { }
1.1       root       64:        if ((bool)thread == false) {
1.1.1.5   root       65:                warnx("Failed to initialize thread(%s) at %s",
                     66:                        threadname.c_str(), __method__);
1.1       root       67:                return false;
                     68:        }
                     69:        return true;
                     70: }
                     71: 
1.1.1.6   root       72: // 開始されたスレッドでのエントリポイント。
                     73: void
                     74: ThreadDevice::OnStart()
                     75: {
                     76:        // このスレッドにスレッド名を設定。
                     77:        PTHREAD_SETNAME(threadname.c_str());
                     78: 
                     79:        // パフォーマンス計測のためのリストに追加。
                     80:        // デバッガスレッドはなくてもいいか。
                     81:        if (GetId() != OBJ_DEBUGGER && GetId() != OBJ_HOSTCOM_DBG) {
                     82:                auto thman = gMainApp.GetThreadManager();
                     83:                thman->RegistThread(this, threadname);
                     84:        }
                     85: 
                     86:        // 実行開始。
                     87:        std::lock_guard<std::mutex> lock_sub(this->thread_starter);
                     88:        ThreadRun();
                     89: }
                     90: 
1.1       root       91: // スレッドに終了を要求し、その終了を待つ。
                     92: // スレッド外から呼ぶこと。
                     93: void
                     94: ThreadDevice::TerminateThread()
                     95: {
                     96:        if ((bool)thread) {
1.1.1.6   root       97:                auto thman = gMainApp.GetThreadManager();
                     98:                thman->UnregistThread(this);
                     99: 
1.1       root      100:                Terminate();
                    101: 
                    102:                if (thread->joinable()) {
                    103:                        thread->join();
                    104:                }
                    105: 
                    106:                thread.reset();
                    107:        }
                    108: }
1.1.1.3   root      109: 
1.1.1.6   root      110: // このスレッドのスレッドアフィニティを設定する。
1.1.1.5   root      111: /*static*/ void
                    112: ThreadBase::SetThreadAffinityHint(AffinityClass hint)
1.1.1.3   root      113: {
                    114: #if defined(HAVE_PTHREAD_SETAFFINITY_NP) && defined(HAVE_MICPUSET)
1.1.1.6   root      115:        std::lock_guard<std::mutex> lock(exlock);
1.1.1.3   root      116: 
1.1.1.6   root      117:        // policy, hint から cpuset を作成する。
1.1.1.3   root      118:        //
1.1.1.6   root      119:        // policy       hint            動作
                    120:        // -------      -----           ----
                    121:        // Neutral      *                       nop
                    122:        // Binding      Light           set Lo  (if 高性能コア数 < 3)
                    123:        // Binding      Light           nop             (if 高性能コア数 >= 3)
                    124:        // Binding      Heavy           set Hi
                    125: 
                    126:        if (gMainApp.affinity_policy == MainApp::AffinityPolicy::Neutral) {
1.1.1.3   root      127:                return;
                    128:        }
                    129: 
1.1.1.6   root      130:        const std::vector<bool>& fastcore = gMainApp.fastcore;
                    131: 
                    132:        // 高性能コアが少ない場合、Light スレッドに高性能コアを奪われたくないので
                    133:        // Light スレッドを低性能コアに縛っておきたい。
                    134:        // 高性能コアの数が多ければ、そこまでする必要もないか。
                    135:        if (hint == AffinityClass::Light) {
                    136:                uint nhigh = std::count(fastcore.begin(), fastcore.end(), true);
                    137:                if (nhigh >= 3) {
                    138:                        return;
                    139:                }
                    140:        }
                    141: 
                    142:        // ここまで来ると、このスレッドが Heavy なら高性能コアに、
                    143:        // Light なら低性能コアにバインドすることが確定。
1.1.1.3   root      144:        MICPUSet cset;
1.1.1.6   root      145:        bool target = (hint == AffinityClass::Heavy);
                    146:        for (uint i = 0, end = fastcore.size(); i < end; i++) {
                    147:                if (fastcore[i] == target) {
                    148:                        cset.Set(i);
1.1.1.3   root      149:                }
                    150:        }
                    151: 
1.1.1.6   root      152:        // 設定。
1.1.1.3   root      153:        int r = PTHREAD_SETAFFINITY(pthread_self(), cset);
                    154: 
                    155:        if (gMainApp.hostcpu->loglevel >= 1) {
                    156:                std::string csetstr;
1.1.1.6   root      157:                for (uint i = 0, end = fastcore.size(); i < end; i++) {
                    158:                        // 読みやすさのための区切り。先頭の1つは表示時に捨てる。
                    159:                        uint n = i % 16;
                    160:                        if (n == 0) {
                    161:                                csetstr += '\'';
                    162:                        }
                    163:                        if (cset.Get(i)) {
                    164:                                csetstr += "0123456789ABCDEF"[n];
                    165:                        } else {
                    166:                                csetstr += '_';
                    167:                        }
1.1.1.3   root      168:                }
                    169:                gMainApp.hostcpu->putmsgn("SetAffinity:%-12s(%s) %s%s%s",
                    170:                        PTHREAD_GETNAME().c_str(),
                    171:                        (hint == AffinityClass::Light ? "Light" : "Heavy"),
1.1.1.6   root      172:                        csetstr.c_str() + 1,
1.1.1.3   root      173:                        (r == 0 ? "" : " = "),
                    174:                        (r == 0 ? "" : strerror(r)));
1.1.1.6   root      175: 
                    176:                // この後のエラーメッセージより先にログスレッドが動いたら嬉しい。
                    177:                usleep(1);
1.1.1.3   root      178:        }
                    179: 
                    180:        // エラーなら errno ではなく戻り値にエラー番号が返ってくる。
1.1.1.6   root      181:        // NetBSD では sysctl で許可しないと一般ユーザはこの機能を使えない。
                    182:        if (r != 0) {
                    183:                errno = r;
                    184:                warn("PTHREAD_SETAFFINITY");
                    185:                PrintSuggestion(r);
                    186:                // 本当は終了したいが、この時点からではちょっと面倒なので放置。
                    187:                // 以降は何もしない。
                    188:                gMainApp.affinity_policy = MainApp::AffinityPolicy::Neutral;
1.1.1.3   root      189:        }
                    190: #else
                    191:        // なければ無視する。
                    192: #endif
                    193: }
1.1.1.6   root      194: 
                    195: /*static*/ std::mutex ThreadBase::exlock;
                    196: 
                    197: 
                    198: //
                    199: // スレッドマネージャ
                    200: //
                    201: 
                    202: // コンストラクタ
                    203: ThreadManager::ThreadManager()
                    204:        : inherited(OBJ_NONE)
                    205: {
                    206:        SetName("ThreadManager");
                    207: }
                    208: 
                    209: // デストラクタ
                    210: ThreadManager::~ThreadManager()
                    211: {
                    212: }
                    213: 
                    214: // 表示順のスレッド名
                    215: static const std::vector<const char *>
                    216: disp_order = {
                    217:        "Main",
                    218:        "Logger",
                    219:        "SignalThread",
                    220:        "VM",
                    221:        "VirtIOBlock0",
                    222:        "VirtIOBlock1",
                    223:        "VirtIOBlock2",
                    224:        "VirtIOBlock3",
                    225:        "VirtIOBlock4",
                    226:        "VirtIOBlock5",
                    227:        "VirtIOBlock6",
                    228:        "VirtIOBlock7",
                    229:        "VirtIONet",
                    230:        "VirtIOSCSI",
                    231:        "VirtIOEntropy",
                    232:        "VideoRenderer",
                    233:        "SoundRenderer",
                    234:        "HostNet0",
                    235:        "HostNet1",
                    236:        "HostCOM0",
                    237:        "HostCOM1",
                    238:        "HostCOM2",
                    239:        "HostSound",
                    240: };
                    241: 
                    242: // スレッドリストに登録する。
                    243: // 登録したいスレッド自身から呼ぶこと。
                    244: void
                    245: ThreadManager::RegistThread(Object *obj, const std::string& name)
                    246: {
                    247:        // パフォーマンス計測のため自スレッドの clockid を取得。
                    248:        clockid_t clockid;
                    249:        pthread_getcpuclockid(pthread_self(), &clockid);
                    250: 
                    251:        std::lock_guard<std::mutex> lock(threads_mutex);
                    252:        threads.emplace_back(obj, name, clockid);
                    253:        // 都度表示順にソートする。
                    254:        std::sort(threads.begin(), threads.end(),
                    255:                [](const ThreadInfo& a, const ThreadInfo& b) {
                    256:                        auto ia = std::find(disp_order.begin(), disp_order.end(), a.name);
                    257:                        auto ib = std::find(disp_order.begin(), disp_order.end(), b.name);
                    258:                        return ia < ib;
                    259:                }
                    260:        );
                    261: }
                    262: 
                    263: // スレッドリストから obj を削除する。
                    264: void
                    265: ThreadManager::UnregistThread(Object *obj)
                    266: {
                    267:        std::lock_guard<std::mutex> lock(threads_mutex);
                    268: 
                    269:        for (auto it = threads.begin(); it != threads.end(); ++it) {
                    270:                if ((*it).obj == obj) {
                    271:                        threads.erase(it);
                    272:                        break;
                    273:                }
                    274:        }
                    275: }
                    276: 
                    277: // スレッドの CPU 利用率を記録する。
                    278: void
                    279: ThreadManager::CalcPerf(uint64 rtime)
                    280: {
                    281:        std::lock_guard<std::mutex> lock(threads_mutex);
                    282: 
                    283:        for (auto& info : threads) {
                    284:                struct timespec ts;
                    285:                clock_gettime(info.clockid, &ts);
1.1.1.7 ! root      286:                uint64 t = nsec_to_tsec(sec_to_nsec(ts.tv_sec) + ts.tv_nsec);
1.1.1.6   root      287:                if (__predict_true(last_rtime != 0)) {
                    288:                        // 2回目以降
                    289:                        uint percent = (t - info.last_clock) * 100 / (rtime - last_rtime);
                    290:                        info.load->EnqueueForce(percent);
                    291:                }
                    292:                info.last_clock = t;
                    293:        }
                    294:        last_rtime = rtime;
                    295: }
                    296: 
                    297: #define hostcpu_putmsg(lv, fmt...)     do {    \
                    298:        if (hostcpu->loglevel >= (lv))          \
                    299:                hostcpu->putmsgn(fmt);                  \
                    300: } while (0)
                    301: 
                    302: #if defined(__x86_64__)
1.1.1.7 ! root      303: #include <cpuid.h>
1.1.1.6   root      304: // x86_64 CPU の P コア、E コアを調べて fastcore 配列に返す。
                    305: // fastcore は事前にコア数分確保してあること。
                    306: // 検出できれば fastcore を更新して true を返す。
                    307: // 検出できなければ fastcore は不定で false を返す。
                    308: // 失敗ならエラーメッセージも表示する。
                    309: // MainApp から呼ばれるが、スレッド関連なのでこっちに置く。
                    310: /*static*/ bool
                    311: ThreadManager::DetectCPUAffinity_x86(std::vector<bool>& fastcore)
                    312: {
                    313:        Object *hostcpu = gMainApp.hostcpu.get();
                    314: 
                    315:        // CPUID(0x1a, 0) は Intel Core 12世代以降でハイブリッドアーキテクチャ
                    316:        // 情報を返す。eax の bit31-24 (最上位バイト) が 0x40 なら P コア、
                    317:        // 0x20 なら E コア。0 ならたぶんハイブリッド構成でない CPU。
                    318:        //
                    319:        // まずどのコアでもいいので現在のコアで実行してみる。
                    320:        // ここでハイブリッド構成だと分かれば、個別のコアについて調べるのだが、
                    321:        // ただしそのためのスレッドアフィニティ指定には特権が必要な場合があるので、
                    322:        // 特権を要求する作業が必要になるかどうかを先に調べておきたいということ。
                    323: 
                    324: #if defined(HAVE_PTHREAD_SETAFFINITY_NP) && defined(HAVE_MICPUSET)
                    325:        // まず現在のコアで。
                    326:        uint32 eax, ebx, ecx, edx;
                    327:        __cpuid_count(0x1a, 0, eax, ebx, ecx, edx);
                    328: 
                    329:        if ((eax >> 24) == 0) {
                    330:                // CPU はハイブリッド構成ではない。
                    331:                hostcpu_putmsg(1, "%s: Not a hybrid type core", __func__);
                    332:                return false;
                    333:        } else {
                    334:                // CPU はハイブリッド構成なので、個別のコアについて調べる。
                    335:                for (uint i = 0, end = fastcore.size(); i < end; i++) {
                    336:                        int r;
                    337:                        std::thread th([&] {
                    338:                                MICPUSet cset;
                    339:                                cset.Set(i);
                    340:                                r = PTHREAD_SETAFFINITY(pthread_self(), cset);
                    341:                                __cpuid_count(0x1a, 0, eax, ebx, ecx, edx);
                    342:                                if ((eax >> 24) == 0x40) {
                    343:                                        fastcore[i] = true;
                    344:                                }
                    345:                        });
                    346:                        th.join();
                    347: 
                    348:                        if (r != 0) {
                    349:                                errno = r;
                    350:                                warn("PTHREAD_SETAFFINITY");
                    351:                                PrintSuggestion(r);
                    352:                                return false;
                    353:                        }
                    354:                        hostcpu_putmsg(1, "%s: Core%u is %c",
                    355:                                __func__, i, (fastcore[i] ? 'P' : 'E'));
                    356:                }
                    357:                return true;
                    358:        }
                    359: #else
                    360:        hostcpu_putmsg(1, "%s: No PTHREAD_SETAFFINITY", __func__);
                    361:        return false;
                    362: #endif
                    363: }
                    364: #endif // __x86_64__
                    365: 
                    366: // PTHREAD_SETAFFINITY() が EPERM になった時に表示するお知らせ。
                    367: // 2箇所から呼ばれるので関数にしてある。
                    368: static void
                    369: PrintSuggestion(int errorcode)
                    370: {
                    371: #if defined(__NetBSD__)
                    372:        if (errorcode == EPERM) {
                    373:                fprintf(stderr,
                    374:                " ** To allow non-privileged users to set thread affinity on NetBSD:\n"
                    375:                " **  # sysctl -w security.models.extensions.user_set_cpu_affinity=1\n"
                    376:                );
                    377:        }
                    378: #endif
                    379: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.