NEOREX

CPUの挙動まで意識したプログラミングとは

CPUの挙動まで意識したプログラミングとは

こんにちは!ネオレックスの採用担当です。
ネオレックスは、高い技術力を発揮し、「自分たちで考えたものを、自分たちの手で生み出す」ことにこだわりを持った会社です。

高い技術力といわれても、イメージしにくい部分があるかと思います。
そこで本コラムでは、当社の技術メンバーが大切にしている考え方「CPUの挙動まで意識したプログラミング」を紹介します。

本コラムを通じて、ネオレックスの高い技術力、そして、考え方を感じ取ってもらえたら嬉しいです。

ーーーーーーーーー
メッセージを表示させたり、何かを計算させたり、…と、コンピュータに何かをお願いしたいとき、専用の言語、つまり「プログラミング言語」が必要です。

一言でプログラミング言語といっても、実はたくさんの種類があります。

  • マシン語:
  • コンピューターが直接理解できる、0と1のみで構成された言語。

  • アセンブリ言語:
  • マシン語を扱いやすくするために作られたプログラミング言語。
    マシン語と1対1で対応していて、簡単にマシン語へ変換できる。

  • 高水準言語(C、Python、Javascript…):
  • 英語に似た形式の言語で、人間目線でアセンブリ言語以上に記述・読解しやすい言語。コンピューターで実行するために、アセンブリ言語やマシン語へ変換される(どのように変換され、コンピューターで実行されるかは言語によって異なります)。

    プログラミングに興味を持ったことがある人の多くはきっと、扱いやすい高水準言語を学ぼうとしたのではないでしょうか。
    ネオレックスの技術メンバーは高水準言語の知識を駆使するだけでなく、アセンブリ言語やマシン語、CPUの実際の挙動まで意識をしながらプログラムを書いています。

    例えば、コンピューターに計算などの処理をさせようとするとき、それを実現するためのプログラムの書き方(コーディング)は何通りもあります。
    どれを採用しても同じ結果が得られますが、CPUが実際に処理する分量=動作速度の観点ではかなり違ってくることがあります。

    以下のお題について考えてみたいと思います。

    お題「変数aの値を15倍して変数bに格納する」

    実は「15倍する」という計算は、コンピュータは元々それほど得意ではありません。
    でも「16倍する」という計算は得意なんです。
    私たちは普段、数を10進数で扱いますが、コンピュータは数を2進数で持ちます。
    10進数では、1を、

      • 10倍すると「10」に、
      • 100倍すると「100」に、
      • 1000倍すると「1000」に、

    となります。
    計算するというより、桁をずらして後ろに0を補うだけですね。

    同じように、2進数では、1を、

      • 2倍すると「10」に、
      • 4倍すると「100」に、
      • 8倍すると「1000」に、
      • 16倍すると「10000」に、

    という風に、2の累乗の計算は桁をずらして後ろに0を補うだけということになりますので、コンピュータはこういう計算が得意なのです。

    例えば「a = 5」とすると、2進数では「101」ですので、16倍は後ろに0を4つ補って「1010000」となります。 お題は15倍ですので、この結果からaを1つ分引いて15倍にします。

    私たちが暗算する時に「8 × 999」を「8 × 1000から8を引く」とすると簡単になるのと同じ考え方です。

    動作速度を比較

    このコーディングの違いが具体的にどの程度の差になるかを見るために、C言語で書いたコードがどのようにコンパイルされるかを表にしてみました。C言語、アセンブリ言語になじみのない方は「実行サイクル」だけを見ていただければと思います。実行サイクルは各行の命令を実行するために掛かる時間を表しています。

    共通条件

    •プログラムの開始番地:0x00000100
    •変数aのメモリ番地:0x10203040
    •変数bのメモリ番地:0x10203044

    お題をそのままコーディングした場合

    16倍してからaを引く場合

    マシン語のダンプリスト比較

    プログラムのサイズは偶然どちらも17byteと同じですが、実行速度は平均で2.4倍も速くなることが期待できます。

    このように、同じ処理であってもプログラムの書き方次第で動作速度が大幅に違ってくることがあるため、コンパイルされたプログラムがどのようなマシン語となり、その処理が何サイクル程度になるのかというCPUの実際の挙動への意識が時に大変重要になります。

    最近は、強力な最適化機能を持ち、こうした配慮があまり必要ないコンパイラも出てきています。また、一部の処理の実行サイクルを大幅に短縮したCPUも出てきています。
    しかし、プログラムが最終的にマシン語となり、そのマシン語がCPUによって処理されるという原理には変化はありません。

    「マシン語やCPUへの配慮により、より良いシステムを開発することができる。あるいは、こうした配慮がなければ、思わぬパフォーマンスダウンや不具合に見舞われることがある。」

    私たちネオレックスは、このような思想を持って毎日の開発に取り組んでいます。

    今回のコラムでは、分かりやすさを優先し、インテルi386プロセッサとその当時のコンパイラという少し古いものの挙動をベースにまとめています。
    本文中にあるように、最近のコンパイラやCPUは以前に比べて高性能になっています。例えば「b = a * 15;」とコーディングしても自動的に「b = (a << 4) - a;」と読み替えるコンパイラや、本記事で実行サイクルが9~38となっている「mul ebx」を2~3サイクルで実行できるCPUが登場しています。 このため、CPUやコンパイラの特性や挙動を理解していないと、動作速度を上げるつもりの工夫が逆に動作速度を遅くしてしまう結果となることもあり、注意が必要です。

    2024.11.25

? ? ? ?
クラウド勤怠管理システム キンタイミライ
タブレットタイムレコーダー
新卒採用