Search code examples
c++objective-cmacosmachine-learningcoreml

Minimal CoreML Prediction Leaking Memory


I have a memory leak (App memory is just increasing) when using a CoreML prediction. I can't find any documentation or examples where something is being released that I might be missing. In my real project I've got ARC disabled, but not here (so the compiler won't allow me to manually release anything, so I guess those things I've tried don't need it)

I've reduced it down to a minimal case available on github. But here is 99% of it (the repository just has the model and additional project assets, and more error checking - no errors, the prediction runs fine, but cut down for stackoverflow)

#import <Cocoa/Cocoa.h>
#import  "SsdMobilenet.h"
#include <string>
#include <iostream>

uint8_t ImageBytes[300*300*4];

CVPixelBufferRef MakePixelBuffer(size_t Width,size_t Height)
{
    auto* Pixels = ImageBytes;
    auto BytesPerRow = Width * 4;
    CVPixelBufferRef PixelBuffer = nullptr;
    auto Result = CVPixelBufferCreateWithBytes( nullptr, Width, Height, kCVPixelFormatType_32BGRA, Pixels, BytesPerRow, nullptr, nullptr, nullptr, &PixelBuffer );
    return PixelBuffer;
}

int main(int argc, const char * argv[])
{
    auto* Pixels = MakePixelBuffer(300,300);

    SsdMobilenet* ssd = nullptr;

    for ( auto i=0; i<10000;    i++ )
    {
        if ( !ssd )
        {
            ssd = [[SsdMobilenet alloc] init];
        }
        auto* Output = [ssd predictionFromPreprocessor__sub__0:Pixels error:nullptr];
    }
    return 0;
}

Is there something I'm supposed to clear, free, release, dealloc? I've tried releasing the ssd and recreating it every iteration, but that doesn't help.

On HighSierra 10.13.6, xcode 10.1 (10B61).

Leak occurring on this 2011 imac (no metal, CPU execution) and a 2013 Retina MBP (which runs on GPU), and with other models, not just SSDMobileNet.

Edit 1: Looking into instruments, using Generations/Snapshots, it really seems like it's the output leaking, but I cannot dealloc or release it, so maybe there's something else I need to do to free the results? The <non-object>'s are all allocations inside apply_convulution_layer() calls deep inside CoreML. Instruments Generations


Solution

  • Thanks to @Matthijs-Hollemans, using an NSAutoReleasePool with ARC disabled, this doesn't leak. (I can also auto-release the SSD, but this particular combination keeps the pre-allocated SSD persistent).

    I don't have a solution for ARC/AutoReferenceCounting builds, as NSAutoReleasePool is unavailable.

    int main(int argc, const char * argv[])
    {
        auto* Pixels = MakePixelBuffer(300,300);
    
        SsdMobilenet* ssd = [[SsdMobilenet alloc] init];
        //SsdMobilenet* ssd = nullptr;
    
        for ( auto i=0; i<10000;    i++ )
        {
            NSAutoreleasePool* pool= [[NSAutoreleasePool alloc]init];
            if ( !ssd )
            {
                ssd = [[SsdMobilenet alloc] init];
            }
            auto* Output = [ssd predictionFromPreprocessor__sub__0:Pixels error:nullptr];
            //[Output release];
            //[ssd release];
            //ssd = nullptr;
            [pool drain];
        }
        return 0;
    }