通訳者のボトルネック

このノートは、インタープリター型プログラミング言語をしばらく使用している、または使用し始めたばかりの若いプログラマーを対象としていますが、言語自体の動作原理についてはまだ研究していません。



最近では、給料やオフィスタイプの仕事が悪くない可能性があるため、プログラミングは若者の間で非常に人気があります。 さらに、初期開発に十分なほど複雑ではないプログラミング言語(JavaScript、PHP、Perl、Python、Java、C#、Basicなど)が求められています(おわかりのように、それらはすべて同じファミリー-インタープリターです)。 その結果、この業界では、どこでもプログラミングの特別な訓練を受けていないかなり多くの労働者が現れました。 「X」という言語のプログラマが必要で、「Xを2週間で」という本を購入し、3週間後にはすでに「X」で何らかのプロジェクトを書いています。 そして、数千行のコードの後、またはデータベースが実際のデータでいっぱいになった後、プロジェクトは容赦なくスローダウンし始めます。 もちろん、アイアンがプロジェクトに到達するまで「ドラムを演奏する」ことができますが、このオプションは常にではなく、誰もが満足しているわけではありません。





通常、主な問題は何ですか? 通常、理解がない場合:「Y」コマンドが実行されたときに実際に何が起こるか。 コミュニケーション言語としてのプログラミング言語-同じことを異なる言葉で説明できます。 しかし、コンピューターの場合は、説明ができるだけ簡潔である方が良いでしょう。 「実行速度」という意味で「より良い」。 さらに、簡潔さは、あなたではなく、中央処理装置にとって理解可能な言語のレベルにあるべきです。 特定のプログラミング言語で呼び出す関数の名前の簡潔さは、パフォーマンスに影響を与えません(例外があります)。 パフォーマンスは、この機能が腸で行うことによって影響を受けます。 そのためには、コンピューターが実際に数値、文字列、配列、関数などをどのように処理するかを理解する価値があります。



このメモが大きくなりすぎないように、低レベルでどのように機能するかについてはここでは説明しません。 この説明は言語によって異なる場合があります。 インターネットや書籍で詳細情報を検索したり、自分で調べたりできます。 必要に応じて、コメント内の質問に回答し、それらをすべて別のメモに入れることができます。 それまでの間、一般的な省略から「脚が成長する場所」と注意すべき点についてのみ説明します。



まず、プログラミング言語のクラスを見てみましょう。 私はそれらを3つのグループに分けます(おそらく彼らはそのように分けます):





アセンブラーは、 概して 、中央処理装置自体の言語です。 プログラマーが書いたものは、人間に優しい構文で書かれていますが、出力は同じで、プロセッサーに優しい構文のみです。 一部のアセンブラコンパイラは、この単純な翻訳に加えて、コードを分析し、最適化を試みますが、本質は変わりません。 アセンブラでのプログラミング方法を知っている場合、ハードウェアのすべてのニュアンスを知っているため、最適なソリューションでタスクを実装する機会があります。



コンパイラーは、プログラマーの言語、C、C ++、Pascalなどにより適しています...条件分岐、ループ、変数や関数の操作などが言語の構文に表示されるため、何かを書くのははるかに簡単です。 その結果、複雑なサイクルを実装するために多くのCPU命令を記述する必要がなくなりました。 さらに、CPU(中央処理装置)がまったく認識していないさまざまな設計を実装しています。 しかし、プログラムのロジック(クラス、オブジェクト、レコード、配列など)の構造化がはるかに容易になります。 コンパイル時に、プログラムは特定のCPUが理解できる言語に翻訳されます。 アセンブラーとCPUが理解する言語は本質的に同じであるため、コンパイルされたプログラムをいつでもアセンブラーに転送できます(逆アセンブリー)。 コンパイル済みプログラムをコンパイル済み言語に翻訳することは、はるかに複雑なタスクです。これは、一部のプロセッサー言語構成体が、コンパイル済み言語の簡略化された構文に常に適切に転送できないためです。 さらに、変数と関数の名前はすべてコンパイル中に失われ、それらを復元することはできません(プログラムがデバッグモードでコンパイルされている場合を除く)。



通訳者 -JavaScript、PHP、Perl、Python、Java、C#、Basicなど、これらの言語は進化の最高段階を表しています。その特性は、アプリケーション実行プラットフォームからの潜在的な独立性にあります。



アセンブラで書かれたプログラムは、それらのプロセッサのコマンドによって書かれたものであり、他のプロセッサはそれらを理解できないため、書かれたタイプのプロセッサでのみ動作します。



コンパイルされた言語で書かれたプログラムは、コンパイルされたプラットフォームでのみ機能します。 理論的には、プログラムはさまざまなプラットフォーム用にコンパイルできますが、実際には、必要に応じて、プログラミング段階でも、プログラムがコンパイルされるはずのすべてのプラットフォームの機能を考慮する必要があります。



インタプリタ言語で書かれたプログラムは、特定のプログラム層によって実行されます。プログラム層は、コードをリアルタイムで読み取り、CPUが理解できる言語に変換します。 その結果、インタプリタ開発者はアプリケーションの移植性の問題を取り上げました。 次に、プログラムがすべてのシステムで機能するように、異なるシステム用にこのレイヤーを作成する必要があります。 しかし、すべてのシステムはまったく異なるため、絶対的な独立性を常に実現できるとは限りません。 Linuxに「Z」機能があるが、Windowsには存在しない場合、それなしで実行するか、プログラムがLinuxでのみ機能します(たとえば、ファイルシステムを操作する機能)。



インタープリター言語の主な欠点は、実行速度です。 CPUが理解する言語でコンパイルされたプログラムは、すぐにCPUによって処理されますが、インタープリター言語で書かれたプログラムは最初に認識され、CPUが理解する言語に翻訳され、その後CPUが実行を開始する必要があります 現代の通訳者は、この欠点に対処するための多くの手段を獲得しています。 十分に高品質のオプティマイザーとキャッシュシステムに加えて、プログラムをバイトコードに変換します(リアルタイムまたはコンパイルのシミュレーションのいずれか)。 これで、中間層プログラムは毎回「手書きテキスト」を認識する必要がなくなりました。 これは1回だけ実行されるか、まったく実行されません(プログラムが既にバイトコードに変換されている場合)。 「原稿」の代わりに、プログラムのバイトコードで動作します。 バイトコードはCPU言語に非常に似ていますが、CPU言語ではありません(よりプラットフォームに依存しません)。 CPUの言語に翻訳する必要があります。 したがって、C ++よりも高速に実行されるJavaについての噂が著しく誇張されていることは明らかです。 そして、プロセッサがJavaバイトコードを理解することを学ぶまで、これはそのままです。



さて、通訳者の仕事の短い一般的な説明の後、小さなプロジェクトを書くときにスキップできるが、時には正しく理解されて使用されるとパフォーマンスを大幅に向上させることができる3つのトピックに言及したいと思います。



すでにコンパイルされている言語機能





プログラミング言語は単なる構文以上のものです。 また、さまざまなデータやデバイスを操作するための既製の関数ライブラリのセットです。 コンパイル言語では特に違いはありませんが、インタープリターでは違いがあります。 Javaなどの一部の言語では、これらの関数はインタープリター自体の言語で記述されています。 また、JavaScriptやPHPなどの一部の言語では、コンパイルされた言語で、つまり、プログラムの実行時に既にコンパイルされており、追加の処理を必要としません 。 したがって、これらの呼び出しは追加の処理を必要としません。その結果、このインタープリター言語で同じものを書くよりも実行がはるかに速くなります。 したがって、 この種の組み込み関数を実行する機会があれば、たとえそれが余分なことをしていて問題を解決したとしても、独自の複雑な構造やそうではない構造を書く代わりにそれを使用してみてください。 たとえば、複雑な条件を持つ文字列を一連の部分文字列に分割するには、同じ文字列を手動で処理して独自のループを作成するよりも、正規表現を使用することをお勧めします。



複雑だが使いやすいフレームワーク





関数ライブラリのセットに加えて、一部の愛好家は言語の構文とロジックの変換も試みており、構造やデータ(jQuery、LINQ、ORMなど)を操作するときに何かを簡素化するアイデアを紹介しています。 言語がコンパイルされていれば、それほど怖くありません。 しかし、インタプリタでは、サードパーティの抽象関数に盲目的に没頭することは有害です。 はい、多くの場合、このようなコンバータを使用すると、より便利になりますが、この利便性はほとんどの場合、作業速度により実現されます。 これらの「ヘルパー」のソースコードを見て、 必要なものを正確に実行する言語に組み込まれたいくつかの関数を呼び出すほうが、何を理解する前に内部の「トン」コードを実行するよりも効率的である場合があります。あなたは彼女から欲しいと最終的にそれを行います。 たとえば、すべてのDIVを受信するJavaScriptでは、必要なものをすぐに返す組み込み関数「document.getElementsByTagName(「DIV」)」を直接呼び出すか、美しいjQuery関数「$(「DIV」)」を呼び出すことができます、いくつかの正規表現、いくつかのチェック、配列の「手動」結合を実行し、その後のみ必要なものを返します。



文字列を操作する





そして最後に、私が最後に注目したかったのは、文字列の操作です。 インタプリタ言語では、文字列の操作が非常に透明になったため、これらが最もリソースを集中的に使用する操作であるという事実は明らかではありません。 この事実は通常、少なくともコンパイルされた言語で手動で作業した人だけが知っています(彼らはこの作業を容易にする機能も持っています)。 問題は、文字列を使用したほとんどすべての操作(文字列の作成、文字列の連結、部分文字列への分割、部分文字列の削除、部分文字列の置換)、メモリ内の空き領域の検索、必要な長さ、新しい文字列、結果のデータを新しい場所にコピーすることです。 UTF-8のような複雑なフォーマットの出現により、文字列による検索などの単純な一見した操作でさえ、特に高速ではありません。 ASCII形式での作業と比較。 したがって、 行を使用せずにできる行を悪用しないでください 。 たとえば、連想配列-番号付き配列を回避できる場合は、回避してください!





最新のプロセッサでは、最適化されたコードとホイップコードのパフォーマンスの違いをほとんど何もしない関数では、感じられないかもしれません。 違いは、クイックコードが何度も実行される場所(ループ内、頻繁に呼び出される関数内)、またはそのようなコードが多数ある場所でより明白になります。



頑張って!



All Articles