なぜ抽象化と速度を犠牲にする必要があるのですか?


11

高水準言語が速度の点で低水準言語に到達できないように見えるのはなぜですか?高水準言語の例としては、Python、Haskell、Javaがあります。低レベル言語を定義するのは難しいですが、Cを例に考えてみましょう。比較はインターネット1のいたるところにあり、Cの方がはるかに高速で、時には10倍以上であることに同意しています。1

そのようなパフォーマンスの大きな違いを引き起こすのは何ですか?なぜ高級言語が追い付かないのですか?

最初はこれがすべてのコンパイラーの責任であり、将来的には改善されると信じていましたが、最も人気のある高水準言語のいくつかは何十年も前から存在しており、速度に関してはまだ遅れています。C構文ツリーと同様にコンパイルして、マシンコードを生成する同じ手順を実行することはできませんか?それとも構文自体に関係があるのでしょうか?


例:1


5
「そして、Cの方がはるかに高速であるということに全員が同意しています」-それは確かに間違っています。
ラファエル

2
とにかく、私は、投稿は同様によく考えられていない質問に対する私の答えによって最もよく答えられると思います。複製?
ラファエル

2
こちらこちらご覧ください。ジャンクの答えに注意してください。
ラファエル

関連:stackoverflow.com/questions/6964392/…すべての「高水準言語」が遅いという神話は非常に悲しいです。
xji

抽象化について他の人に同意します!=遅いですが、コンピュータサイエンスの教師(私も1人でした)が認識していないはるかに大きな問題があると思います。つまり、実際のプログラム(1000行以上のコード)では、多くの方法があり、パフォーマンスは桁違いに異なります。big-Oについて考えるだけでは完全に失敗します。こちらをチェックしてください
Mike Dunlavey 16

回答:


19

いくつかの神話を暴く

  1. 速い言語のようなものはありません。言語は一般的に高速なコードを生成できますが、異なる言語は異なるベンチマークで優れています。特定の欠陥のあるベンチマークセットで言語をランク付けすることはできますが、孤立して言語をランク付けすることはできません。

  2. パフォーマンスを隅々まで必要とする人はCを使用するため、Cコードはより高速になる傾向があります。Cが「10倍」高速であるという統計は、Pythonを使用する人があまり気にしないため、不正確になる可能性があります速度について、最適なPythonコードを記述していません。これは特にHaskellのような言語で見られます。本当に一生懸命試してみれば、Cと同等のパフォーマンスを持つHas​​kellを書くことができます。しかし、ほとんどの人はそのパフォーマンスを必要としないので、比較に欠陥があります。

  3. Cを高速にするのは、抽象化ではなく安全ではない場合があります。配列の境界とnullポインターチェックの欠如は時間を節約し、長年にわたって多くのセキュリティホールの原因となってきました。

  4. 言語は高速ではなく、実装は高速です。速度は目標ではないため、多くの抽象言語は低速で始まりますが、最適化が追加されるにつれて高速になります。

抽象化と速度のトレードオフはおそらく不正確です。私はより良い比較を提案します:

シンプルさ、スピード、抽象化:2つ選択してください。

同一のアルゴリズムを異なる言語で実行している場合、速度の問題は、「これを機能させるために実行時にどれだけのことをする必要があるか」という問題に帰着します。

PythonやJavaScriptなどの単純な高度に抽象化された言語では、実行時までデータの表現がわからないため、実行時に多くのポインタの逆参照と動的チェックが発生し、処理が遅くなります。

同様に、コンピューターを壊さないようにするために行う必要がある多くのチェックがあります。Pythonで関数を呼び出すときは、呼び出すオブジェクトが実際に関数であることを確認する必要があります。そうしないと、ひどいことをするランダムなコードを実行してしまう可能性があります。

最後に、ほとんどの抽象言語には、ガベージコレクションによるオーバーヘッドがあります。ほとんどのプログラマーは、動的に割り当てられたメモリの存続期間を追跡する必要があるのは面倒であり、実行時にガベージコレクターに実行してもらうことに同意しています。これには時間がかかります。CプログラムがGCに費やす必要はありません。

遅いという意味ではない

ただし、抽象的で高速な言語もあります。この時代で最も支配的なのは錆です。借用チェッカーと洗練された型システムを導入することで、抽象コードが可能になり、コンパイル時の情報を使用して、実行時に実行する必要のある作業(つまり、ガベージコレクション)の量を削減します。

静的に型付けされた言語は、ランタイムチェックの数を減らすことで時間を節約し、コンパイル時にタイプチェッカーを要求するように要求することで複雑さを導入します。同様に、値をその型システムにnullにできるかどうかをエンコードする言語がある場合、コンパイル時のnullポインターチェックにより時間を節約できます。


