6

私は、通常は素晴らしく実行されるかなり大きなプログラムを持っていますが、実行するために非常に多くのメモリを使用します。これは大量のデータを収集する機械学習のアプローチであるため、一般的には問題ありませんが、すべてのデータが収集された後でもメモリが急速に増加するため、valgrind massif を使用して何が問題なのかを調べました。Massif のヒープ ツリーの上部は次のようになります。

99.52% (60,066,179B) (heap allocation functions) malloc/new/new[], --alloc-fns, etc.
->43.50% (26,256,000B) 0x439785: Image::Image(SDL_Surface*) (Image.cpp:95)
| ->43.50% (26,256,000B) 0x437277: EncodedFeature::forwardPass() (EncodedFeature.cpp:65)
....

だから私は、うーん、構築されたイメージは解放されていないかもしれないと思いましたが、そうではありません:

void EncodedFeature::forwardPass()
{
    // Get image:
    Image* img = new Image(screen);

    // Preprocess:
    if(preprocessor)
        preprocessor->process(img);

    // Do forward pass:
    encoder->encode(img, features);

    delete img;
}

したがって、Image コンストラクターには次のように指定します。

Image::Image(SDL_Surface* surface)
{
    this->width = surface->w;
    this->height = surface->h;
    pixels = new int*[width];
    for(int i = 0; i < width; i++)
        pixels[i] = new int[height];

    for(int x = 0; x < surface->w; x++)
        for(int y = 0; y < surface->h; y++)
            pixels[x][y] = getPixelFromSDLSurface(surface, x, y);
}

後でデストラクタで解放されるピクセル配列を割り当てるだけです。

Image::~Image()
{
    if(!pixels)
        return;

    for(int x = 0 ; x < width; x++)
        delete[] pixels[x];

    delete[] pixels;
}

だから最後の犯人:

Uint32 Image::getPixelFromSDLSurface(SDL_Surface *surface, int x, int y)
{
    if(!surface)
        return 0;

    // Got this method from http://www.libsdl.org/cgi/docwiki.fcg/Pixel_Access
    int bpp = surface->format->BytesPerPixel;
    /* Here p is the address to the pixel we want to retrieve */
    Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;

    switch(bpp) {
    case 1:
        return *p;
        break;

    case 2:
        return *(Uint16 *)p;
        break;

    case 3:
        if(SDL_BYTEORDER == SDL_BIG_ENDIAN)
            return p[0] << 16 | p[1] << 8 | p[2];
        else
            return p[0] | p[1] << 8 | p[2] << 16;
        break;

    case 4:
        return *(Uint32 *)p;
        break;

    default:
        return 0;       /* shouldn't happen, but avoids warnings */
    }
}

コメントで述べたように、SDL wiki から入手したので、何も漏れていないことを願っています。私の場合、bpp は実際には常に 1 であるため、アドレス p の int を返すだけです。

私はここで私の知恵の終わりにいます。記憶がどこに行ったか誰か思いつきますか?つまり、massif は特に Image コンストラクターを指していますが、そこには何も問題はありません...

私の問題を見てくれてありがとう!

マックス


コメントへの回答:

そうです、ポインターとして img は必要ありませんでした。私はJavaのバックグラウンドを持っているので、すべてをポインターにしたいだけです:)変更しましたが、役に立ちませんでした。

行 95 は、コンストラクターの最初の for ループ内にあります。 pixels[i] = new int[height];

プリプロセッサの 1 つで画像のサイズを変更しますが、reset 関数を呼び出してこれを行います。これにより、古い配列が確実に削除されます。

void Image::reset(int width, int height)
{
    if(pixels)
    {
        // Delete old image:
        for(int x = 0 ; x < width; x++)
            delete[] pixels[x];

        delete[] pixels;
    }

    this->width = width;
    this->height = height;
    pixels = new int*[width];
    for(int i = 0; i < width; i++)
        pixels[i] = new int[height];
}

その後、ピクセル値を補充します...

どこにも例外はスローされていません。

どこでスマート ポインターを使用することをお勧めしますか?

すべての答えをありがとう!

4

2 に答える 2