背景
私は、長い間行ったことのない古い(しかし素晴らしい)サイトを再訪しました-Alioth Language Shootout( http://benchmarksgame.alioth.debian.org/ )。
私は数年前にC/C++でプログラミングを始めましたが、それ以来、ほとんどJavaに携わっています。これは、関与しているプロジェクトの言語の制約のためです。図を覚えていません。おおよそ、Javaがリソース使用の点でC/C++に対抗しました。
実行時間は比較的良好であり、Javaは最低でもC/C++より4倍遅いが、平均して周り(またはそれ以下)2x。Java自体の実装の性質上、これは驚くことではなく、実際のパフォーマンス時間は低い私が期待したよりも。
実際のブリックはメモリ割り当てでした-最悪の場合Java割り当て:
52倍の記憶...絶対に厄介ですよね? ... またはそれは?現在、メモリは比較的安価です。
質問:
作業メモリに厳しい制限があるターゲットプラットフォーム(つまり、組み込みシステムなど)について語らない場合、汎用を選択するときにメモリ使用量を考慮する必要があります今日の言語は?
Scalaを第一言語として使用することを検討しているため、私は一部質問します。私はそれの機能的な側面が非常に好きですが、私が見ることができることから、メモリはJavaよりも高速です。しかし、メモリは年々速く、安く、豊富になっているようです(少なくとも4GBのDDR3 RAMがないと、消費者のラップトップを見つけるのはますます困難になっているようです)。より読みやすいソリューションのより迅速な構築を可能にする(おそらく実装ごとに高価な)高水準言語機能と比較して、ますます無関係になっていますか?
メモリ管理は、何かが大量のメモリを持っている場合でも、それがどれだけ速く現れるかを制御するため、完全に関連しています。最良かつ最も標準的な例は、Call of DutyやBioshockなどのAAAタイトルのゲームです。これらは、最適化と使用に関して大量の制御を必要とする事実上リアルタイムのアプリケーションです。問題は使用自体ではなく、管理です。
それは2つの言葉に帰着します:ガベージコレクション。ガベージコレクションアルゴリズムは、パフォーマンスにわずかな中断を引き起こしたり、アプリケーションを1〜2秒間ハングさせたりすることがあります。会計アプリではほとんど無害ですが、Call of Dutyのゲームでのユーザーエクスペリエンスの面で破滅的な可能性があります。したがって、時間が重要なアプリケーションでは、ガベージコレクションされた言語は非常に問題になる可能性があります。これは、Squirrelの設計目標の1つです。Squirrelは、代わりに参照カウントを使用して、LuaのGCに関する問題を解決しようとしています。
頭痛の種ですか?もちろん、正確な制御が必要な場合は、それに耐えます。
実際のブリックはメモリの割り当てでした-最悪の場合、Javaは、Cの52倍、C++の25倍のメモリを割り当てました。
質問の基になっている数字を理解していますか?
これらのJavaとCプログラムの間に大きな格差がある場合、それは主にデフォルトのJVMメモリ割り当てと、libcが必要とするものすべてです):
割り当てられるメモリを必要とするタスクを見てください(またはマルチコアプログラムからの結果を蓄積するために追加のバッファを使用してください):
マンデルブロ
Javaプログラム67、880KB :: Cプログラム30、444KB
k-ヌクレオチド
Javaプログラム494、040KB :: Cプログラム153、452KB
逆補数
Javaプログラム511、484KB :: Cプログラム248、632KB
regex-dna
Javaプログラム557、080KB :: Cプログラム289、088KB
二分木
Javaプログラム506、592KB :: Cプログラム99、448KB
...今日、汎用言語を選択する際に、メモリ使用量を考慮する必要がありますか?
特定の使用法、特定アプローチの特定を解決するかどうか)解決する必要がある問題は、使用される固有プラットフォームで利用可能なメモリの固有制限によって制約されます。
すべてのものと同様に、それはトレードオフです。
シングルユーザーデスクトップで実行するアプリケーションを構築していて、そのマシンでRAMの大部分を制御することが合理的に期待できる場合、メモリを犠牲にすることは価値があるかもしれません。実装速度の使用法。同じマシンをターゲットにしているが、同時に実行されている他のメモリを大量に消費する多数のアプリケーションと競合する小さなユーティリティを構築している場合は、より慎重になる必要があるかもしれません。トレードオフ。ユーザーは、実行中にすべてのメモリを必要とするゲームで問題ない可能性があります(ただし、ワールドエンジニアが指摘しているように、ガベージコレクターが定期的にアクションを一時停止してスイープを実行することを決定した場合は心配になります) -他のことをしているときにバックグラウンドで実行している音楽プレーヤーが大量のメモリを飲み込んで動作する能力を妨げる場合、それらはそれほど熱狂的ではなくなるでしょう。Webベースのアプリケーションを構築している場合、サーバーで使用するメモリは能力を制限します同じユーザーのセットをサポートするために、より多くのアプリケーションサーバーにより多くのお金を費やすことを強いる、スケールアウトする必要があります。それは会社の経済に大きな影響を与える可能性があるので、そのトレードオフを作ることに非常に注意する必要があるかもしれません。
それは多くの要因、特に作業している規模に依存します。
議論のために、メモリの30倍、CPU使用率の2倍の違いを想定してみましょう。
Cで記述した場合に10メガバイトのメモリと1ミリ秒のCPUを必要とする対話型プログラムを扱っている場合、それはほとんど重要ではありません。300メガバイトのメモリと2ミリ秒の実行は、通常、一般的なデスクトップではまったく関係ありません。そして、携帯電話やタブレットでさえ多くを意味する可能性は低いです。
1台のサーバーの約半分のリソースを必要とすることと15台のサーバーを必要とすることの違いは、muchより大きなステップです-特に15台のサーバーにスケールアウトする可能性が高いためextraの開発に多くの作業を必要とするために、より少ない作業の代わりに。将来の拡張に関する限り、あなたが言及するのと同じ要因は、顧客ベースが大規模な成長を遂げない限り、それが1今のサーバーでは、そのサーバーが大きくなったときに問題なく新しいサーバーに置き換えることができる可能性はかなり高いです。
あなたが本当に考慮しなければならないもう一つの要因は、あなたが特定のタスクのために見るつもりである開発コストの正確な違いです。現在、あなたは基本的に方程式の片側を見ています。コストとメリットを比較するためには、(明らかに十分ですが)1つだけではなく、コストとメリットの両方を検討する必要があります。本当の質問は、基本的には「xはyより大きいですか?」です。 -しかし、xを見ただけではそれを判断できません。 yも確認する必要があります。
メモリ管理は、今日の世界ではabsolutelyに関連しています。しかし、あなたが期待する方法ではありません。ガベージコレクションされた言語でも、参照リークがないことを確認する必要があります
これがあなたのコードである場合、あなたは何か間違ったことをしています:
static List<string> Cache;
...
Cache.Add(foo); //and then never remove anything from Cache
できない再度使用しない限り、つまりCache=null
を実行して、効果的に警告しない限り、ガベージコレクションは魔法のようにあなたが再び参照を使用しないことを二度と知らないガベージコレクターは、「もうアクセスできなくなります。それを使って何をするか」
それよりも複雑ですが、参照リークは、従来のメモリリークと同じかそれ以上ではありません。
ガベージコレクターを配置できない場所もあります。たとえば、ATTiny84は512バイトのコードROMと32バイトのRAMを備えたマイクロコントローラーです。幸運を!これは極端なことであり、おそらくアセンブリ以外ではプログラムされないでしょうが、それでもまだです。他に、1Mのメモリがある場合もあります。もちろん、ガベージコレクターを組み込むこともできますが、プロセッサーが非常に遅い場合(制限により、またはバッテリーを節約するため)、ガベージコレクターを使用したくないので、プログラマーの追跡知っているはずです。
また、保証された応答時間を必要とする場合、ガベージコレクションを使用することは非常に難しくなります。たとえば、ハートモニターなどがあり、ポートで1
を受信した場合、適切な信号または10ミリ秒以内の何かで応答できることを保証する必要があります。応答ルーチンの途中でガベージコレクターがパスを作成する必要があり、応答に100ミリ秒かかる場合は、誰かが死んでいる可能性があります。ガベージコレクションは、タイミング要件を保証する必要がある場合、不可能ではないにしても非常に困難です。
そしてもちろん、最新のハードウェアでも、ガベージコレクターのオーバーヘッドを気にしないで、パフォーマンスをさらに2%必要とする場合があります。
ドナルド・クヌースが言ったように、時期尚早な最適化はすべての悪の根源です。メモリがボトルネックになると信じる理由がない限り、心配する必要はありません。また、ムーアの法則によりメモリ容量が増加しているため(シングルスレッドコードを高速化していなくても)、将来的にはメモリの制約がさらに緩和されると信じる理由はすべてあります。今日です。
とはいえ、最適化が時期尚早でない場合は、必ずそれを行ってください。私は現在、個人的にメモリ使用量を非常に詳細に理解しているプロジェクトに取り組んでいます。実際には正確な制御が必要であり、ガベージスイープは私を殺します。したがって、このプロジェクトはC++で行っています。しかし、その選択は私にとって数年に一度のイベントのようです。 (できれば数週間以内に、もう数年はC++に触れないようにします。)
「ビッグデータ」を扱う人々にとって、メモリ管理は依然として大きな問題です。天文学、物理学、バイオインフォマティクス、機械学習などのプログラムはすべてマルチギガバイトのデータセットを処理する必要があり、関連する部分をメモリに保持できる場合、プログラムの実行速度が大幅に向上します。 128GBのRAM=のマシンで実行しても問題は解決しません。
GPUを利用するという問題もありますが、おそらくそれを組み込みシステムとして分類します。 CUDAまたはOpenCLを使用する上での難しい考えのほとんどは、メインメモリからGPUメモリにデータを転送する際のメモリ管理の問題です。
公平を期すために、多くのJavaそこにあるJava $ ===は、パフォーマンスを殺し、メモリを独占する本当に無意味なクラス爆発的なパターンにふけますが、そのメモリのどれだけがJVMだけなのか疑問に思います理論的には(heh)新しいアプリを完全に書き直さなくても、同じアプリを複数の環境で実行できるようにします。そのため、デザインのトレードオフの問題は次のように要約されます:「ユーザーのメモリのどれだけが、開発に有利であるか」
これは、IMOを検討する価値のある完全かつ妥当なトレードオフです。しかし、最近のPCは非常に強力でメモリが非常に安いので、そのような懸念や肥大化する機能や肥大化したコードを完全に無視し、多くのものに見えるようになるまで選択肢を怠ることができるという考えです。今はWindows PCを使用していますが、Window '95と同じくらい時間がかかります。真剣に、しかし、Word?ユーザーベースの80%が実際に必要とする新しいがらくたは、18年間でどれだけ追加できるでしょうか。ウィンドウの前にスペルチェックが正しく行われたことを確認してください。しかし、私たちはあなたがそれをたっぷり持っていれば必ずしもスピードではないメモリを話していたので、私は余談です。
しかしもちろん、2週間でアプリを完成させて、2年ではなく数メガバイトの追加コストで、ニーズのみの数Kのバージョンを入手できる場合は、いくつかのメガと比較することを検討する価値があります(私は推測している)平均的なユーザーのマシンで4-12ギグする前に、ずさんだという考えをあざ笑う。
しかし、これがScalaとトレードオフの質問を超えてどう関係しているのですか?ガベージコレクションだからといって、常にデータの流れについて考えるべきではないという意味ではありません。スコープとクロージャの内容と、そのままにしておくか、不要になったときにGCによって割り当てが解除されるように使用するかどうか。これは、JavaScript UI Web開発者でさえ考えなければならなかったことであり、うまくいけば、私たちがそうであるように、パフォーマンスに精通した癌(あなたはすべて、Flashやアプレットなどで殺すべきだったはずです)のような他の問題領域に広がりました。
プログラミングにおけるメモリ管理は無関係な問題になっていますか?
メモリ管理(または制御)が、実際にCおよびC++を使用している主な理由です。
現在、メモリは比較的安価です。
高速メモリではありません。私たちはまだ少数のレジスターを見ています、例えばi7上のL1のための32KBデータキャッシュ、L2のための256KB、そしてL3 /コアのための2MBのようなもの。それは言った:
作業メモリに厳しい制限があるターゲットプラットフォーム(つまり、組み込みシステムなど)について語らない場合、今日の汎用言語を選択するときにメモリ使用量を考慮する必要がありますか?
一般的なレベルでのメモリ使用量。余計に余裕があっても、たとえば50メガバイトのDRAMと数百メガバイトのハードディスク容量を必要とするメモ帳のアイデアが気に入らないので、私は少し非現実的です。私は長い間使用してきましたが、このような単純なアプリケーションがキロバイトで実行できることに対して比較的大量のメモリを消費するのを見るのは、奇妙でちょっと不快に感じます。とはいえ、それでも素晴らしくて反応が良ければ、そういうものに出会ったら、自分と一緒に暮らせるかもしれません。
私の分野でメモリ管理が重要である理由は、一般的にメモリ使用量をそれほど削減しないためです。何百メガバイトものメモリを使用しても、そのメモリに頻繁にアクセスしない場合(たとえば、ボタンクリックまたは他の形式のユーザー入力時のみ)、アプリケーションが必ずしも重要な速度で低下するわけではありません。ボタンを1秒間に100万回クリックする可能性がある韓国のスタークラフトプレーヤーについて話している)。
私の分野で重要な理由は、これらの中で非常に頻繁にアクセスされる(たとえば、すべてのフレームでループされる)メモリを密集させて閉じるためです。クリティカルパス。 100万個のエレメントのうち、1つのフレームごとにすべてループでアクセスする必要があるエレメントを1つだけアクセスするたびに、キャッシュミスが発生するのは望ましくありません。階層の下位にあるメモリを、64バイトキャッシュラインなどの大きなチャンクで低速メモリから高速メモリに移動する場合、それらの64バイトすべてに関連データが含まれている場合、それらの64バイトに複数の要素に相当するデータを収めることができる場合、それは非常に役立ちます。データが追い出される前にすべてを使用するようなアクセスパターンの場合。
数百万の要素について頻繁にアクセスされるデータは、ギガバイトありますが、20メガバイトに及ぶ場合があります。キャッシュミスを最小限に抑えるためにメモリがタイトで近接している場合、描画されるフレームごとにデータをループするフレームレートに違いが生まれますが、メモリ管理/制御が非常に役立ちます。数百万の頂点を持つ球の単純な視覚的な例:
上記は、メッシュの永続的なデータ構造表現をテストしているため、変更可能なバージョンよりも実際には遅くなりますが、それとは別に、データの半分でもこのようなフレームレートを達成するのに苦労していました(確かに、ハードウェアは私の苦労以来速くなった) )メッシュデータのキャッシュミスとメモリ使用量を最小限に抑えるコツがわからなかったためです。メッシュは、ポリゴン、エッジ、頂点、ユーザーがアタッチしたい数のテクスチャマップ、ボーンウェイトなど、同期を維持する必要のある相互依存性の高いデータを格納するため、これに関して私が扱った最も扱いにくいデータ構造の一部です。カラーマップ、選択セット、モーフターゲット、エッジウェイト、ポリゴンマテリアルなどなど。
私は過去20年間に多数のメッシュシステムを設計および実装してきましたが、その速度は多くの場合、メモリ使用量に非常に比例していました。使用しているにもかかわらず、最初よりもはるかに多くのメモリを使用していますが、新しいメッシュシステムは最初の設計(約20年前)の10倍以上高速であり、使用するシステムの約1/10です。想い出。最新バージョンでは、インデックス付き圧縮を使用して可能な限り多くのデータを詰め込みます。また、圧縮解除の処理オーバーヘッドにもかかわらず、圧縮によって実際にパフォーマンスが向上しました。これも、貴重な高速メモリがほとんどないためです。これで、テクスチャ座標、エッジの折り目、マテリアルの割り当てなどを含む100万のポリゴンメッシュを、約30メガバイトの空間インデックスとともにフィットさせることができます。私の最も古いバージョンは数百メガバイトを使用し、最も古いものを今日でも私のi7でテストしています。それは何倍も遅く、数百MBのメモリの使用には空間インデックスさえ含まれていませんでした。
これは、800万を超える四角形と、GF 8400(これは数年前からのものです)を使用したi3のマルチ解像度サブディビジョンスキームを使用した可変プロトタイプです。これは、不変バージョンよりも高速ですが、本番環境では使用されていません。不変バージョンを維持する方がはるかに簡単で、パフォーマンスへの影響もそれほど悪くないことがわかりました。ワイヤーフレームはファセットを示すのではなく、パッチ(ワイヤーは実際には曲線であり、そうでなければメッシュ全体が黒一色になる)に注意してください。ただし、ファセット内のすべてのポイントはブラシによって変更されます。
とにかく、上記のいくつかを示して、メモリ管理が非常に役立つ具体的な例と領域を示したいと思います。また、うまくいけば、お尻から話しているだけだと人々が思わないようにしたいと思います。 DRAMやハードドライブのような遅いメモリについて話しているので、メモリが非常に豊富で安価であると人々が言うとき、私は少しイライラする傾向があります。高速メモリについて話すとき、それはまだ非常に小さくて貴重であり、本当にクリティカルな(つまり、すべてではなく一般的な)パスのパフォーマンスは、少量の高速メモリを再生し、それをできるだけ効率的に使用することに関係します。 。
この種のことについては、C++などの高レベルオブジェクトを設計できる言語を使用しながら、これらのオブジェクトを1つ以上の連続した配列に格納しながら、このようなオブジェクトはすべて連続して表現され、オブジェクトごとに不要なメモリオーバーヘッドは発生しません(たとえば、すべてのオブジェクトがリフレクションまたは仮想ディスパッチを必要とするわけではありません)。これらのパフォーマンスが重要な領域に実際に移動すると、オブジェクトプールを操作したり、プリミティブデータ型を使用してオブジェクトのオーバーヘッドやGCコストを回避したり、メモリへの頻繁なアクセスを維持したりするなど、メモリを制御することが生産性の向上になります。一緒に隣接。
したがって、私のケースでは、メモリ管理/制御(またはその欠如)が、問題に取り組むために最も生産的に使用できる言語を選択する主な理由です。私は間違いなく、パフォーマンスに重要ではないコードのシェアを記述します。そのため、Cからの埋め込みが非常に簡単なLuaを使用する傾向があります。