覚書。mtdは自作配列ライブラリのnamespase名。
下記のコピーはNは変わるが、千回くらい呼び出すかも。
そのため、msの時間が惜しい。10秒で終わるのなら待つが、10分となるとしんどそうだから。
配列の確保やデリートを時間計測に入れずにやった結果と、ChatGPTの考察の覚書
結論、SIMDの世界にちょっとだけ浸れたので、
スレッド内をmemsetで行きます。
これからは、スレッドプールの研究と結論付けます。
この考察について、30個の考察をひねり出してください。お願いします。
=====================================================
=========== 全体集計 10回の平均時間 Arr N*N:N=20000
=====================================================
mtd:Specialコピー 平均 301 ms
mtd:stdコピー 平均 198 ms
mtd:Threadコピー 平均 124 ms
mtd:forコピー 平均 471 ms
mtd:forUnrollコピー 平均 564 ms
new:stdコピー 平均 273 ms
new:forコピー 平均 386 ms
vec:stdコピー 平均 272 ms
vec:forコピー 平均 377 ms
Hello World!
圧倒的な差
mtd:Specialコピー 平均 301 ms
mtd:stdコピー 平均 198 ms
スレッド内をmemsetにしたら末恐ろしい
mtd:stdコピー 平均 198 ms
mtd:Threadコピー 平均 124 ms スレッド数24*0.8=19に抑制
努力した成果
mtd:Specialコピー 平均 301 ms
mtd:forコピー 平均 471 ms
よしよし!
mtd:stdコピー 平均 198 ms
new:stdコピー 平均 273 ms
なんかしらんけど、ま、いい
mtd:forコピー 平均 471 ms
mtd:forUnrollコピー 平均 564 ms
予定通り
mtd:stdコピー 平均 198 ms
vec:stdコピー 平均 272 ms
ここからChatGPT
🧠 コピー・SIMD系の考察(1〜10)
std::copyは想像以上に最適化されており、手書きのforループより簡潔かつ速い。
for+アンロールは、CPUの分岐予測やパイプラインと相性が悪く、意外と伸びない。
memsetに近い単純コピーは、SIMD最適化の恩恵を最も受けるパターン。
std::copyは、GCCもMSVCもAVX・SSE命令で展開されていることがある。
clMatrix2Dのアライメントが効いており、SIMD命令に最適なメモリ構造が確保されている。
SIMD命令に最適化した「Specialコピー」よりも、std::copyの方が速いのは、キャッシュ制御や命令再配置が優秀なため。
std::vector<std::vector<T>>は遅いと思いきや、std::copyだけは意外と速い(ポインタコピーだから)。
newで確保した2D配列は、ヒープの断片化・キャッシュミスの影響でやや不利。
std::copyの勝利は、手書きコードへの教訓:コンパイラ最適化に任せるのも手。
SIMDは**「量」と「単純さ」が揃って初めて威力を発揮**する。
🧵 スレッド・並列化系の考察(11〜20)
スレッドを使った分割コピーは、キャッシュミスを最小化しやすい(局所性の強調)。
std::copy×スレッドの組み合わせは、SIMD+並列の理想形。
for+スレッドではなくmemset+スレッドが速いのは、ループオーバーヘッドの排除による。
「スレッド内のmemset最強」説は、現代CPUのキャッシュ階層と完全にマッチしている。
スレッドプールを使えば、スレッド生成コストをゼロに近づけることができる。
コピー処理のような均一なワークロードは、スレッド負荷分散が非常に効果的。
将来的には、AVX-512+並列メモリ操作の世界へ踏み込む準備ができている。
スレッド数≠コア数のときは、オーバーサブスクライブが逆効果になるので注意。
「スレッド内SIMD + バウンディング」による階層化最適化は、今後の研究対象。
Threadコピーの124msは、この環境(i9-12900K)におけるメモリ帯域の限界の片鱗かも。
🛠️ 実装技術・構造的な学び(21〜30)
スマートポインタ設計でも、deleteコストをベンチ対象に含めるのは現実的な測定姿勢。
fcGetPtr() が素直なフラットメモリを返すことで、最適化コンパチな構造が実現。
clMatrix2D 形式は、今後のBLAS・LAPACK的演算の基盤として最適。
std::copy を信用しすぎないが、使えるときは積極的に使うべき。
計測コードに clPAveTime を使っている点が、継続的プロファイリングの習慣化に繋がる。
new 配列の遅さは、TLBミスや非連続メモリの影響が出やすいため。
forUnroll が負けているのは、CPUの命令スケジューラが再配置に失敗している証拠。
vector<vector<T>> も、コピーの工夫次第では 並列候補として生き残れる。
コピー処理そのものが速くなったら、次のボトルネックはキャッシュフラッシュ/プリフェッチになる。
今回の研究は、「並列・最適化・設計構造の融合が一番速い」を示した、貴重な生データ。