tonycoz / imager

Imager - image manipulation from perl.
http://imager.perl.org/
27 stars 3 forks source link

high RAM usage when reading in HEIF/HEIC format #511

Closed sherrardb closed 8 months ago

sherrardb commented 8 months ago

good day, when reading a relatively-small (2MB) .heic file, i end up with about 600MB of RAM usage in the parent process. i am quite happy to poke around in the source to better narrow down the cause, but before going further i first wanted to confirm whether this kind of growth is expected, or whether this might point to an issue on my end.

~$  ls -lh /tmp/00600919-CJg-7HLh.heic
-rw-rw-r-- 1 sburton web 2.0M Apr 12  2022 /tmp/00600919-CJg-7HLh.heic

~$  file /tmp/00600919-CJg-7HLh.heic
/tmp/00600919-CJg-7HLh.heic: ISO Media, HEIF Image HEVC Main or Main Still Picture Profile

~$  perl -Mstrict -mImager -we '$|++; warn `top -b -c -Em -n1 | grep -F $$ | grep -F perl`."\n"; Imager->new()->read( file=> "/tmp/00600919-CJg-7HLh.heic" ); warn `top -b -c -Em -n1 | grep -F $$ | grep -F perl`."\n";'
 1162 sburton   20   0   17.2m  11.4m   4.9m S   0.0   0.1   0:00.03 perl -Mstrict -mImager -we $|++; warn `top -b -c -Em -n1 | grep -F $$ | grep -F perl`."\n"; Imager->new()->read( file=> "/tmp/00600919-CJg-7HLh.heic" ); warn `top -b -c -Em -n1 | grep -F $$ | grep -F perl`."\n";
 1163 sburton   20   0    6.5m   2.9m   2.7m S   0.0   0.0   0:00.00 sh -c top -b -c -Em -n1 | grep -F 1162 | grep -F perl

 1162 sburton   20   0  614.8m  51.7m   8.0m S   0.0   0.6   0:00.92 perl -Mstrict -mImager -we $|++; warn `top -b -c -Em -n1 | grep -F $$ | grep -F perl`."\n"; Imager->new()->read( file=> "/tmp/00600919-CJg-7HLh.heic" ); warn `top -b -c -Em -n1 | grep -F $$ | grep -F perl`."\n";
 1268 sburton   20   0    6.5m   2.9m   2.7m S   0.0   0.0   0:00.00 sh -c top -b -c -Em -n1 | grep -F 1162 | grep -F perl

~$  cpanm --sudo Imager Imager::File::HEIF
Imager is up to date. (1.019)
Imager::File::HEIF is up to date. (0.003)

please let me know if you require further details, or if i have overlooked something in the docs.

cheers 00600919-CJg-7HLh.zip

sherrardb commented 8 months ago

... and the versions of the underlying libs, since i didn't think to check or include that before creating the issue:

~$  locate .so | grep -F -e heif -e heic -e libde265 | xargs -r ls -lrt
-rwxr-xr-x 1 root root 6480928 Nov  4 12:26 /usr/local/lib/libde265.so.0.1.5
lrwxrwxrwx 1 root root      17 Nov  4 12:26 /usr/local/lib/libde265.so.0 -> libde265.so.0.1.5
lrwxrwxrwx 1 root root      17 Nov  4 12:26 /usr/local/lib/libde265.so -> libde265.so.0.1.5
-rw-r--r-- 1 root root 3597176 Nov  4 12:38 /usr/local/lib/libheif.so.1.17.3
lrwxrwxrwx 1 root root      17 Nov  4 12:42 /usr/local/lib/libheif.so.1 -> libheif.so.1.17.3
lrwxrwxrwx 1 root root      12 Nov  4 12:42 /usr/local/lib/libheif.so -> libheif.so.1

they are locally-installed and have been updated, but the problem unfortunately persists.

sherrardb commented 8 months ago

sorry, i didn't realize that Imager::File::Heif was in a separate repo until i started to look into the source code. please let me know if i should file this issue against the other repo.

tonycoz commented 8 months ago

Here is fine.

I don't think the code in the dist allocates much beyond what's needed to store the images.

It's unclear to me which column has the 614.8m, which looks like the VSZ, the RES (resident size) is only 51.7m which seems reasonable given the size.

If I check with valgrind:

