覚書 引用 ChatGPT

 

オリジナル(仮)

for (int i = iStart; i < iEnd; ++i) {
    pDst[i] = pSrc[i];
}

高速化版(一行ずつ改変明示)

// 1. restrict でポインタの非エイリアス宣言(ただしC++では非標準。VCなら __restrict 使用)
T* __restrict pS = pSrc + iStart; // 元: pSrc[i]
T* __restrict pD = pDst + iStart; // 元: pDst[i]
int iLen = iEnd - iStart;         // 2. 範囲長を事前計算

int i = 0;
int iLen4 = iLen & ~3;            // 3. 4の倍数部分まで(ループアンローリング用)

// 4. アンローリングループ(4要素ずつコピー)
for (; i < iLen4; i += 4) {
    pD[i    ] = pS[i    ];
    pD[i + 1] = pS[i + 1];
    pD[i + 2] = pS[i + 2];
    pD[i + 3] = pS[i + 3];
}

// 5. 余り(最大3要素)をコピー
for (; i < iLen; ++i) {
    pD[i] = pS[i];
}

コメント付きまとめ
行    内容    説明
1-2    __restrict 使用    ポインタが非エイリアスであると仮定することで最適化しやすくなる(MSVCで効果大)
3    iLen    範囲長を事前に計算し、ループの条件式から差分を排除
4-9    4連アンローリング    実行パイプライン効率化、分岐回数減少
11-13    余り要素処理    長さが4で割り切れない場合の残り処理

覚書。

        // 呼び出し側が高速に使える forEach 的な手法
        //これは「関数の中に関数を渡して処理する」=高階関数というテクです!
        // fnとは?渡されたラムダ関数・関数ポインタ・関数オブジェクトのこと。
        //// 全要素を加算する処理(呼び出し側は何も意識せず!)
        template <typename Func>
        void forEach(Func&& fn) {
            T* pVec = this->mVector;
            for (size_t i = 0; i < piSize; ++i) {
#ifdef PREFETCH_OPT
                __builtin_prefetch(&pVec[i + 1], 0, 1);
#endif
                fn(pVec[i]); // ← fnがここで1要素に対する処理を行っている!
            }
        }
        //        //呼び出し方
        //        void foo(){
        //            int sum = 0;
        //            arr.forEach([&](int& val) {
        //                sum += val;  // ← fnの中身
        //            });
        //        }

 

引用 ChatGPT

 

私はフォルダ名・ファイル名には必ず日付の6桁(YYMMDD)を付けている。ファイラーに時系列でソートされているため使いやすい。

このソフトはクリップボードにYYMMDDの6桁の数字を入れてくれる。タスクバーにピン留していて、後はぺするだけ。

改行を削除したのは、読んでもらうため。コードは素人丸出しです。

 

#include <iostream>
#include <filesystem>
#include <thread>
#include <chrono>
#include <Windows.h>
#include "shlobj_core.h"
inline BOOL SetClipboardText(const char* str) {int BufSize;char* Buf;HANDLE hMem;BufSize = strlen(str) + 1;hMem = GlobalAlloc(GMEM_SHARE | GMEM_MOVEABLE, BufSize);if (!hMem) {return false;}Buf = (char*)GlobalLock(hMem);if (Buf) {strcpy_s(Buf, 32, str);GlobalUnlock(hMem);if (OpenClipboard(NULL)) {EmptyClipboard();SetClipboardData(CF_TEXT, hMem);CloseClipboard();return true;}}return false;}inline std::string fcStrYYMMDD() {auto now = std::chrono::system_clock::now();auto t = std::chrono::system_clock::to_time_t(now); auto tm = *std::localtime(&t); std::ostringstream oss; oss << std::put_time(&tm, "%Y%m%d"); return oss.str().erase(0, 2); }inline void fcYYMMDD_Clipboard() { std::string str = fcStrYYMMDD(); const char* datetime = str.c_str(); if (!SetClipboardText(datetime)) { std::cout << "error!\n"; getchar(); } }inline void fcHolder_ClipboardCopy(std::string& strFN) {const char* path = strFN.c_str(); HGLOBAL hGlobal = GlobalAlloc(GHND, sizeof(DROPFILES) + strlen(path) + 2); if (!hGlobal) { return; }DROPFILES* dropfiles = (DROPFILES*)GlobalLock(hGlobal); if (!dropfiles) { GlobalFree(hGlobal); return; }dropfiles->pFiles = sizeof(DROPFILES); dropfiles->fNC = TRUE; dropfiles->fWide = FALSE; memcpy(&dropfiles[1], path, strlen(path)); GlobalUnlock(hGlobal); if (!OpenClipboard(NULL)) {GlobalFree(hGlobal);return;}if (!EmptyClipboard()) {GlobalFree(hGlobal);return;}if (!SetClipboardData(CF_HDROP, hGlobal)) {GlobalFree(hGlobal);return;}GlobalFree(hGlobal);CloseClipboard();return;}inline void fc_N_Minits(int& iN) {for (int i = iN; i >= 1; i--) {std::this_thread::sleep_for(std::chrono::seconds(1));std::cout << i << "秒";if (i != 1) { std::cout << "..."; }}std::cout << "\n\n";}inline void fcYYMMDD_Holder_Clipboard() {std::string strYYMMDD = fcStrYYMMDD();std::string strCrentPath = std::filesystem::current_path().string<char>();std::string strFN = strCrentPath + "\\" + strYYMMDD;std::filesystem::create_directory(strFN);fcHolder_ClipboardCopy(strFN);int iN = 10;std::cout <<"■■■ IronYYMMDD2 ■■■\n\n""YYMMDDフォルダサービスを開始しました。\n"<<iN<<"秒後にサービスを終了します。\n";fc_N_Minits(iN);std::filesystem::remove_all(strFN);iN = 5;std::cout <<"YYMMDDフォルダサービスを終了しました。\n""クリップボードからフォルダ貼り付けはできません。\n代わりにクリップボードにYYMMDDを挿入しました。"<< iN<< "秒後、自動的にプログラムを終了します。\n";fc_N_Minits(iN);fcYYMMDD_Clipboard();return;}int main() {if (GetAsyncKeyState(VK_SHIFT) != 0) {fcYYMMDD_Holder_Clipboard();}fcYYMMDD_Clipboard(); return 0;}

