基本情報技術者試験問題に挑戦しよう!「主記憶の実効アクセス時間」~C言語でつくって学ぶ~【GitHub対応】

パソコン C言語

基本情報技術者試験とは

基本情報技術者試験は、情報処理推進機構(IPA)が実施し、「情報処理の促進に関する法律」に基づき経済産業省が認定する国家試験である。

ITエンジニアとしてキャリアをスタートする方のIT基礎力を問う試験で、「ITエンジニアの登竜門」とも言われる。
「基本情報技術者試験」の試験範囲は、IPAのWebサイトに掲載されているシラバスに明記されている。


それでは、基本情報技術者試験(科目A)のサンプル問題にチャレンジしてみよう。

サンプル問題

問12

A ~ D を,主記憶の実効アクセス時間が短い順に並べたものはどれか。

ア A,B,C,D

イ A,D,B,C

ウ C,D,A,B

エ D,C,A,B

正解:イ

キャッシュメモリ

キャッシュメモリとは、CPUと主記憶装置の間にある超高速の記憶装置である。
CPUと主記憶の処理速度の差を埋めるためにキャッシュメモリを使用して処理の待ち時間を短縮する。
必要なデータがキャッシュメモリ上に配置されている確率のことを「ヒット率」という。
一方、キャッシュメモリ上に必要なデータが配置されていない確率は「1-ヒット率」になる。

実効アクセス時間

キャッシュメモリを利用したときのアクセス時間を「実効アクセス時間」という。

実効アクセス時間の計算

実効アクセス時間 = キャッシュメモリへのアクセス時間 × ヒット率 + 主記憶装置へのアクセス時間 ×(1-ヒット率)

AとBはキャッシュメモリがないので、実行アクセス時間=主記憶装置のアクセス時間となる。
CとDの実行アクセス時間は、
C (20×0.6) + 70×(1-0.6) = 40
D (10×0.9) + 80×(1-0.9) = 17

したがって、主記憶の実効アクセス時間が短い順に並べると、
A(15)、D(17)、B(30)、C(40)となる。


(以下、2026.9.23追記)

今回は、科目Aのサンプル問題を題材に、答えを覚えるのではなく、C言語の短いプログラムを実際に動かして「配列へのアクセス方法によって速度が変わる」ことを体感するという進め方で解説する。

なお、この記事で使用するコードは、筆者のリポジトリ pc-labo-fe-exam-c の 01_cache_access フォルダにまとめている。 ご自分で gcc -O2 -o cache_access cache_access.c && ./cache_access を実行すれば、そのまま同じ傾向の結果を確認できる。

C言語でつくって確かめてみよう

ヒット率そのものをプログラムで測ることは難しいが、ヒット率が高くなりやすいアクセス方法/低くなりやすいアクセス方法を作り、実行時間を比較することならできる。

考え方はシンプルである。

  1. 大きな配列を先頭から順番に(1個ずつ隣へ)アクセスする → 実行時間を測定
  2. 同じ配列を飛び飛びに(ストライドを空けて)アクセスする → 実行時間を測定
  3. 両者の実行時間を比較する

CPUキャッシュは、一度読み込んだデータの近くのデータもまとめてキャッシュに乗せる性質(空間的局所性)を持つ。そのため、

  • 順番にアクセスする場合 → 一度読み込んだキャッシュラインを効率よく使い切れる(ヒット率が高いD寄りの状態)
  • 飛び飛びにアクセスする場合 → アクセスのたびに新しいキャッシュラインを読み込む必要が出やすい(ヒット率が低いC寄り、あるいはそれ以上に悪い状態)

以下のプログラムを、GitHubのCodespacesでC言語が使える環境を構築して実行してみよう。
なお、GitHubについては、こちらの記事を参照してほしい。

C言語(全体)

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define SIZE 10000000  /* 配列のサイズ(1000万要素、約40MB) */
#define STRIDE 64      /* ストライド(飛び飛びにアクセスする間隔) */