$ PERL_DESTRUCT_LEVEL=2 valgrind perl -Mblib -MImager -e '$im = Imager->new(file => shift) or die' tmp/00600919-CJg-7HLh.heic 
==2221046== Memcheck, a memory error detector
==2221046== Copyright (C) 2002-2022, and GNU GPL'd, by Julian Seward et al.
==2221046== Using Valgrind-3.19.0 and LibVEX; rerun with -h for copyright info
==2221046== Command: perl -Mblib -MImager -e $im\ =\ Imager-\>new(file\ =\>\ shift)\ or\ die tmp/00600919-CJg-7HLh.heic
==2221046== 
==2221046== 
==2221046== HEAP SUMMARY:
==2221046==     in use at exit: 36,701,505 bytes in 82 blocks
==2221046==   total heap usage: 100,385 allocs, 100,303 frees, 429,381,832 bytes allocated

Note that this is the total allocations, including memory that's been allocated since, if I ask ps for the SZ and VSZ:

$ PERL_DESTRUCT_LEVEL=2 perl -Mblib -MImager -e '$im = Imager->new(file => shift) or die; system qq(ps -o "pid sz vsz rss args "  $$);' tmp/00600919-CJg-7HLh.heic 
    PID    SZ    VSZ   RSS COMMAND
2224535 223178 892712 100292 perl -Mblib -MImager -e $im = Imager->new(file => s

I get a similarly a similar inflated size for the VSZ, but SZ is a lot more reasonable.

heif-convert, one of the examples included with the library:

$ valgrind heif-convert tmp/00600919-CJg-7HLh.heic simple.jpg
==2221444== Memcheck, a memory error detector
==2221444== Copyright (C) 2002-2022, and GNU GPL'd, by Julian Seward et al.
==2221444== Using Valgrind-3.19.0 and LibVEX; rerun with -h for copyright info
==2221444== Command: heif-convert tmp/00600919-CJg-7HLh.heic simple.jpg
==2221444== 
File contains 1 image
Written to simple.jpg
==2221444== 
==2221444== HEAP SUMMARY:
==2221444==     in use at exit: 0 bytes in 0 blocks
==2221444==   total heap usage: 18,340 allocs, 18,340 frees, 190,734,510 bytes allocated

Given the size of the image:

$ heif-info tmp/00600919-CJg-7HLh.heic 
MIME type: image/heic
main brand: heic
compatible brands: mif1, MiPr, miaf, MiHB, heic

image: 4032x3024 (id=49), primary
  thumbnail: 320x240
  color profile: prof
  alpha channel: no 
  depth channel: no
metadata:
  Exif: 2138 bytes

The uncompressed RGB image in memory itself uses about 40MB, and Imager::File::HEIF and libheif will have their own copies of the RGB image.

I'll need to find a good over time memory profiler to track this down any further.

tonycoz commented 8 months ago

It does look like there's a leak though.

sherrardb commented 8 months ago

sorry for the lack of detail in the initial output. even though you have answered the question already, for the sake of posterity, the 600MB is indeed VSZ

~$  perl -Mstrict -mImager -we '$|++; warn `top -b -c -Em -n1 -p $$ | tail -n2`."\n"; Imager->new()->read( file=> "/tmp/00600919-CJg-7HLh.heic" ); warn `top -b -c -Em -n1 -p $$ | tail -n2`."\n";'
  PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND
 2794 sburton   20   0   17.2m  11.4m   4.9m S   0.0   0.1   0:00.03 perl -Mstrict -mImager -we $|++; warn `top -b -c -Em -n1 -p $$ | tail -n2`."\n"; Imager->new()->read( file=> "/tmp/00600919-CJg-7HLh.heic" ); warn `top -b -c -Em -n1 -p $$ | tail -n2`."\n";

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND
 2794 sburton   20   0  615.2m  52.3m   8.6m S   0.0   0.7   0:01.12 perl -Mstrict -mImager -we $|++; warn `top -b -c -Em -n1 -p $$ | tail -n2`."\n"; Imager->new()->read( file=> "/tmp/00600919-CJg-7HLh.heic" ); warn `top -b -c -Em -n1 -p $$ | tail -n2`."\n";
tonycoz commented 8 months ago

The leak I found is fixed in 0.004.

tonycoz commented 7 months ago

and 0.005 lets you turn off the decoder threading as discussed in strukturag/libheif#1032

sherrardb commented 7 months ago

thanks a bunch. i was resigned to having to manually compile libheif.