はじめに
こんにちは,Latte72 です.
この度,セキュリティキャンプ2026全国大会の 開発L2ゼミ に応募し,無事選考を通過して参加してきました.
普通であれば,応募課題晒しは通過発表があった6月中にやるようなものなのですが,予定が立て込んでいて 面倒で放置していたので参加後の執筆となってしまいました.
今回自分が参加したL2ゼミは昨年度と担当者が変わっているということもあり,自分は特に過去に参加した方の回答を参考にしたわけではありませんが,応募課題を書く際に「応募課題晒し」を参考にする方は多いと思うので,私も誰かの役に立つことを願って応募課題を公開しておこうと思います.
先に断っておくと,この L2 の応募課題には自分らしさや試行錯誤の過程などはあまり残せなかったなと感じています.
自分はこの応募課題に取り組む際に,講師が書いた『Verylで作るCPU』は軽く読んでいたものの,実装自体は第1章の途中までしかできておらず,CPUの設計についても実はあまり理解できていませんでした.
そのため,私の回答には間違いや考察が甘い箇所が多数含まれることをご承知おきください.
参加記も執筆したので是非こちらもご覧ください. 執筆中です.
L2 『プロセッサゼミ』 について
日常的に使用するスマートフォンやパソコンにおいて、プログラムの実行速度はユーザー体験を左右する最も重要な要素です。プログラムを高速かつ高効率に処理するためには、コンピュータがどのように構成されて動いているか(コンピュータアーキテクチャ)の理解が不可欠です。
本ゼミでは、コンピュータの主要な部品である「プロセッサ」について、ハードウェアとソフトウェアの両面から実践的に学びます。
(公式サイトより引用)
L2 はプロセッサについて扱うゼミで,HDL でCPU を自作するCPU自作班と,NPU 上で動作するプログラムを作成するNPUプログラミング班に分かれています.
私はCPU自作班に応募しました.
CPU自作班を選んだ理由については後ほど課題の中で触れます.
実際に L2 ゼミに参加した感想などは参加記に記載してあります.
実際の応募課題と私の回答
「プロセッサゼミ」の応募課題は全3問(Q1-Q3)からなります.
Q1は共通問題であり,当ゼミに応募する方は全員回答してください.
Q2とQ3は選択問題です.このゼミは途中からCPU自作班とNPUプログラミング班に分かれます.選考に通過した場合,回答した選択問題に対応する班に配属されます.両方とも回答した場合の配属は講師側で決定させてもらいます.
書いてあるままですが,共通問題 Q1とCPU自作班専用の Q2 に分かれています.
私はNPUプログラミング班には応募していないので割愛します.
引用になっている部分が与えられた課題で,そこに続く文章が私の回答,注釈になっているのが応募課題晒しをするにあたって付けたコメントです.
Q1(共通)
Q1.1
プロセッサゼミをなぜ志望するか教えてください.
あわせて,本ゼミで取り組みたいことがあれば記述してください.
ゼミではまず基本的な課題に取り組んだ後,受講者の皆さんそれぞれの興味に応じた発展課題に取り組む時間を設ける予定です.
また,何か他にアピールしたいこと(今まで作ってきたものなど)があれば,それも自由に書いてください.
私はこれまでx86-64をターゲットとしたCコンパイラ LaCC やRISC-V をターゲットとしたOS を自作してきました.
RISC-V 64 bit をターゲットとしたOS である os2026(仮) はまだ開発途中で,ほとんど機能が実装されていない状況ですが,今後 OS の実装も進めていき,自分で設計した CPU の上で自分が設計した OS を動かしたい と思い,このゼミに応募させていただきました.
自分が作ってきたものが書いてあった方が,技術力のレベル感が伝わりやすいのではないかと考えて製作物を載せています.
また,私が低レイヤーに触れたいと思ったそもそものきっかけは,中学生の時に触れたPythonがあまり高速でないことに不満を感じたことでした.
そこでプログラミング言語の設計,ひいてはOSの設計に興味を持ち,コンパイラやOSを自作してきました.
コンパイラやOSなどはCPU についてあくまでもソフトウェアのレイヤーから見たものであり,それぞれのアーキテクチャやプロセッサがどのような設計思想を持っていて,どのような構造をしているのかについてはあまり考える機会がありませんでした.
速度やセキュリティなどの観点でさらに性能を向上させるためには,CPU 自体の構造や命令セットの設計についても理解する必要があると感じるようになりました.
昨年,kanatasoさんの「Verylで作るCPU」を読み,CPUもソフトウェアに近い形で設計できることを知り,CPUの自作というものにより現実味を感じるようになりました.
プロセッサは単純な命令群だけでなく,SIMDのような拡張命令やセキュリティの保護機能のための命令など様々なものがあり,時間があればそれらも実装しながらプロセッサの構造を理解していきたいと考えています.
私は技術書典で「Verylで作るCPU」を購入し物理本を持っていますが,なんとこの本はWeb上で無償で公開されています!!!
とても丁寧にわかりやすく書かれていて,これだけ見ればCPUの基本設計を学ぶことができるので,ぜひ https://cpu.kanataso.net/ をご覧ください!
Q1.2
ターミナルでcoding agentに「このディレクトリ以下の構造を把握して」とプロンプトを入力してから結果がターミナルに表示されるまでにコンピュータの内側で起こっていることを,プロセッサの視点に立って説明してください.
ここではリモートのAPIで動作するcoding agentを想定して説明します.
また,リモート側のサーバーでの処理に関しては割愛し,ユーザーのPC側での処理に注目して説明します.
まず,ユーザーがキーボードで文字を入力します.
キーが押されると,キーボード内部のマイコンがキー入力を検出し,ホスト側のI/Oデバイスに信号を送り,割り込みが発生します.
改めて見ると,割り込みに関してもう少ししっかり書けそうな気がします.
- キー入力をきっかけとして外部デバイスが割り込みを要求する
- 割り込み要求がPLICなどに通知される
- 割り込みが有効であればtrapして,
mepcやmcauseなどを更新してtrap handlerへ制御を移す - handlerは汎用レジスタをメモリへ退避した後,PLICのレジスタを読み出して割り込み要因を特定し,対応するdeviceの処理を行う
- 処理後は割り込みの完了を通知し,レジスタを復元してMRETを実行することで元のプログラムへ復帰する
ターミナルがフォーカスされている場合,OSはその入力イベントをターミナルエミュレータのプロセスに配送します.
coding agent のCLIが起動している場合,その入力行がターミナルからptyを経由してCLIプロセスに渡されます.
その後,CLIはプロンプト本文,ディレクトリ構造,モデル名,認証トークン,セッション情報などからAPIに送るリクエストを組み立てます.
APIへ送信する段階では,CLIプロセスはsocket,connect,write などに相当するシステムコールを発行します.
宛先のホスト名が必要ならDNS問い合わせも発生します.
改めて見てみるとこの回答はプロセッサの視点というよりはOSの視点なような...
もう少しプロセッサの視点に寄せるなら以下のようなことが書けそうです.
- CLIプロセスを実行しているCPUコアが
ecall命令を実行する - CPUはユーザーモードからカーネルモードへ遷移する
- カーネルが必要な処理を行う
- 完了すると
sret命令によってユーザーモードへ戻る
HTTPSで通信する場合,TLSによる暗号化も行われます.
CPUはリクエスト本文を暗号化し,パケットとして送れる形にします.
CPUはNICに送信バッファの場所を教え,送信を指示します.
APIからの応答は,再びネットワークを通ってユーザーのPCへ戻ります.
PC側のNICがパケットを受け取ると,割り込みやポーリングによってOSが受信を処理します.
CPUでTLSの復号を行い,CLIプロセスが読めるデータとしてソケットバッファに置きます.
スケジューラがCLIプロセスをCPUに割り当てると,CLIは受信した応答を処理します.
ストリーミング形式なら,受け取った断片ごとにJSONやイベント形式を解析し,表示用の文字列に変換します.
今回のプロンプトは「このディレクトリ以下の構造を把握して」という内容なので,CLI は find . や ls -R などのコマンドを実行してディレクトリ構造を取得します.
内部ではopendir,readdir,statなどに相当するAPI を利用し,OSカーネルを通してファイルシステム情報を取得します.
コマンドの実行結果を再びAPIに送り,その結果を処理することを複数回繰り返したのち,最終的にCLIが画面に出力する段階になります.
CLIが画面に出力するときは,標準出力へwriteします.
標準出力はptyにつながっているため,OSカーネルを経由してターミナルエミュレータへデータが渡されます.
よく考えたら find . などのコマンドの実行中にもどのようなコマンドが実行されたかが,表示されるはずですし,Chain-of-Thought の間にも思考内容が表示されるはずですね...
まあ表示内容自体は非本質なので...
GUI環境では,ターミナルエミュレータがOSの描画APIに描画を依頼します.
CPUは文字のレイアウト,フォント処理,描画コマンドの準備などを行い,GPUに描画を依頼します.
結果として,ターミナル上にcoding agentの応答が表示されます.
Q1.2 に関しては特に粗が多そうなのであまり参考にすべきではないかも.
他のゼミの方々の応募課題晒しを見るとしっかり分析して書かれているものが多数あり,そちらを参考にした方がよさそうです.
例えば, セキュリティキャンプ2026 CDN 自作ゼミ応募課題晒し など.
Q2 (選択・CPU自作)
Q2は難しい問題が多いので,すべてを完璧に回答できる必要はありません.調べても分からないことがあれば「どこまで調べて,何が分からなかったか」を書いてください.
Q2.2,Q2.3,Q2.4は,CPUが命令を処理する仕組みと構造の観点から回答してください.
Q2.1
CPU自作班では受講者がそれぞれ好きなISAのCPUを実装し,高速化や機能の追加に取り組んでもらう予定です.
あなたが実装したいCPUの種類,使用するプログラミング言語,ハードウェア記述言語を教えてください.
講師が予習するためにも,詳しく教えてください.
RISC-V 64bitをターゲットとしたCPUを実装したいと考えています.
私は今まで x86-64 をターゲットとしたアプリケーションを作ることが多かったのですが,x86-64 では比較的よく使う命令についても仕様に拡張が繰り替えされており,命令セットが複雑になってしまっていると感じています.
RISC-Vは命令セットがシンプルで,基本的な命令セットに加えて必要な機能を拡張命令セットとして追加できるようになっているため,CPUの構造を理解するのに適していると考えています.
CPUの実装には Veryl を使用し,「Verylで作るCPU」をベースに拡張していきたいと考えています.
SIMDのような拡張命令やセキュリティの保護機能のための命令など実用性の高い命令を追加していきたいです.
また,テストやシミュレーションには適宜 Python や C++ を使用したいと考えています.
ここはただのお気持ち表明をしているだけなので特にコメントはないです.
Q2.2
同じプログラムを実行した場合であっても,昔の高性能なCPUと比較して,最新の高性能なCPUの方が実行時間が基本的に短くなっています.
何が差異を生み出しているのでしょうか?
比較する年代,技術や手法を明示したうえで,CPUで命令が実行される手順に注目して説明してください.
「同じプログラム」はシングルスレッドで動作するx86 命令を実行するプログラムを想定して説明します.
1995年発表のIntel Pentium Proと2025年発表のAMD Ryzen AI 7 PRO 350を比較して説明しようと思います.
余談ですが,AMD Ryzen AI 7 PRO 350 は自分が今メインで利用している ThinkPad X13 Gen6 に搭載されているものです.
Intel Pentium Pro は Ryzen AI 7 PRO 350 の 30年前に発表されたCPU で比較としてちょうどいいかなと思い選びました.
この Pentium は,Out of Order 実行や,それに関連するレジスタリネーミング,x86命令のμopへの変換などが初めて導入されたCPUらしいです.
Intel Pentium Pro は 1 コア 1スレッドで周波数は最大 200MHz, キャッシュはL1 16KB, L2 256KB~1MBです.
一方で,AMD Ryzen AI 7 PRO 350 は 高性能コアが4 コア, 高効率コアが 4コアで合計 16スレッド,周波数は高性能コアが最大 5.0GHzで高効率コアが最大 3.5GHz, キャッシュはL1 80KB, L2 8MB, L3 16MBです.
実行時間には単純なクロック周波数の差だけでなく,キャッシュのサイズや分岐予測の性能,デコード速度,Out-of-Order実行の性能など様々な要素が影響しています.
命令フェッチの段階では,Q2.3 で説明する分岐予測やの性能や命令キャッシュのサイズが大きく向上しています.
具体的には,Pentium ProのL1命令キャッシュは8KBでしたが,Ryzen AI 7 PRO 350の高性能コアではL1命令キャッシュが32KBです.
このため,Pentium Pro よりも Ryzen AI 7 PRO 350 の方が次に実行する命令列を高い精度で予測し,高速にフェッチできるようになっています.
次に,デコードの段階でも差があります.
x86命令はCISCの思想を持っており,複雑で長さが可変なので,CPU内部では扱いやすいμopに変換されます.
Pentium Proはこのμop方式を採用した先進的なCPUで最大3命令/サイクルをデコードできましたが,Ryzen AI 7 PRO 350では1サイクルでデコードできる命令数がさらに増加し,さらに一度デコードしたμopを再利用するop cacheも備えています.
そのため,同じループを何度も実行する場合,命令を毎回デコードし直す必要が減るため,実行ユニットへ命令を安定して供給できます.
さらに,CPUは命令をそのままの順番で処理するわけではなく,ある命令がメモリアクセス待ちで止まっていても,別の独立した命令を先に進められるように Out-of-Order実行を行います.
Pentium Proもアウトオブオーダ実行を備えていましたが,Ryzen AI 7 PRO 350では同時に保持できる命令数や,1サイクルに処理できる命令数が大きく増えています.
メモリアクセスの段階でも差は大きいです.
CPUの演算器は非常に高速ですが,DRAMへのアクセスは相対的に遅いため,キャッシュの性能が重要になります.
Pentium ProはL1 16KBとL2 256KB~1MBを持っていましたが,Ryzen AI 7 PRO 350はコアあたりのL1に加え,合計8MBのL2,16MBのL3を備えています.
キャッシュに収まるデータが増えるほど,低速なDRAMへアクセスする回数が減り,実行時間が短くなります.
また,今回は同一のx86 命令を実行するプログラムという仮定を置きましたが,命令セットの拡張も性能差に大きく寄与しています.
Pentium Proは整数演算や浮動小数点演算を並列に実行できましたが,SSEやAVXのようなSIMD命令には対応していません.
一方,Ryzen AI 7 PRO 350はAVX2やAVX-512系の命令に対応しており,複数のデータを1命令でまとめて処理できます.
現代的なプログラミング言語における配列処理や画像処理,暗号処理,数値計算などでは命令数そのものを減らせる場合が多くあると考えられます.
Q2.3
分岐予測,投機的実行,Out-of-Order実行の中から1つ選んで,どのような技術かを簡単に説明してください.
また,その動作を観測するプログラムを書いて,プログラムと実行環境,結果と考察を教えてください.
観測できなかった場合は,なぜ観測できなかったか,どうすれば観測できそうかを考察してください.
分岐予測とは,CPUが条件分岐の結果を実際に確定する前に予測し,次に実行する命令列を先読みする技術です.
たとえばC言語のif文では,条件式の評価結果によって次に実行する命令が変わります.
このときCPUが条件判定の完了を毎回待っていると,パイプラインで先に命令を処理しておくことができず,性能が低下してしまう.
そこでCPUは,過去の分岐履歴などを使って,次はtrueになりそうか,falseになりそうかを予測します.
予測が当たれば,パイプラインを止めずに処理を進められます.
一方,予測が外れると,誤った経路で先に進めていた命令を破棄し,正しい経路からやり直す必要があります.
そのため,分岐予測ミスが多いプログラムでは実行時間が長くなります.
現代のCPUの分岐予測では,ある分岐についての前回の分岐結果だけでなく,過去数回分の分岐結果や他の分岐の結果などももとにして,次の分岐の結果を予測しています.
以下が分岐予測の効果を観測するための作成したCプログラムです.
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define N 100000000
uint8_t data[N];
double now_sec(void) {
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return (double)ts.tv_sec + (double)ts.tv_nsec * 1e-9;
}
uint64_t branch_test(void) {
uint64_t sum = 0;
for (size_t i = 0; i < N; i++) {
if (data[i]) {
sum += 1;
} else {
sum += 2;
}
}
return sum;
}
void fill_predictable(void) {
for (size_t i = 0; i < N; i++) {
data[i] = i % 2;
}
}
void fill_unpredictable(void) {
uint32_t x = 23456789u;
for (size_t i = 0; i < N; i++) {
x ^= x << 13;
x ^= x >> 17;
x ^= x << 5;
data[i] = x & 1;
}
}
int main(void) {
double start, end;
uint64_t result;
fill_predictable();
start = now_sec();
result = branch_test();
end = now_sec();
printf("predictable: result = %llu, time = %.6f sec\n",
(unsigned long long)result, end - start);
fill_unpredictable();
start = now_sec();
result = branch_test();
end = now_sec();
printf("unpredictable: result = %llu, time = %.6f sec\n",
(unsigned long long)result, end - start);
return 0;
}
このプログラムでは fill_predictable の周期は2としていますが,手元だと周期が2^14くらいまではしっかり予測できていました.
動的分岐予測(おそらくTAGE) すごすぎる.
このプログラムでは,data 配列の内容が短い周期で繰り返しており予測可能な場合と,周期がとても長く予測不可能な場合で,分岐予測の効果を観測しています.
fill_predictable では,分岐予測が当たりやすいように data[i] が0と1を交互に取るように設計しました.
一方,fill_unpredictable では,分岐予測が当たりにくいように data[i] がほとんどランダム(周期が2^32-1)に0と1を取るように設計しました.
-O2, -fno-tree-vectorize, -fno-if-conversion, -fno-if-conversion2 などのオプションをつけてコンパイルしました.
これにより,ループのベクトル化や,条件分岐の条件付き代入・算術式への変換を抑制し,分岐命令が残る形で測定しやすくしています.
CPUの世代差を比較するために2種類の実行環境で実行した結果はそれぞれ以下のようになりました.
- CPU: Intel Core i7 10510U
- OS: Almalinux 9.7
- Compiler: gcc 11.5.0
predictable: result = 150000000, time = 0.064235 sec
unpredictable: result = 150001653, time = 0.314644 sec
- CPU: AMD Ryzen AI 7 PRO 350
- OS: Ubuntu 24.04 LTS on Windows 11 25H2 (WSL2)
- Compiler: gcc 13.3.0
predictable: result = 150000000, time = 0.023595 sec
unpredictable: result = 150001653, time = 0.312772 sec
どちらの環境でも,予測可能な分岐では高速に実行され,予測しにくい分岐では遅くなりました.
このことから,分岐予測が成功するかどうかが実行時間に大きく影響することを観測できました.
また,unpredictable の実行時間は両環境でほぼ同じであったにも関わらず,predictable の実行時間は AMD Ryzen AI 7 PRO 350 のほうが Intel Core i7-10510U よりも約3倍高速でした.
このことから,unpredictableでは分岐予測ミスからの回復コストが支配的になっているのに対し,predictable では単純な命令の処理性能が支配的になっており,単純な命令の処理性能は AMD Ryzen AI 7 PRO 350 のほうが高いと考えられます.
Q2.4
任意のISAの中から定義を1つ選んで,そのように定義されている理由や経緯を教えてください.
その定義を変えればCPUを高速化できそうかを考察してください.
ISAはあなたしか知らない自作のISAでも構いませんが,その場合は仕様書にアクセスする方法を教えてください.
自分が最も触れてきた時間の長い x86-64 について説明します.
x86-64 は, 16 bit の8086 から始まったx86アーキテクチャを32bit 化し,さらに64bit 化したもので,IntelとAMDが主に開発してきました.
特徴としては16bitの時代から基本的に後方互換性を失うことなく新機能を追加してきたことが挙げられます.
そのため,環境さえ整えば近年のx86-64 CPUでも 16bit 8086 の命令で構成されたプログラムを実行できます.
その一方で,この後方互換性維持のために,命令セットはかなり複雑になっており,近年は AArch64 をはじめとするRISC系のアーキテクチャに性能面で遅れを取っていると感じられることもあります.
ここではx86-64の定義の中から,命令長の可変性について定義を変えればCPUを高速化できそうかを考察しようと思います.
x86-64の命令長は可変長であり,8086からある1バイトの命令からSIMD拡張命令などに使われる15バイトの命令まで様々です.
これは上記で述べた通り後方互換性を保ちつつ,32-bit化,64-bit化,拡張命令群を後から追加してきた結果です.
可変長命令はコード密度が高く,命令キャッシュやメモリ帯域には有利ですが,命令境界を見つけるデコードが難しく,フロントエンドの設計が複雑になるため,性能面では不利になることもあります.
この定義を固定長命令に変えれば,デコーダはかなり単純にすることができます.
RISC-VやAArch64のように32 bit固定長を基本にすれば,命令境界の検出,並列デコード,分岐先デコードが簡単になります.
一部のみを短縮命令として16 bit にしたとしても,デコードの複雑さは大幅に減ると考えられます.
一方で,現代のx86-64 CPUはデコード後に内部でμopに変換し,μop cacheやloop bufferを使ってデコード負荷を軽減しているため,固定長化のメリットが相対的に小さくなっています.
したがって,固定長化はフロントエンドには効きますが,既存x86-64 CPU全体の性能を大きく向上させるための変更としては費用対効果が悪いと考えられます.
他にも,非アラインメモリアクセスを禁止することにより多少の性能向上は見込めるかもしれませんが,既存のコードを活用することができなくなり,それをするならば他のアーキテクチャに移行する方が現実的な選択肢になると考えられます.
また,レジスタの数を増やすことも性能向上に寄与する可能性がありますが,命令prefixをさらに追加する必要が有り,デコードが複雑になってしますため,そこまで大きな性能向上は見込めないと考えられます.
x86-64 の様々な定義について調べていてわかったこととしては,x86-64 は既に互換性を犠牲にせずに性能を向上させるための様々な工夫がされているため,定義を変えることで大幅に高速化できる余地はあまりないのではないかということです.
例えば,32bit から64bitに拡張する際に,32bitレジスタへの書き込みが上位32 bitをゼロクリアするという定義を追加することで,64bitレジスタの上位32 bitを気にせずに32bit命令を使えるようにしており,無駄な命令を減らしているため性能が向上していると考えられます.
x86-64 といえば命令が可変長!ということでこの特徴を選びましたが,実際にそれによってどのようなメリットやデメリットが発生しているのかということについては全く詳しくなかったので頑張って調べました...
Q2.5
今までに作ったり,書いたことがあるCPU(シミュレータや周辺部品でも可)について,そのテスト手法を教えてください.
テストは何を対象にして何を検証したのか,今後テスト手法を改善するならどうするかを書いてください.
まだCPUを作ったことがない場合は,CPUをハードウェア記述言語で書くにあたって,どのように設計してテストするかの計画を立てて教えてください.
「Verylで作るCPU」を読み,流れに沿ってCPUの設計とテストについて学びました.
ただし,実際に自力でオリジナルなCPUを設計してテストしたことはないため,ここではCPUをハードウェア記述言語で書くにあたっての設計とテストの計画を立てて説明します.
まず,早い段階までに「Verylで作るCPU」第III部までのCPUを実装してみたいと考えています.
今回,自分で設計した CPU の上で自分が設計した OS を動かしたい,という目標を掲げたため,そのあとは自分のOSを動かすために必要な機能を追加していきたいと考えています.
ただし,「Verylで作るCPU」第III部ではLinux を動かすための機能までは実装されているため,いくつかの機能を整えれば自作のOSを動かすことはできると考えています.
テストに関しても,まずは「Verylで作るCPU」のテストコードを参考にしながら進めていく予定です.
基本的なALU,レジスタファイル,命令デコーダ,メモリインターフェースなどの小さいモジュール単位でのテストや,命令単位のテストで基本的な命令が正しく動作することを確認するとともに,riscv-testsを用いて命令セットの組み合わせでも正しく動作することを確認していきたいと考えています.
ただし,これらのテストですべての機能が網羅できるわけではないため,OSを動かすよりも前の段階でも,OSを動かすことを意図したテストコード(例外や割り込み,外部デバイスのテストなど)を作成していきたいと考えています.
最終的には,CPUの上でOSのテストを動かすことで,CPU全体およびOSが正しく動作することを確認したいと考えています.
至らない点もあるかもしれませんが,このゼミで自分だけのCPUを設計できることを楽しみにしております.
よろしくお願いいたします.
ここもお気持ち表明なので特にコメントはなし.
おわりに
改めて読み直してみると粗が多く,よくこれで通ったなという気持ちしかないです...
一方で,約2ヶ月間の事前学習と1週間のキャンプ中でCPUを実際に実装し,CPUの様々な高速化手法を教えていただいたことにより,少しはCPUの気持ちを理解することができました.
この文章を読んで,「なんだ,この程度の回答でも通過できるのか」と思った方も多いかと思います.
応募課題を出せば参加できる可能性が0%にならずに済むので,少しでも迷っているならぜひ気楽に応募してみてください!
重ね重ねにはなりますが,参加記も執筆したので是非ご覧ください. 執筆中です.