2
「パフォーマンスのあらゆる側面を必要とする人々がCを使用するため、Cコードはより高速になる傾向があります」-正確に。「ZでY年の経験を持つX年の学生/専門家によって書かれたコードの平均実行時間」という形のベンチマークを見たいです。XとYが小さい場合、Cの答えは通常「コードが正しくない」と思います。Cが約束するパフォーマンスの可能性を活用するために必要な経験/専門知識がどれだけあるかを知ることは、非常に興味深いでしょう。
ラファエル

Haskellは、ルールを証明する例外です。;)私はC ++があなたが巣のポインタを共有していない限り、そのスマートポインタによってGC程度ではなく、合理的な妥協点を発見したとC.ほど高速になると思う
Kaveh

0

ここにこれに関するいくつかの重要なアイデアがあります。

  • WRTの抽象化と速度を比較する簡単な比較/ケーススタディは、JavaとC ++です。javaは、メモリ管理などのc ++の下位レベルの側面の一部を抽象化するように設計されています。初期の頃(1990年代中頃に言語が発明された頃)、Javaガベージ検出はそれほど速くありませんでしたが、数十年の研究の後、ガベージコレクターは非常に微調整/高速/最適化されたため、ガベージコレクターはJavaでのパフォーマンスの低下。たとえば、この1998年の見出しもご覧ください。パフォーマンステストは、JavaがC ++ / javaworldと同じくらい高速であることを示しています。

  • プログラミング言語とその長い進化には、一種の超越的な設計パターンとして固有の「ピラミッド型/階層型構造」があります。ピラミッドの上部は、ピラミッドの他の下部を制御するものです。つまり、ビルディングブロックはビルディングブロックから作られます。これは、API構造でも確認できます。この意味で、抽象化が進むと、ピラミッドの最上部にある新しいコンポーネントが常に他のコンポーネントを制御します。つまり、ある意味では、すべての言語がお互いに同じレベルになるということではなく、他の言語のルーチンを必要とする言語です。たとえば、多くのスクリプト言語(python / ruby​​)はCまたはC ++ライブラリを呼び出すことが多く、数値ルーチンまたは行列ルーチンがこの典型的な例です。したがって、高水準言語と低水準言語があり、高水準言語はいわば低水準言語を呼び出します。この意味で、相対速度の測定は実際には比較できません。

  • 抽象化と速度のトレードオフ、つまりその主要な設計目標を最適化するために、常に新しい言語が発明されていると言う人もいるかもしれません。多分それはより多くの抽象化が常に速度を犠牲にするほど多くはありませんが、より良いバランスが常により新しいデザインで求められています。たとえば、Google Goは多くの点でトレードオフを念頭に置いて具体的に最適化され、同時に高レベルとパフォーマンスの両方を実現しました。たとえば、Google Goをご覧ください。Googleのプログラミング言語がエンタープライズ / techworldでJavaに匹敵する理由


0

パフォーマンスについて私が考えたいのは、「ゴムが道に出会うところ」です。コンピュータは、抽象化ではなく命令を実行します。

私が見たいのはこれです:最終結果に実質的に貢献することによって「キープを獲得」して実行されるすべての命令はありますか?単純すぎる例として、1024エントリのテーブルでエントリを検索することを検討してください。プログラムは答えを知る前に10ビットを「学習」する必要があるため、これは10ビットの問題です。アルゴリズムがバイナリ検索の場合、不確実性が2倍に縮小されるため、各反復は1ビットの情報を提供します。したがって、各ビットに1つずつ、10回の反復が必要です。

一方、線形探索は、最初の反復で不確実性が非常に小さい係数で縮小されるため、最初は非常に非効率的です。したがって、彼らは費やされた努力について多くを学んでいない。

OK、つまり、コンパイラがユーザーに「抽象的」と見なされる方法で適切な命令をラップすることを許可できる場合、それは問題ありません。


0

その性質上、抽象化により、プログラマーとシステムの下位層(コンパイラー、ライブラリー、およびランタイムシステム)の両方への情報の伝達が減少します。抽象化を支持して、これは一般に、下位層がプログラマーが指定されていない動作に関与していないと想定できるようにし、指定された動作を提供する際の柔軟性を高めます。

この「ドントケア」の側面からの潜在的な利点の例は、データレイアウトです。C(低抽象化)では、コンパイラーはデータレイアウトの最適化により制約されます。コンパイラーがホット/コールドまたは偽共有回避の最適化が有益であることを(たとえば、プロファイル情報を通じて)識別できたとしても、通常、そのような適用はできません。(「あたかも」のように指定すること、つまり仕様をより抽象的に扱うことにはある程度の自由がありますが、すべての潜在的な副作用を導出すると、コンパイラーに負担がかかります。)

