C言語とお付き合いし始めた話
こんばんは。JTC勤務で最近、組込み系の開発の部署に配属されました「にこ」です。
いままでpythonメインだったため、組込み言語で初めてふれたメモリ管理について学習のためまとめることにしました。
メモリ割り当てを意識したきっかけ
上級言語(python,ruby,javaなど)に触れているとGarbageCollectionが働いてメモリについては自動的に管理されます。そもそもpandasやseleniumでデータ加工やWebスクレイピングに使用するのがメインだとメモリ管理に意識を向ける必要もほとんどありません。(list型を複製しようとしたときにはじめてメモリ管理に近いものに触れましたが…)
GarbageCollection: プログラムが使用しなくなったメモリ領域を自動で解放する仕組み
一方、下級言語もといC言語に触れると変数のポインタとアドレスに突き当たります。このポインタ変数を通してメモリ上での変数の扱いをイメージすることが求められます。
そしてポインタ変数の初期化時に関係してくるのが動的メモリ割り当てです。(mallocなど)
この動的メモリ割り当ての方法ですがmalloc以外にもvmallocやkmalloc,kzllocなどほかにも色々な種類が存在し、
このメモリ割り当ての種類などについて今回触れていきます。
メモリ割り当ての種類
コンピュータのメモリ空間は、大きく分けて2層構造になっており、メモリの割り当てを考える際にまずどちらの空間で動作するのかから考えます。
- ユーザー空間:アプリケーション(ユーザーが直接動かすプログラム)が動く場所
- カーネル空間:OSの中核(カーネル)が動く場所。ハードウェアを直接制御する領域
以下はChatGPTに生成してもらった、各空間別の動的メモリ割り当ての方法の意思決定フローチャートです。
- ユーザー空間
- カーネル空間
アライメント
キーワードは知りつつ実践するタイミングを逃していたのがこのアライメントです。このアライメントを説明するにはまずC言語のstructureについて知る必要があります。structureはいくつかの変数を一つにまとめたオブジェクトです。また変数にはそれぞれ使用するbyte数が決められていて、structureはこのbyte数以上のサイズとなります。ただしデータは2のべき乗数単位で確保されて、構造体内のデータのうちこの倍数に収まらないと余分にデータ領域を確保されてしまいます。
aligned_allocではこのアライメントの制約を化してSIMD、GPU用途に向けてデータを最適化するときに使用します。
SIMD: 一つの命令で複数のデータを同時に処理する並列処理技術
このアライメントに収めるためには各変数のサイズを考えてサイズを整える必要があります。
アーキテクチャで変動するデータサイズ
変数のデータサイズについて一部アーキテクチャのbit数によってサイズが変動してしまいます。
| 型 | 典型的サイズ (32bit) | 典型的サイズ (64bit) | 備考 |
|---|---|---|---|
char |
1 byte | 1 byte | 文字型、sizeof(char) == 1 は規格上必須 |
signed char |
1 byte | 1 byte |
char と同じだが符号あり |
unsigned char |
1 byte | 1 byte | 符号なし |
short / short int
|
2 bytes | 2 bytes | 符号あり、short <= int
|
unsigned short |
2 bytes | 2 bytes | 符号なし |
int |
4 bytes | 4 bytes | 符号あり、環境によっては2や8のこともある |
unsigned int |
4 bytes | 4 bytes | 符号なし |
long / long int
|
4 bytes (32bit) | 8 bytes (64bit Linux) | LP64環境では64bit、Windowsは32bit |
unsigned long |
4 bytes | 8 bytes | 符号なし |
long long / long long int
|
8 bytes | 8 bytes | C99で導入、ほぼすべての環境で8 |
unsigned long long |
8 bytes | 8 bytes | 符号なし |
float |
4 bytes | 4 bytes | IEEE 754 単精度 |
double |
8 bytes | 8 bytes | IEEE 754 倍精度 |
long double |
8, 12, 16 bytes | 16 bytes (Linux x86_64) | 環境依存、x86では80bit拡張精度が多い |
_Bool |
1 byte | 1 byte | C99以降、0/1を保持 |
size_t |
4 bytes | 8 bytes | 無符号型、メモリサイズを表す |
ptrdiff_t |
4 bytes | 8 bytes | ポインタ差の型、符号あり |
※ChatGPT出力の表
※long double(32bit)についてはコンパイラ依存でサイズが決まるため3通り存在
まとめ
今回はフローチャートを作成したため、これを参考に実装にいかしていきたいです。次回はハード面にも触れつつCとのてぇてぇな話をかけたらと思います。
ご覧いただきありがとうございました。
本内容はGPT出力と別途調べたりもしていますが誤りなどあれば随時指摘お願いいたします。