8

Visual Studio C ++ 2010(リリースモードでビルド)で記述されたWindowsベースのソフトウェアで400万個の三角形をレンダリングしたいと思います。390万個の三角形をレンダリングすると、ソフトウェアが消費するRAMメモリの合計は400MBになります。しかし、400万個の三角形(さらに10万個以上)をレンダリングしようとすると、システムでエラーが発生します。

For Example:
Point *P = new (std::nothrow) Point[nb_triangles]; //==> "(std::nothrow)" is for catching the run time memory allocation error. (Point is X, Y, Z floats)
If(P == NULL)
   message("System can't allocate this much memory.");  // System gives me this error. It means the system can't reserve huge memory for this operation.

頂点、面法線、頂点法線などにメモリを割り当てる必要があります。

実際に私が得ていないのは、8 GBのRAMメモリがあり(ただし、32ビットXPウィンドウでは3.2 GBのメモリ)、ソフトウェアは400 MBだけ予約されており、空きメモリは1 GBを超えていますが、 10万個の三角形をさらにレンダリングすると、エラーが発生します。なぜそれが私にエラーを与えているのですか?それはまだ1GB以上の空きRAMメモリを持っているので?

この問題を修正する方法はありますか?アプリケーションに使用可能なすべてのメモリを割り当てるにはどうすればよいですか?この問題のために、私は390万の三角形をレンダリングするためだけにソフトウェアに制限を設ける必要があり、それは良くありません。

そして、私の心にあるもう1つの質問は、メモリ割り当て用のc ++ "new"演算子でエラーが発生することですが、c"malloc"演算子はどうでしょうか。「malloc」でこの問題を修正できますか?これら2つの間に違いはありますか?

案内してください。ありがとう。

更新#1:

たくさん試したり、コードを変更したり、メモリリークを削除したりしましたが、400万を超えるメモリを割り当てることはできません。コード全体を「ベクター」に変更することはできません。「ベクトル」に変更することはできません。「新しい」で自分のデータ構造に固執する必要があります。以下は、1つのオブジェクトをレンダリングするために割り当てたいポインタです。

P = new points[10000000]; // points is the class with 3 floats X, Y, Z;
N = new Norm[10000000]; // Norm is the class with 3 floats X, Y, Z;
V = new vNorm[10000000]; // vNorm is the class with 3 floats X, Y, Z;
T = new Tri[10000000]; // Tri is the class with 3 integers v1, v2, v3;
4

4 に答える 4

14

これはWindowsプログラミングの偉大な神話の1つであり、プロセスでRAMが不足することはありません。Windowsはデマンドページング仮想メモリオペレーティングシステムです。プロセスがより多くのRAMを必要とする場合、オペレーティングシステムは、他のプロセスが所有する他のメモリページをページアウトすることによってスペースを確保します。または、プロセス自体、しばらく使用されていないページをスワップアウトします。

その神話は、タスクマネージャーがデフォルト設定でプロセスのメモリ使用量を報告する方法によって促進されます。ワーキングセット、RAMにあるプロセスの実際のバイト数を示します。通常、プロセスによって割り当てられる仮想メモリの量よりもはるかに小さい値。プロセスは、仮想メモリを割り当てることができなくなると、OOMで停止します。Taskmgrのもう1つの統計、VMサイズ値。また、通常、すべてのVMが使用されたためではなく、十分な大きさの穴が残っていないために停止します。SysInternalsのVMMapユーティリティは、プロセスがそのアドレス空間をどのように使用しているかを確認するための優れた方法です。

より大きな仮想メモリアドレス空間を取得するには、かなり根本的な見直しが必要です。今日は簡単ですが、プラットフォームターゲットとしてx64をターゲットにするだけです。64ビットプロセスでは、ページングファイルの最大サイズによってのみ制限される、大量のアドレス空間を利用できます。/ LARGEADDRESSAWAREリンカーオプションを使用することで、64ビットオペレーティングシステムで実際に実行されていることを期待できる限り、32ビットモードで足を引きずることができます。これにより、64ビットオペレーティングシステムでVMサイズが2GBから4GBに増加します。

于 2013-03-25T02:10:54.073 に答える
2

質問の1つについて:

is there any diffirence between these two?

newとmallocの違いは次のとおりです。

  1. mallocCで使用され、初期化されてmallocいないメモリを割り当てます。割り当てられたメモリは、で解放する必要がありfreeます。

  2. new対応するコンストラクターを呼び出して、割り当てられたメモリを初期化します。で割り当てられたメモリは、(デストラクタを呼び出す)でnew解放する必要があります。delete割り当てられたメモリを解放するために、メモリブロックのサイズを指定する必要はありません。

とが標準に従って関連しているかどうかは不明です(特定のコンパイラがを使用して実装するかどうかによって異なりますnew)。そのため、。に置き換えるだけで問題が解決する場合と解決しない場合があります。mallocnewmallocnewmalloc