ある日、if文の中かっこ{}の省略できることを知った。

if文の処理内容が1行なら{}がなくてもコンパイルが通る。

なんかかっこよかったので、かなり乱用した。
・・・

起こりました。

 

・・・

その日から必ず{}を入れるようになりました。

 

メリット

その日、気分がいい。

 

デメリット

いつか、気分を悪くする。

クラス継承によって関数呼び出しが、遅くなるとの報告。数値解析等の時間がおしい計算でのパフォーマンスの影響は?気になる。

 

引用

https://puarts.com/?pid=988

 

 

 

引用元は14年前の記事なので、現在2025年の計測をして見たい。

ここに書く

覚書

あなたの仮説通り、スレッド作成コストと同期オーバーヘッドが重いです。
swap専用スレッド + 通知待機型の雛形(std::condition_variable)

std::mutex mtx;
std::condition_variable cv;
bool ready = false;

void swap_worker() {
    while (true) {
        std::unique_lock<std::mutex> lock(mtx);
        cv.wait(lock, []{ return ready; }); // 条件待ち

        // ここでswap処理
        ready = false;
    }
}

これならCPU 100%にならず、省エネで待機できます。

 

引用 ChatGPT

覚書。

前から謎だった件。

std::threadであまり速度が速くならなかったので、オーバーヘッドだなと。

だとしたら、関数専用スレッドを作ったらから始まった。

 

while(true) ループでCPUが100%にならない?

結論:
    何も入っていないループはCPUを100%使います。
    しかし**「軽すぎて人間が気づかない」**のと、
    OSスケジューラが「ほぼ無駄な仕事」と判断して他スレッドに回すため、 あまり深刻に見えないことも多いです。

もし以下のように「本当に何もしていないループ」があれば:

while (!flag) {
    // 本当に何もしてない
}

これが何十万回/秒で回ってたら、当然CPUは忙殺されます。

対策:
#include <thread>
std::this_thread::sleep_for(std::chrono::milliseconds(1)); // 負荷を逃がす

あるいは std::condition_variable を使えば OS がちゃんと「寝かせてくれます」(これが次の 5 の話)。

5. condition_variable::wait の省エネ仕組みとは?

std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, []{ return ready; });

このとき、スレッドは OS に「休ませてください」と伝えることになります。 → OSはスレッドを完全にスリープ状態に入れる
→ CPUリソースを一切使いません
つまり:

    ポーリングループ(busy wait):CPU酷使
    条件付きwait:CPUリソース0、めっちゃ省エネ

GUIアプリが多用するのはこの省エネ型です。
GUIの謎ループの中身も実はこう:

while (GetMessage(&msg, nullptr, 0, 0)) {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
}

→ GetMessage 内部が OSのwaitシステムコール で「新しいイベントが来るまで寝る」
→ CPUはアイドル状態。省エネです。

 まとめ
項目    ポイント
while(true) ループ    何もしないと100%CPU食う。軽い処理&OSの最適化で目立たないだけ
cv.wait()    スレッドを完全にスリープさせて省エネ

引用 ChatGPT

覚書。

this-> の意味    テンプレート継承で名前解決のため。速度的にはローカル化推奨
 