int main(void) {
    int *array = malloc(sizeof(int) * SIZE);
    if (array == NULL) {
        perror("malloc");
        return 1;
    }

    for (int i = 0; i < SIZE; i++) {
        array[i] = i;
    }

    long long sum = 0;
    clock_t start, end;

    /* ① 順番にアクセス(ストライド1) */
    start = clock();
    for (int i = 0; i < SIZE; i++) {
        sum += array[i];
    }
    end = clock();
    double sequential_time = (double)(end - start) / CLOCKS_PER_SEC;
    printf("順次アクセス      : sum = %lld, 実行時間 = %f 秒\n", sum, sequential_time);

    /* ② 飛び飛びにアクセス(ストライドSTRIDE、アクセス総数は①と同じ) */
    sum = 0;
    start = clock();
    for (int offset = 0; offset < STRIDE; offset++) {
        for (int i = offset; i < SIZE; i += STRIDE) {
            sum += array[i];
        }
    }
    end = clock();
    double strided_time = (double)(end - start) / CLOCKS_PER_SEC;
    printf("ストライドアクセス: sum = %lld, 実行時間 = %f 秒\n", sum, strided_time);

    printf("倍率:ストライドアクセスは順次アクセスの約 %.2f 倍\n", strided_time / sequential_time);

    free(array);
    return 0;
}

②のループは、一見複雑に見えるかもしれないが、やっていることは単純である。offset を 0 から STRIDE-1 まで変えながら、STRIDE 個おきに配列全体をなめている。
つまり①と②でアクセスする要素の総数は同じであり、違うのは「アクセスする順番」だけである。

実行結果

順次アクセス      : sum = 49999995000000, 実行時間 = 0.002302 秒
ストライドアクセス: sum = 49999995000000, 実行時間 = 0.039887 秒
倍率:ストライドアクセスは順次アクセスの約 17.33 倍

同じ回数だけ配列にアクセスしているにもかかわらず、飛び飛びにアクセスした方が10倍以上遅いという結果になった。(倍率は実行環境によって変動するが、通常、順次アクセスの方が明確に高速になる。)

  • 順次アクセスでは、一度読み込んだキャッシュライン上のデータをそのまま使い切れるため、ヒット率が高い状態に近い
  • ストライドアクセスでは、アクセスするたびに別のキャッシュラインを読み込み直す必要が生じやすく、ヒット率が低い状態に近い

キャッシュの空間的局所性がプログラムの実行時間に影響することを体験できた。

なぜC言語なのか(PythonやJavaScriptとの比較)

今回の題材は元々Pythonでの実装も検討したが、結論としてはこのテーマにはC言語が最も相性が良いと判断した。

参考までに、同じロジックをPythonで実装して実行してみたところ、次のような結果になった。

順次アクセス      : sum=1999999000000, 実行時間=0.131607秒
ストライドアクセス: sum=1999999000000, 実行時間=0.165444秒
倍率:1.26倍

C言語では10倍以上の差が出たのに対し、Pythonでは1.26倍程度の差しか現れなかった。これは、Pythonの1回のループ処理そのものにかかるオーバーヘッドが大きく、キャッシュヒット・ミスによる数ナノ秒単位の差を覆い隠してしまうためである。また、Pythonのリストは要素そのものではなく要素へのポインタの並びであり、C言語の配列のようにメモリ上に隙間なく連続して並んでいるわけではないという違いも影響している。JavaScriptも、実行エンジン(V8など)による最適化やJITコンパイルの挙動が絡むため、同様に結果が安定しにくい。

「メモリ上に連続して並んだデータに、直接アクセスする」というC言語の特性が、今回のようなハードウェアに近い現象を確かめるテーマには向いている、というのが実験を通じての結論である。

まとめ

  • 実効アクセス時間 = キャッシュメモリへのアクセス時間 × ヒット率 + 主記憶装置へのアクセス時間 ×(1-ヒット率)
  • ヒット率が高いほど実効アクセス時間は短くなる、という関係は式の上だけでなく、実際のプログラムの実行時間の差としても確認できる
  • 配列への「順次アクセス」と「ストライドアクセス」を比較すると、同じ回数のアクセスでも実行時間に大きな差が生まれる
  • この種のハードウェアに近いテーマは、メモリレイアウトを直接扱えるC言語との相性が良い

暗記だけでなく、短いコードを1本つくって動かしてみることで、キャッシュメモリの意味が体感として残りやすくなるだろう。

(参考)筆者のリポジトリ

この記事で扱ったコードは、以下のGitHubリポジトリで公開している。今後、C言語で扱う「基本情報技術者試験問題に挑戦しよう!」シリーズのコードも、同じリポジトリにフォルダを追加していく形でまとめていく予定である。

GitHub – HappyTalk10/pc-labo-fe-exam-c

コメント

タイトルとURLをコピーしました