示したコードから、エラーの原因を特定することは困難です。動的配列をに置き換えてvector、問題が解決するかどうかを確認することができます。一方、valgrindコードにメモリリークがあるかどうかを確認するために使用できます(残念ながら、Windowsではvalgrindを使用できないため、makefileを使用してコードをLinuxに移植できる場合)。

于 2013-03-25T02:00:18.263 に答える
2

そして、私の心にあるもう1つの質問は、メモリ割り当て用のc ++ "new"演算子がエラーを発生させることです。c"malloc"演算子はどうですか?「malloc」でこの問題を修正できますか?これら2つの間に違いはありますか?

mallocとの間には違いがあります。newたとえば、newメモリを初期化し、クラスのコンストラクタを自動的に呼び出します。または、プリミティブ型(、floatなど)の場合は初期化します。また、によって割り当てられたメモリは、デストラクタを呼び出すキーワードで削除する必要があります。intcharnewdelete

CとVisualStudioのmalloc()演算子newは、内部的にを呼び出しますHeapAlloc()。必要なメモリが大きすぎる場合、またはプロセス間で共有されている場合にHeapAlloc()呼び出します。VirtualAlloc()したがって、必ずしも問題が解決するとは限りません。実際、C ++を使用している場合は、を使用することに固執しnewます。

于 2013-03-25T02:03:38.217 に答える
0
P = new points[10000000]; // points is the class with 3 floats X, Y, Z;
N = new Norm[10000000]; // Norm is the class with 3 floats X, Y, Z;
V = new vNorm[10000000]; // vNorm is the class with 3 floats X, Y, Z;
T = new Tri[10000000]; // Tri is the class with 3 integers v1, v2, v3;

このコードは、それぞれ3つのfloatの30Mベクトルを割り当てるため、30M * 3 * 4 = 360MBのデータに加えて、それぞれ3つの整数の10Mベクトル、10M * 3 * 2=60MBのデータになります。

それは一般的にはうまくいくはずです。そして、それが機能することを自分自身に証明するには、このコードをスタンドアロンプ​​ロジェクトに入れる必要があります。うまくいくものから始めなければなりません。そしてそれは確かに機能します。やってみなよ:

// main.cpp

struct Vector3 { float x, y, z; }
struct Tri { int v1, v2, v3; }

int main() {
  const int N = 10000000;
  new Vector3[N]; // no need to assign to variables in this simple test
  new Vector3[N];
  new Vector3[N];
  new Tri[N];
  // At this point, all four arrays are dynamically allocated although
  // inaccessible as we don't have any variables to work with. That's fine
  // for what we want to show: that the allocations do succeed. If they won't,
  // the code will "crash", i.e. abort via std::terminate.

  // No need to deallocate - it's a waste of time. The process is exiting,
  // the memory will be freed no matter what you do at this point. The OS
  // is taking over
}

システムに2GBを超えるRAMがある場合は、正常に動作します。少し新しいMSVC(2017)を使用して、VMでWindows XP SP 6(32ビット)を試してみました。MSVCバージョンは、MSVC 2010のいくつかの恐ろしいバグを除けば、その点で大きな違いはありません(アレイの新しいものと同じくらい基本的なものは、それほどひどく壊れることはないと思います)。

示されているように、new nullptrを返すことはありませんnewが返される場合、有効な値を返します。割り当てに失敗しても戻らないので問題ありません。

どうして戻らないの?例外がスローされ、プログラムはエラー表示で終了します。これは、その例外が未処理のまま伝播するためです。

コード全体を「ベクター」に変更することはできません

は?それはかなり些細なことです。ベクトルはCスタイルの配列と同じようにインデックス付けできるため、ベクトルを使用するコードは同じままです。

方法は次のとおりです。

// main.cpp
#include <algorithm>
#include <vector>
#define CPP20 (__cplusplus > 201703L)
#if CPP20
#  include <ranges>
#endif

struct Vector3 { float x, y, z; }
struct Tri { int v1, v2, v3; }

int main() {
  const int N = 10000000;
  auto P = std::vector<Vector3>(N);
  auto N = std::vector<Vector3>(N);
  auto V = std::vector<Vector3>(N);
  auto T = std::vector<Tri>(N);

  // awful C code as an example
  for (int i = 0; i < N; ++i)
    N[i] = P[i];

  // more idiomatic C++ code
  std::copy(P.begin(), P.end(), N.begin());

  // even more idiomatic on C++20
  #if CPP20
  std::ranges::copy(P, N);
  #endif

  // KISS code
  P = N; // you can't do that with raw C arrays!

  // C++20 KISS with superpowers
  std::ranges::copy(P | std::views::reverse, N)
}
于 2021-07-01T05:40:00.803 に答える