3. this-> はなぜ使うの? 継承との関係は?
継承先で使っている this->member は、テンプレート + 継承の組み合わせで必要になる場面があります。

template<typename T>
class Base {
protected:
    T mVector[10];
};

template<typename T>
class Derived : public Base<T> {
public:
    T& operator()(size_t i) {
        return this->mVector[i]; // this-> が必要
    }
};

これは「Base<T>::mVector がテンプレートによって名前解決が遅延されている」ため、this-> で明示しないとコンパイルできないことがあるんです。
速度についての補足:
    this->mVector[i] は実際には *(this + offset) 的なアクセスになる。
    対して、T* p = this->mVector; → p[i] にすると、レジスタに乗る確率がグッと上がる。
つまり:
auto& vec = this->mVector;
for (...) {
    do_something(vec[i]); // 高速になることが多い
}

 

auto& vec = this->mVector; はディープコピーか?
いい着眼点です!
 答え:NO、 これはディープコピー ではない!
auto& vec = this->mVector;
これは「参照(reference)を束ねてるだけ」です。
つまり vec[i] はそのまま this->mVector[i] と同じメモリを見ていることになります。
したがって:
inline T& operator()(const size_t& c) noexcept {
    return this->mVector[c];  // <- これは妥当
}
これ自体も 1回のアクセスごとに this-> のオフセット参照が発生するだけで、それほど重くないです。 でも、頻繁に呼ばれる場合は、この mVector を関数内で参照にキャッシュしたほうが速くなるケースもあります。
 結論(速度重視まとめ)
状況    おすすめ書き方    備考
単発呼び出し    this->mVector[c]    問題なし
高頻度アクセス    auto& vec = this->mVector; → vec[c]    レジスタ化・オフセット計算の省略効果あり
operator() 内部で使う    無理に最適化しなくてOK    ただし中で複数回アクセスするなら auto& 化あり

 

この変換はキャッシュヒット率&レジスタ最適化の観点で効果があります。

 

引用 ChatGPT

覚書。

 

1. memsetはint以外でも使える?
ゼロ初期化目的であればOKな型:
    int, float, double, bool, char, short, long, unsigned 系
    いわゆる「POD(Plain Old Data)」型ならOK ❌ NGや危険:
    ポインタ(アーキテクチャ依存でNULLポインタが非0バイトになる場合あり)
    オブジェクト型(コンストラクタ・デストラクタを持つ)
memsetでゼロ埋めしたいなら、「すべてのビットが0で意味がある」型だけにするのが安全。

 

2. std::is_same で memset分岐する方法?
if constexpr (std::is_same<T, int>::value || std::is_same<T, double>::value || ...) {
    std::memset(...);  // OK
} else {
    // 何もしない or static_assert でコンパイルエラー
}

 

テンプレート制約ならこう:
static_assert(
    std::is_same<T, int>::value || std::is_same<T, float>::value || ...,
    "T must be a POD type suitable for memset");

3. コンパイルエラーを強制する方法
static_assert(std::is_same<T, int>::value || std::is_same<T, double>::value,
              "This array class supports only primitive types");
これでオブジェクト型や構造体を使うと即ビルドエラーにできます。

 

4. memsetゼロ初期化OKなプリミティブ型一覧
    int, unsigned int
    short, unsigned short
    long, unsigned long
    long long, unsigned long long
    float, double, long double
    bool
    char, unsigned char, wchar_t, char16_t, char32_t
    std::byte(C++17~)  共通点:全ビットが 0 のとき「意味を持つ」型

 

引用 ChatGPT

覚書。

 

A. -O3とMSVCの最適化レベルについて
GCC / Clang 編(Unix系に多い)
    -O0:最適化なし
    -O1:軽めの最適化
    -O2:基本的な最適化(バランス型)
    -O3:最大限の最適化(アンローリング含む・コード肥大化あり)
    -Ofast:未定義動作上等(規格違反でも速度最優先)

-O3 では明示的にループアンローリング等が入る。

 

MSVC 編(Visual Studio系)
MSVCの最適化オプション(IDEの設定などで使うもの):

オプション    説明
/Od    最適化なし(デバッグ向き)
/O1    サイズ優先最適化(コード小さく)
/O2    スピード優先最適化(ガチ)
/Ox    最大限最適化(/O2 + 明示的強化)※非推奨
/GL    リンク時のコード生成最適化(LTCG)

 MSVCにおける/O2が、GCCでの-O3に相当する最強チューニング。
 MSVCでは:
    ループアンローリングも /O2 で自動的に入る(条件付き)
    /Oxはマイクロソフトも「非推奨」っぽく扱っている(古い)
 参考:
    /O2 + /GL + /favor:INTEL64 + /arch:AVX2 あたりでガチ組み

引用 ChatGPT