microsoft / microsoft/onnxruntime-genai

Memory leak during back-to-back inferences

Open
#590 20 comments 6 reactions 1 assignee View on GitHub

@PatriceVignola is already working on this.

Since Jul 5, 2024.

ep:DML model:transformer performance
Dominant language
C++
Stars
1.1k
Forks
354
Avg merge
2d 16h
Merged PRs (30d)
85

Description

I am experiencing a memory leak while running my application, which is to run an MMLU accuracy test on my Radeon 780M iGPU via DirectML.

Each inference adds tens-hundreds of megabytes to the total system memory and total graphics memory utilized, until it eventually fills up after about 50 inferences and crashes the system.

## My system

* Ryzen "Pheonix" 7940HS with Radeon 780M iGPU
* 32 GB system memory

## Software

* Phi-3-Mini with awq 4 bit weights
* onnxruntime-genai-directml 0.2.0
* onnxruntime-directml 1.18.0

The model is running on the Radeon 780M iGPU,

## My Code

I define a `generate()` function like this, that is meant to return all the response tokens to the input_ids from a prompt.

```python
def generate(
input_folder,
input_ids,
max_new_tokens=512,
do_sample=True,
top_k=50,
top_p=1.0,
temperature=0.7,
pad_token_id=None,
):
model = og.Model(input_folder)
params = og.GeneratorParams(model)

if pad_token_id:
params.pad_token_id = pad_token_id

max_length = len(input_ids) + max_new_tokens

params.input_ids = input_ids
params.set_search_options(
do_sample=do_sample,
top_k=top_k,
top_p=top_p,
temperature=temperature,
max_length=max_length,
min_length=max_length,
)
params.try_graph_capture_with_max_batch_size(1)

generator = og.Generator(model, params)

prompt_start_time = time.perf_counter()
generator.compute_logits()
generator.generate_next_token()
prompt_end_time = time.perf_counter()

time_to_first_token = prompt_end_time - prompt_start_time

if max_new_tokens > 1:

token_gen_times = []
while not generator.is_done():
token_gen_start_time = time.perf_counter()
generator.compute_logits()
generator.generate_next_token()
token_gen_end_time = time.perf_counter()

token_gen_times.append(token_gen_end_time - token_gen_start_time)

if token_gen_times:
# List will be empty if we generated 1 or 0 tokens, and we don't
# want a divide-by-zero error in those cases
avg_token_gen_latency_s = sum(token_gen_times) / len(
token_gen_times
)
tokens_per_second = 1 / avg_token_gen_latency_s

return [generator.get_sequence(0)]
```

Then, I call `generate(tokenizer(prompt), max_new_tokens=1)` dozens of times while running the MMLU accuracy test. Each prompt adds a bit more memory utilization until the system crashes.

## Screenshots

Here is a screenshot of system and iGPU memory utilization. It is climbing like a staircase due to the memory leak, when it should be flat.
![image](https://github.com/microsoft/onnxruntime-genai/assets/80718789/88f6b0e9-4ed4-4be4-8107-dbe6cfcf5feb)
![image](https://github.com/microsoft/onnxruntime-genai/assets/80718789/1a06eb23-6190-4b73-ad73-a9a4444e1a28)

For reference, here is the exact same MMLU accuracy test code running on a Huggingface Transformers implementation of Phi-3-Mini on CPU. Memory utilization is flat, as expected.

![image](https://github.com/microsoft/onnxruntime-genai/assets/80718789/f8a2cff5-c4e3-4221-8fc9-2115265712b1)

## The Question

What do I do about this memory leak? Do I need to do some explicit garbage collection in my code to make my `generate()` function save to run many times in a loop?

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.