Anti-pattern: Closure capturing large objects harms performance#

Don’t capture large objects in the closure of a remote function or class. Use the object store instead.

When you define a ray.remote function or class, it’s easy to accidentally capture large objects of more than a few MB implicitly in the definition. This can lead to slow performance or even out-of-memory (OOM) errors, because Ray isn’t designed to handle large serialized functions or classes.

To resolve this problem for large objects, use one of the following two options:

  • Use ray.put() to put the large objects in the Ray object store, and then pass object refs as arguments to the remote functions or classes. Better approach #1 in the code example shows this option.

  • Create the large objects inside the remote functions or classes by passing a lambda method. Better approach #2 shows this option. It’s also the only option for using unserializable objects.

Code example#

Anti-pattern:

import ray
import numpy as np

ray.init()

large_object = np.zeros(10 * 1024 * 1024)


@ray.remote
def f1():
    return len(large_object)  # large_object is serialized along with f1!


ray.get(f1.remote())

Better approach #1:

large_object_ref = ray.put(np.zeros(10 * 1024 * 1024))


@ray.remote
def f2(large_object):
    return len(large_object)


# Large object is passed through object store.
ray.get(f2.remote(large_object_ref))

Better approach #2:

large_object_creator = lambda: np.zeros(10 * 1024 * 1024)  # noqa E731


@ray.remote
def f3():
    large_object = (
        large_object_creator()
    )  # Lambda is small compared with the large object.
    return len(large_object)


ray.get(f3.remote())