0

Android 用の視覚化アプリを開発しています (Android 2.2 を実行している古いデバイスを含む)。

私のアプリの入力モデルには、通常、数万の頂点で構成される領域が含まれています。典型的なモデルは 50000 から 100000 の頂点 (それぞれx、y、z 座標 floatを持つ) を持ちます。つまり、600K から 1200 キロバイトの合計メモリを消費します。アプリでは、すべての頂点がいつでもメモリ内で使用できる必要があります。このアプリについて共有できるのはこれだけです (高レベルの使用例を共有することは許可されていません)。そのため、以下の結論が正しいかどうか、およびより良い解決策があるかどうか疑問に思っています。

たとえば、count=50000 個の頂点があるとします。2 つの解決策があります。

1.) 私の以前の解決策は、独自のものを使用していましたVertexObj(カプセル化による読みやすさの向上、個々の座標にアクセスする際の局所性の向上):

public static class VertexObj { 
      public float x, y, z;
}

VertexObj mVertices = new VertexObj[count]; // 50,000 objects

2.) 私の他のアイデアは、float[]代わりに大きなものを使用することです:

float[] mVertices = new VertexObj[count * 3]; // 150,000 float values

最初の解決策の問題は、大きなメモリ オーバーヘッドです。アプリのヒープが 16 ~ 24 MB に制限されている可能性があるモバイル デバイスを使用しています (そして、私のアプリは他のことにもメモリを必要とします)。Androidの公式ページによると、本当に必要でない場合はオブジェクトの割り当てを避ける必要があります。この場合、頂点が 50,000 であってもメモリ オーバーヘッドは非常に大きくなる可能性があります。

まず第一に、「有用な」メモリは 50000*3*4 = 600K です (これは値によって使い果たされfloatます)。次に、要素のために +200K のオーバーヘッドがVertexObjあり、おそらく Java オブジェクト ヘッダーのためにさらに +400K のオーバーヘッドがあります (Android でもオブジェクトごとに少なくとも 8 バイトです)。これは、50,000 個の頂点に対して 600K の「無駄な」メモリであり、100% のオーバーヘッド (!) です。100,000 頂点の場合、オーバーヘッドは 1.2MB です。

2 番目のソリューションは、値に有効な 600K のみを必要とするため、はるかに優れていfloatます。

どうやら結論としては で行くべきfloat[]なのですが、この場合のリスクを知りたいです。私の疑問は、メモリ管理の低レベル (厳密には Android 固有ではない) の側面にも関連している可能性があることに注意してください。

私が知る限り、私が書くnew float[300000]と、アプリは VM に 300000*4 = 1200K バイトの連続ブロックを予約するように要求します。( Dalvik ヒープに 1MB をはるかに超える空き領域がbyte[]あるにもかかわらず、 Android で 1MB を要求したところ、 が返されました。これは、1MB の連続したブロックを予約できなかったためだと思います。)OutOfMemoryException

Android の VM の GC は圧縮 GC ではないため、メモリが「フラグメント化」されている場合、そのような膨大なfloat[]割り当てが OOM を引き起こす可能性があるのではないかと心配しています。私がここにいるなら、このリスクは処理されるべきです。たとえば、より多くのオブジェクトを割り当てるのはどうfloat[]でしょうか (それぞれが 200KB などの部分を格納します)。このようなリンク リスト メモリ管理メカニズムは、オペレーティング システムと VM で使用されるため、ここ (アプリケーション レベル) で使用する必要があるとは思えません。私は何が欠けていますか?

何もない場合、最善の解決策はオブジェクトのリンクリストを使用することだと思いfloat[]ます(OOMを回避し、オーバーヘッドを小さく保つため)?

4

2 に答える 2

1

float配列の割り当て中に直面しているメモリ不足は非常に奇妙です。

ヒープ内で使用可能な最大メモリ ブロックが float 配列に必要なメモリよりも小さい場合、ヒープは必要なメモリに対応するためにサイズを増やします。

もちろん、ヒープがアプリケーションで利用可能な最大値にすでに達している場合、これは失敗します。これは、アプリケーションがヒープを使い果たした後、かなりの数のオブジェクトを解放してメモリの断片化を引き起こし、割り当てるヒープがなくなったことを意味します。ただし、この場合、断片化されたメモリが float 配列を保持するのに十分であると仮定すると (そうでない場合、アプリケーションは実行されません)、順序を割り当てるだけです。

アプリケーションの起動時に float 配列に必要なメモリを割り当てると、大量のメモリが確保されます。次に、countigous メモリが既に割り当てられているため、アプリケーションに残りの作業を任せるだけです。

DDMSin を使用してEclipse、yout アプリを選択し、 ボタンを押すと、割り当てられているメモリ ブロック (および空きメモリ ブロック) を簡単に確認できUpdate Heapます。

誤解を招くのを避けるために、投稿前にテストして、float[300000].

よろしく。

于 2012-12-12T20:58:57.487 に答える
0

実際に、テスト ケースのデータを埋め込みたいという問題に遭遇しました。私がそのような配列を宣言したために、関数が 65,535 バイトのデータを超えると Eclipse が不平を言い続けたので、巨大な配列を埋め込むのはとても楽しい時間です。ただし、これは実際には非常に一般的なアプローチです。

残りは最適化に入ります。大きな疑問はこれです: そのすべての最適化を行うのに苦労する価値があるでしょうか? RAM に問題がない場合は、1.2 メガを使用しても問題ないはずです。配列がそれほど大きい場合、Java が泣き言を言う可能性もありますが、LinkedList のような手の込んだデータ構造を使用したり、配列を小さなものに切り刻んだりすることができます。静的に設定されたデータの場合、狂ったように読んでいる場合は配列が適していると思います。

整数の .xml ファイルを作成できることはわかっているので、値を掛けて読み込み、値で割るなどの戦術を使用して整数として格納することも別のオプションです。テキスト ファイルなどをアセット フォルダーに入れることもできます。アプリケーションで一度実行するだけで、好きなように読み書きできます。

ダブル対フロートに関しては、あなたの場合、数学または科学のケースでは、それをやってのけることができれば、ダブルの方が安全だと思います。なんらかの計算を行う場合、特に乗算などの演算では、double でエラーが発生する可能性が低くなります。フロートは通常より高速です。Java が SIMD パッキングを行うかどうかはわかりませんが、そうであれば、倍精度よりも多くの float を SIMD レジスタにパックできます。

于 2012-12-12T18:55:35.513 に答える