Skip to content

Commit 212eb5a

Browse files
committed
Trim process heap after document analysis
1 parent 79d6d8d commit 212eb5a

3 files changed

Lines changed: 97 additions & 1 deletion

File tree

mineru/backend/vlm/vlm_analyze.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
open_pdfium_document,
3636
)
3737
from ...utils.models_download_utils import auto_download_and_get_model_root_path
38+
from ...utils.model_utils import clean_memory
3839

3940
from mineru_vl_utils import MinerUClient
4041
from packaging import version
@@ -518,6 +519,7 @@ def doc_analyze(
518519
finally:
519520
if not doc_closed:
520521
close_pdfium_document(pdf_doc)
522+
clean_memory(get_device())
521523

522524

523525
async def aio_doc_analyze(
@@ -617,3 +619,4 @@ async def aio_doc_analyze(
617619
finally:
618620
if not doc_closed:
619621
close_pdfium_document(pdf_doc)
622+
clean_memory(get_device())

mineru/utils/model_utils.py

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
11
# Copyright (c) Opendatalab. All rights reserved.
2+
import ctypes
3+
import gc
24
import math
35
import os
6+
import sys
47
import time
5-
import gc
8+
from functools import lru_cache
9+
from typing import Callable
10+
611
from PIL import Image
712
from loguru import logger
813
import numpy as np
@@ -35,6 +40,39 @@
3540
}
3641

3742

43+
@lru_cache(maxsize=1)
44+
def _get_malloc_trim() -> Callable[[int], int] | None:
45+
"""Return glibc's heap trimming function when it is available."""
46+
if sys.platform != "linux":
47+
return None
48+
49+
try:
50+
libc = ctypes.CDLL(None)
51+
malloc_trim = getattr(libc, "malloc_trim", None)
52+
except OSError:
53+
return None
54+
55+
if malloc_trim is None:
56+
return None
57+
58+
malloc_trim.argtypes = [ctypes.c_size_t]
59+
malloc_trim.restype = ctypes.c_int
60+
return malloc_trim
61+
62+
63+
def trim_process_memory() -> None:
64+
"""Return unused glibc heap pages to the operating system when possible."""
65+
malloc_trim = _get_malloc_trim()
66+
if malloc_trim is None:
67+
return
68+
69+
try:
70+
malloc_trim(0)
71+
except Exception as exc:
72+
# Heap trimming is an optional optimization and must never break parsing.
73+
logger.debug(f"Unable to trim process heap: {exc}")
74+
75+
3876
def _get_bbox(item):
3977
bbox = item.get("bbox")
4078
if bbox is not None:
@@ -203,6 +241,7 @@ def clean_memory(device='cuda'):
203241
if torch.sdaa.is_available():
204242
torch.sdaa.empty_cache()
205243
gc.collect()
244+
trim_process_memory()
206245

207246

208247
def clean_vram(device, vram_threshold=8):

tests/unittest/test_model_utils.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
from typing import Any
2+
3+
from mineru.utils import model_utils
4+
5+
6+
def test_trim_process_memory_invokes_malloc_trim(monkeypatch: Any) -> None:
7+
calls = []
8+
9+
def fake_malloc_trim(padding: int) -> int:
10+
calls.append(padding)
11+
return 1
12+
13+
monkeypatch.setattr(model_utils, "_get_malloc_trim", lambda: fake_malloc_trim)
14+
15+
model_utils.trim_process_memory()
16+
17+
assert calls == [0]
18+
19+
20+
def test_trim_process_memory_is_optional(monkeypatch: Any) -> None:
21+
monkeypatch.setattr(model_utils, "_get_malloc_trim", lambda: None)
22+
23+
model_utils.trim_process_memory()
24+
25+
26+
def test_clean_memory_runs_process_heap_trim(monkeypatch: Any) -> None:
27+
calls = []
28+
monkeypatch.setattr(model_utils, "trim_process_memory", lambda: calls.append(True))
29+
30+
model_utils.clean_memory("cpu")
31+
32+
assert calls == [True]
33+
34+
35+
def test_get_malloc_trim_configures_available_symbol(monkeypatch: Any) -> None:
36+
class FakeTrim:
37+
argtypes = None
38+
restype = None
39+
40+
def __call__(self, padding: int) -> int:
41+
return padding
42+
43+
fake_trim = FakeTrim()
44+
fake_libc = type("FakeLibc", (), {"malloc_trim": fake_trim})()
45+
monkeypatch.setattr(model_utils.sys, "platform", "linux")
46+
monkeypatch.setattr(model_utils.ctypes, "CDLL", lambda _: fake_libc)
47+
model_utils._get_malloc_trim.cache_clear()
48+
49+
try:
50+
assert model_utils._get_malloc_trim() is fake_trim
51+
assert fake_trim.argtypes == [model_utils.ctypes.c_size_t]
52+
assert fake_trim.restype is model_utils.ctypes.c_int
53+
finally:
54+
model_utils._get_malloc_trim.cache_clear()

0 commit comments

Comments
 (0)