より抽象的な仕様は、トレードオフと用途の変化に対してもより堅牢です。下位層は、新しいシステム特性や新しい用途に合わせてプログラムを再最適化する際の制約が少なくなります。より具体的な仕様は、プログラマーによって書き直されるか、「あたかも」動作を保証するために、下位層によって追加の努力を払わなければなりません。

抽象化を隠す情報のパフォーマンスを妨げる側面は「表現できない」であり、下位層は通常「知らない」として扱います。つまり、下位層は、最適化に役立つ情報を、一般的な一般的な使用、対象を絞った使用、特定のプロファイル情報などの他の手段と区別する必要があります。

情報非表示の影響は、他の方向にも作用します。プログラマは、すべての詳細を考慮して指定する必要がないため、生産性を高めることができますが、より高いレベルの設計選択の影響に関する情報が少なくなる場合があります。

一方、コードがより具体的である(抽象性が低い)場合、システムの下位層は、指示されたとおりに、実行するように指示されたことをより簡単に実行できます。コードがターゲットを絞った用途向けに適切に記述されている場合、ターゲットを絞った用途にうまく適合します。抽象度の低い言語(またはプログラミングパラダイム)を使用すると、プログラマー詳細な設計と、特定の言語では下位層に簡単に伝達されない情報を使用して、実装を最適化できます。

前述のように、追加のプログラマーのスキルと労力が価値のある結果を生み出すことができる場合、抽象度の低い言語(またはプログラミング手法)は魅力的です。より多くのプログラマの努力とスキルが適用されると、結果は通常より良くなります。さらに、パフォーマンスが重要なアプリケーションではあまり使用されない言語システム(開発の努力や信頼性を強調する代わりに、境界チェックとガベージコレクションは、プログラマーの生産性だけでなく、抽象化によってプログラマーのメンタルロードを減らすことで信頼性を向上させることができます)パフォーマンスを改善するためのプレッシャーが少なくなります。

最適化は通常、特定の用途に合わせてコードを調整することで可能になるため、特定性は自分自身を繰り返さないという原則にも反します。これは、信頼性とプログラミング作業に明らかな影響を及ぼします。

言語によって提供される抽象化には、あまり重要でない抽象化を選択する手段がない、望ましくない、または不要な作業が含まれる場合もあります。不必要な作業が下位層によって発見および削除される場合があります(たとえば、境界チェックがループの本体から抽出され、場合によっては完全に削除される場合があります)が、有効な最適化であることを確認するには、次のことにより「スキルと労力」が必要です。コンパイラ。

言語の年齢と人気は、熟練したプログラマの可用性とシステムの下位層の品質(成熟したライブラリとコード例を含む)の両方における注目すべき要素でもあります。

そのような比較におけるもう1つの混乱要因は、事前コンパイルとジャストインタイムコンパイルのやや直交する違いです。ジャストインタイムコンパイルは、プロファイル情報(プログラマーにプロファイルの実行を提供する必要がない)とシステム固有の最適化(事前コンパイルはより広範な互換性を対象とする場合があります)をより簡単に活用できますが、積極的な最適化のオーバーヘッドは次のように説明されます。ランタイムパフォーマンスの一部。JITの結果をキャッシュできるため、一般的に使用されるコードのオーバーヘッドを削減できます。(バイナリ再最適化の代替手段はJITコンパイルのいくつかの利点を提供できますが、従来のバイナリ配布形式はほとんどのソースコード情報をドロップし、システムに特定の実装からの意図を識別しようとする可能性があります。)

(より低い抽象化言語は、プログラマー制御に重点を置いているため、事前コンパイルを使用することを支持しています。リンク時の実装の選択はより優れたプログラマー制御を提供しますが、インストール時コンパイルは許容されます。JITコンパイルは重要な制御を犠牲にします。 )

ベンチマーク方法論の問題もあります。同等の努力/スキルを確立することは事実上不可能ですが、それが達成されたとしても、言語の目標が結果にバイアスをかけることになります。短い最大プログラミング時間が必要な場合、抽象度の低い言語のプログラムは、より抽象度の高い言語での単純な慣用的な式と比較して、完全に書かれていなくても失敗する可能性があります。高い最大プログラミング時間/労力が許されれば、抽象度の低い言語が有利になります。ベストエフォートの結果を示すベンチマークは、抽象度の低い言語を優先する傾向があります。

他のプログラミングパラダイムの利点を得るために、言語であまり慣用的ではない方法でプログラミングすることが可能な場合がありますが、表現力が利用できる場合でも、そうすることのトレードオフは好ましくない場合があります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.