PENGUMUMAN

Overmind: Memotong Waktu Pemuatan Model ML dari 15s menjadi 0.2s

Bagaimana Meshy membangun Overmind — sebuah pustaka open-source yang mengurangi waktu pemuatan model ML dari 15 detik menjadi 0,2 detik dengan menggunakan memori bersama tanpa salinan, tanpa memerlukan perubahan pada kode inferensi.

Bin Wang, Senior Infrastructure Engineer
Diposting: 6 Maret 2026

TL;DR Memuat model ML lambat, bahkan dengan cache halaman Linux yang sudah dipanaskan. Jadi kami membuat perpustakaan untuk membuatnya cepat. Ada beberapa detail teknis menarik yang ingin kami bagikan, jadi kami menulis blog ini. Perpustakaan ini juga memberikan dampak yang tidak terduga, yang dibahas di bagian akhir.

Alasan

Semuanya dimulai 2 tahun lalu, ketika kami meluncurkan percobaan pertama mode generasi lowpoly. Mode lowpoly tidak berjalan baik, menghasilkan hasil yang buruk dari perspektif saat ini, tetapi kami membayar mahal untuk itu -- sebuah GPU khusus hanya memproses tugas satu digit per hari. Ia memiliki bobot yang disesuaikan dengan baik, cukup besar untuk mengeluarkan semua bobot model lainnya dari VRAM. Lebih buruk lagi, kami memiliki mungkin 3 model seperti itu (tidak ingat jumlah pastinya), mereka merupakan bagian signifikan dari infrastruktur inferensi kami, membuat rasio efisiensi yang cukup tidak memaafkan. Dan tidak, kami tidak bisa memuat model secara naif tepat waktu, itu memakan waktu 30 detik, lebih lama dari waktu pemrosesan sebenarnya.

Kami tidak memiliki insinyur pipeline khusus saat itu, pengembang algoritma kami mencoba yang terbaik untuk mengatasi ini. Beberapa hari kemudian, basis kode kami dipenuhi dengan this.to('cpu') dan that.to('cuda'). Pendekatan ini bekerja untuk sementara waktu, tetapi mengganggu alur pengembang algo kami dari waktu ke waktu. Bagaimana jika semuanya bisa terjadi secara otomatis? Ini Python, semuanya memang terjadi secara otomatis di Python.

Bagaimana Anda mendefinisikan 'otomatis'?

Mari kita masuk ke peran seorang pengembang algoritma. Semuanya cukup jelas: Saya tidak ingin peduli tentang kinerja runtime di luar algoritma inti saya kecuali saya benar-benar harus melakukannya. Saya lebih suka tidak tahu apa-apa tentang pertukaran model masuk dan keluar.

Tentu saja kami tidak bisa mencapai itu, tetapi kami bisa mencoba meminimalkan gangguan yang harus kami perkenalkan ke kode algoritma. Ini mengingatkan saya pada monkey-patching dari perpustakaan gevent, yang mempatch (terutama) perpustakaan socket, menggantinya dengan gevent.socket yang dapat beralih ke greenlet lain ketika IO akan memblokir, mirip dengan goroutine (sebenarnya gevent lebih tua dari Golang!).

Karena kami hanya menggunakan perpustakaan HuggingFace (transformers, diffusers) untuk memuat model pada saat itu, target menjadi jelas: Kami hanya memperkenalkan panggilan monkey-patch, dan sisa kode harus tetap tidak berubah, XXXPipeline.from_pretrained(...) harus jauh lebih cepat.

Beberapa Fakta, Keputusan Jelas, dan Asumsi

Overmind adalah perpustakaan caching, ia menyimpan hasil panggilan pemuatan model ke dalam memori sistem dan kemudian merekonstruksinya dengan cepat.

Kami melewatkan pembahasan tentang bagaimana monkey-patching diimplementasikan, itu adalah detail yang tidak terlalu menarik. Yang perlu kita ketahui adalah, ia mengarahkan semua panggilan XXXPipeline.from_pretrained(...) ke overmind.api.load(XXXPipeline.from_pretrained, ...).

Kami menggunakan pickle untuk menyerialisasi hasil cache kami karena... kami tidak punya pilihan, dan torch.save sendiri menggunakan pickle, aneh jika tidak menggunakannya.

Kami menggunakan arsitektur klien/server karena kami tidak ingin menginvalida cache kami ketika proses berakhir. Ada banyak panggilan subprocess yang bisa mendapatkan manfaat darinya.

Kami mengasumsikan parameter XXXPipeline.from_pretrained adalah hal-hal yang dapat di-hash sederhana (str dan hal-hal serupa) dan model lain dimuat oleh overmind (dijelaskan kemudian).

Nama overmind dipinjam dari Starcraft, seperti yang mungkin sudah Anda duga.

Merekonstruksi dengan cepat!

Kami tidak bisa secara naif menyimpan hasil pickle.loads dalam memori dan menyebutnya selesai. Setelah semua, dalam skenario yang sudah dipanaskan, cache halaman Linux melakukan tugasnya menyimpan model di disk dan kita masih bisa melihat waktu pemuatan yang diukur dalam puluhan detik.

Ketidakefisienan berasal dari penyalinan memori. Dalam Python, bahkan membuat jutaan objek akan memakan waktu tidak lebih dari beberapa ratus ms. Namun, untuk penyalinan memori sebesar 10GiB, itu akan memakan waktu setengah detik. Kita harus menghindari penyalinan memori sebanyak mungkin.

Untungnya, sebagian besar potongan memori besar adalah tensor Torch, kita dapat dengan aman hanya mengalamatkan mereka dan mengabaikan sisanya.

Sebenarnya, saya mendapatkan pengetahuan tentang struktur internal tensor Torch dalam kode reduksi saat meneliti mekanisme berbagi tensor:

python
# Disalin dari torch.multiprocessing.reductions, sebagian besar kode dihapus
def reduce_tensor(tensor):
    ...
    storage = tensor._typed_storage()
    ...
    metadata = (
        tensor.storage_offset(),
        tensor.size(),
        tensor.stride(),
        tensor.requires_grad,
    )
    return (rebuild_tensor, (type(tensor), storage, metadata))

Cukup sederhana: sebuah tensor adalah tipe, metadata, dan penyimpanan dasarnya. Di sini storage adalah tipe TypedStorage, tetapi sebenarnya TypedStorage hanyalah pembungkus sederhana untuk UntypedStorage. UntypedStorage adalah kelas yang sebenarnya menyimpan semua data tensor.

Tugas kita menjadi lebih spesifik sekarang: Bagaimana kita menghindari penyalinan UntypedStorage? Bisakah kita mengelola memori tensor ini sendiri dan membangun UntypedStorage dengan menunjuk ke memori yang kita kelola?

Jawabannya adalah ya!

Dengan melihat sekilas kode C++ di mana UntypedStorage dibangun, kita dapat dengan mudah menemukan potongan kode seperti ini:

cpp
// Disalin dari torch/csrc/Storage.cpp
static PyObject* THPStorage_get(THPStorage* self, PyObject* index) {
    // ...mengabaikan kode yang tidak terkait...

    auto new_storage_impl = make_storage_impl(
        c10::StorageImpl::use_byte_size_t(),
        slicelength,
        at::DataPtr(
            static_cast<void*>(data + start),
            old_storage_impl,
            [](void* s) {
              c10::raw::intrusive_ptr::decref(static_cast<at::StorageImpl*>(s));
            },
            old_storage_impl->device()),
        old_storage_impl->allocator(),
        /* resizable */ false,
        device_opt);

    PyObject* _ret =
        THPStorage_NewWithStorage(Py_TYPE(self), std::move(new_storage_impl));

    return _ret;
}

Tidak hanya kita dapat menggunakan pointer, tetapi kelas at::DataPtr juga dapat menangani penghancuran, membuat manajemen masa pakai jauh lebih sederhana.

Di sisi Python, pointer ke wilayah memori direpresentasikan oleh objek memoryview, objek-objek ini mendukung protokol buffer. Kita dapat mendapatkan objek memoryview dari banyak hal, bytes dan mmap adalah 2 hal utama yang mendukungnya, dan mereka juga yang kita pedulikan.

Akhirnya, kita tahu apa yang harus kita lakukan: membuat fungsi yang menerima objek memoryview dan mengubahnya menjadi UntypedStorage tanpa penyalinan. Dengan kemampuan untuk merekonstruksi UntypedStorage dari memoryview, data tensor yang sebenarnya tidak harus ada dalam aliran pickle, sangat mengurangi ukuran data yang harus kita salin.

cpp
void initOvermindHelpers(py::module m) {
    // ...
    m.def("_make_untyped_storage", [](py::buffer b) {
        auto info = new py::buffer_info(b.request());

        return pybind11::reinterpret_steal<py::object>(THPStorage_NewWithStorage(
            THPStorageClass,
            c10::make_intrusive<at::StorageImpl>(
                c10::StorageImpl::use_byte_size_t(),
                info->size,
                at::DataPtr(
                    info->ptr, info,
                    [](void* ptr) {
                        py::gil_scoped_acquire gil;
                        auto b = static_cast<py::buffer_info*>(ptr);
                        delete b;
                    },
                    at::DeviceType::CPU
                ),
                /*allocator=*/nullptr,
                /*resizable=*/false,
            )
        ));
    });
}

Itulah blok bangunan inti dari overmind.

Berbagi tensor!

Catatan: Sudah ada mekanisme berbagi tensor di PyTorch, tetapi tidak sesuai dengan kebutuhan kita. Lebih lanjut tentang ini nanti.

Pertama, berbagi memori antara klien dan server

Ketika kita melihat 'berbagi' dan 'memori' datang bersama, kita semua memiliki dorongan untuk menggunakan shmget dan teman-temannya. Ini "dirancang" untuk digunakan sebagai mekanisme berbagi memori, bukan? Tetapi memiliki 2 kelemahan utama:

  • POSIX shm adalah sumber daya yang langka, apa yang dapat Anda gunakan ditentukan oleh bagaimana sysadmin mengonfigurasi sistem. Contoh ekstrim tetapi umum adalah kontainer Docker, secara default Anda hanya memiliki 64MiB POSIX shm yang dapat digunakan.
  • POSIX shm hidup lebih lama dari proses Anda, Anda harus melakukan manajemen sendiri. Jika proses manajemen dihentikan secara paksa, atau tidak menanganinya dengan hati-hati, objek shm bisa tertinggal di sistem tanpa batas waktu.

Jika Anda melihat dengan cermat, Linux penuh dengan panggilan sistem yang menarik. memfd_create adalah salah satu yang kita minati: Ini memberi Anda fd yang mewakili alokasi memori anonim. Anda dapat melakukan berbagai operasi file di atasnya: baca, tulis, dan, tentu saja, mmap. Jika kita dapat berbagi fd, kita dapat berbagi memori.

Berbagi fd memiliki cara 'standar' tetapi kuno untuk melakukannya: sendmsg dengan SCM_RIGHTS. Kita dapat memanfaatkan pustaka untuk membantu kita menyembunyikan detail menakutkan dari proses sendmsg, tetapi kita masih harus melakukan koordinasi antara proses server dan klien. Kami memutuskan untuk menggunakan trik di sini: Cukup buka /proc/{pidof(server)}/fd/{memfd} di sisi klien, sementara tidak pernah menutup fd di sisi server overmind. Satu-satunya komunikasi yang diperlukan adalah tuple (pid, fd). Ini bekerja dengan sempurna dalam kasus kami.

Kata-kata di atas merangkum menjadi baris-baris ini:

python
class SharedMemory:
    @classmethod
    def create(cls, shift):
        # Dipanggil di sisi server
        libc = ctypes.CDLL(None)
        name = _make_filename(shift).encode('utf-8')
        fd = libc.memfd_create(name, os.O_RDWR)
        os.ftruncate(fd, 1 << shift)
        mem_id = (os.getpid(), fd)
        return cls(fd=fd, mem_id=mem_id)

    @classmethod
    def rebuild(cls, mem_id):
        # Dipanggil di sisi klien
        pid, fd = mem_id
        local_fd = os.open(f'/proc/{pid}/fd/{fd}', os.O_RDWR)
        return cls(fd=local_fd, mem_id=mem_id)
    
    def get_buffer(self):
        # Dipanggil di kedua sisi
        self._mmap = mmap.mmap(self._fd, size)
        self._buf = memoryview(self._mmap)
        return self._buf

Integrasi dengan pickling

Seperti yang telah kita bahas sebelumnya, kita perlu memodifikasi proses pickling dari UntypedStorage. Mirip dengan apa yang diimplementasikan dalam torch.multiprocessing.reductions, kita mendefinisikan fungsi reduce kustom kita untuk pickle:

python
# Hoarder dan borrower adalah pembungkus untuk SharedMemory di atas, berisi
# hal-hal membosankan seperti arena memori, dll.
def _reduce_storage(storage):
    # Dipanggil oleh server
    device = storage.device
    storage = storage.cpu()

    # Simpan konten dalam memori bersama
    # `frag` berisi informasi lengkap yang diperlukan untuk menemukan konten.
    frag = hoarder.put(storage)

    return (_rebuild_storage_on_client, (frag, device))

def _rebuild_storage_on_client(frag, device):
    # Dipanggil oleh klien
    mv = borrower.borrow(frag)  # Dapatkan memoryview dari memori bersama
    storage = _make_untyped_storage(mv)  # Zero-copy!
    if device.type == 'cuda':
        return storage.cuda(device.index)
    return storage

class OvermindPickler(dill.Pickler):
    ...

OvermindPickler.register(torch.storage.UntypedStorage, _reduce_storage)

Sekarang, OvermindPickler.dumps dan OvermindPickler.loads yang sederhana akan memanfaatkan memori bersama untuk mempercepat. Anda dapat berhenti membaca di sini jika Anda sudah merasa cukup. Sisanya adalah detail.

Iblis dalam Detail

Mengapa tidak menggunakan berbagi tensor internal PyTorch?

Untuk 'berbagi tensor internal', maksud saya torch.multiprocessing.reductions.

  1. Pada tingkat tinggi, metode PyTorch dirancang untuk 'mengirimkan tensor ke subprocess', tampaknya sama tetapi dengan perbedaan halus.
  2. PyTorch menggunakan POSIX shm untuk berbagi memori, tunduk pada batasan yang disebutkan sebelumnya.
  3. Untuk setiap tensor (atau UntypedStorage), PyTorch mengalokasikan objek POSIX shm khusus untuk itu, bahkan jika hanya berisi 4 byte. Setiap objek mengkonsumsi fd.
  4. PyTorch mendekalokasi POSIX shm setelah mereka di-unpickle, membuatnya tidak cocok untuk kebutuhan kita. Kita perlu mendeserialisasi aliran pickle yang sama beberapa kali.
  5. Ada banyak logika berbagi terkait CUDA, yang merupakan kebisingan murni dan masalah untuk kasus penggunaan kita.

Mengapa Anda mengatakan 'data tensor disalin beberapa kali'?

Untuk torch.load yang tipikal di disk:

  • File torch.save di disk dibaca ke dalam memori.
  • Dapatkan data torch.UntypedStorage yang sebenarnya sebagai bytes melalui ekstraksi file Zip (torch.save menghasilkan file zip).
  • Kode C++ akan menyalin data ke dalam memori yang dikelola sendiri di konstruktor torch.UntypedStorage.

Untuk pickle.dumps yang naif dan kemudian pickle.loads:

  • Aliran pickle yang dihasilkan secara internal menyematkan aliran pickle lainnya, pickle.loads akan menyalin aliran dalam ke bytes baru.
  • Data torch.UntypedStorage tertanam dalam aliran pickle dalam, salinan lain terjadi pada konstruksi torch.UntypedStorage.
  • Kode C++ akan menyalin data ke dalam memori yang dikelola sendiri di konstruktor torch.UntypedStorage.

diffusers memiliki modul dinamis

Repositori model dapat menyertakan file Python yang diimpor saat runtime ke dalam namespace diffusers_modules. Klien tidak memiliki ini di sys.path, yang menyebabkan unpickling gagal. Untungnya, diffusers akan menulis file Python dinamis ini di disk, jadi kita bisa mengimpor modul dan menyelesaikannya.

python
def diffusers_dyn_module_workaround():
    from diffusers.utils.constants import HF_MODULES_CACHE
    modpath = Path(HF_MODULES_CACHE) / "diffusers_modules/__init__.py"
    spec = importlib.util.spec_from_file_location("diffusers_modules", modpath)
    sys.modules["diffusers_modules"] = importlib.util.module_from_spec(spec)

Dukungan untuk bitsandbytes

Hal yang paling menjengkelkan tentang mendukung bitsandbytes adalah bahwa proses kuantisasi terjadi di GPU. Setelah kita menginisialisasi CUDA dan torch di server overmind, tidak ada cara mudah untuk menguninisialisasinya, yang dapat menyebabkan masalah untuk beban kerja nyata (terutama VRAM yang kurang dapat digunakan). Oleh karena itu, kami memodifikasi server kami untuk memunculkan subprocess, memuatnya ke dalam memori bersama, dan menghentikannya. Ini terjadi untuk meningkatkan stabilitas server overmind.

Parameter yang dikuantisasi adalah subclass khusus yang disediakan oleh bitsandbytes. Mereka tidak dirancang dengan 'picklability' dalam pikiran, jadi kita harus melakukannya sendiri.

python
def _reduce_bnb_param(p):
    dev = p._prev_device
    assert p.quant_state
    return (_rebuild_bnb_param, (type(p), p.data, p.quant_state.as_dict(packed=True), dev))


def _rebuild_bnb_param(typ, data, qs_dict, dev):
    return typ.from_prequantized(data, qs_dict, device=dev)


def bitsandbytes_quirks():
    try:
        import bitsandbytes
    except ImportError as e:
        return

    ForkingPickler.register(bitsandbytes.nn.modules.Params4bit, _reduce_bnb_param)
    ForkingPickler.register(bitsandbytes.nn.modules.Int8Params, _reduce_bnb_param)

Model yang dikuantisasi melalui bitsandbytes dilengkapi dengan hooks dan monkey-patches yang tidak dapat dipickle, kita harus menghapusnya:

python
from accelerate.hooks import remove_hook_from_module
remove_hook_from_module(model, True)
model.__dict__.pop('to', None)  # Hapus monkeypatches peringatan
model.__dict__.pop('cuda', None)

Kami juga mengalami masalah di mana fungsi bersarang dalam fungsi lain (bukan berada di tingkat atas), yang membuatnya tidak dapat dipickle. Kami mencoba mengatasinya, tetapi tidak berhasil. Kami harus mengganti pickle kami dari yang disediakan stdlib ke dill untuk mempickle ini. dill jauh lebih kuat, tetapi ini adalah implementasi Python murni, yang jauh lebih lambat daripada versi pustaka standar. Untungnya, biaya ini hanya akan dibayar sekali saat kita memuat model pertama kali (hanya mempengaruhi pickling, bukan unpickling).

Dukungan untuk stable-fast

stable-fast menghasilkan hasil torch.compile, yang tidak dapat dipickle. Tetapi dengan torch.jit.save, kita bisa menyimpan hasilnya sebagai file zip. Ini terdengar tidak efisien, tetapi lebih baik daripada tidak sama sekali.

Dengan hanya torch.jit.save tidak cukup untuk mempickle hasil stable-fast. stable-fast menggunakan proses 'flatten' untuk membuat modul Torch dapat dilacak. Ketika menemui sesuatu yang tidak dikenalnya (misalnya, kelas dataclass), itu tidak akan menyerialisasikannya, tetapi hanya akan menyimpan referensi ke kelas yang sebenarnya. Kami telah menambal logika terkait untuk benar-benar menyimpan kelas yang dipickle dalam aliran 'flatten'.

python
def stable_fast_quirks():
    ...

    # pickle dataclass type instead of just put it into a container (which will not survive after torch.jit.save)
    def flatten_dataclass(obj):
        from sfast.utils.flat_tensors import flatten_bytes, flatten_dict
        import dataclasses
        d = dict((field.name, getattr(obj, field.name))
                for field in dataclasses.fields(obj))
        import pickle
        pickled = pickle.dumps(obj.__class__)
        return flatten_bytes(pickled) + flatten_dict(d)

    def unflatten_dataclass(tensors, start):
        from sfast.utils.flat_tensors import unflatten_bytes, unflatten_dict
        import pickle
        pickled, start = unflatten_bytes(tensors, start)
        clz = pickle.loads(pickled)
        content, start = unflatten_dict(tensors, start)
        return clz(**content), start

    sfast.utils.flat_tensors.flatten_dataclass = flatten_dataclass
    sfast.utils.flat_tensors.unflatten_dataclass = unflatten_dataclass

Ada dua trik lagi di sini:

  1. Kami mengemas ulang file ZIP dengan ZIP_STORED, sehingga kami tidak perlu mendekompresi file ZIP untuk setiap pemuatan berikutnya.
  2. Antarmuka torch.jit.load juga mengalami masalah penyalinan memori, jadi kami menulis pembungkus sederhana untuk memuatnya melalui protokol buffer Python, seperti UntypedStorage.
cpp
void initOvermindHelpers(py::module m) {
    // ...
    m.def("import_ir_module_from_buffer_0copy",
        [](std::shared_ptr<torch::jit::CompilationUnit> cu, py::buffer buffer) {
            auto info = buffer.request();
            imemstream in((char*)info.ptr, info.size);  // Tidak ada salinan!
            return import_ir_module(std::move(cu), in, ...);
        }
    );
}

Pola vae=vae

Basis kode kami memiliki sesuatu seperti ini, mencoba memuat model dengan model yang dimuat sebelumnya sebagai argumennya:

python
import overmind.api
overmind.api.monkey_patch_all()

import torch
from diffusers.models import AutoencoderKL

from diffusers import (
    ControlNetModel,
    StableDiffusionControlNetPipeline,
)

vae = AutoencoderKL.from_pretrained(
    "lemon2431/ChineseInkComicStrip_v10",
    subfolder="vae",
    torch_dtype=torch.float16,
)
controlnet_depth = ControlNetModel.from_pretrained(
    "lllyasviel/control_v11f1p_sd15_depth",
    torch_dtype=torch.float16,
    variant="fp16",
)
controlnet_edge = ControlNetModel.from_pretrained(
    "lllyasviel/control_v11p_sd15_softedge",
    torch_dtype=torch.float16,
    variant="fp16",
)

pipeline = StableDiffusionControlNetPipeline.from_pretrained(
    "lemon2431/ChineseInkComicStrip_v10",
    vae=vae,  # Di sini!
    controlnet=[controlnet_edge, controlnet_depth],  # dan Di sini!
    torch_dtype=torch.float16,
    safety_checker=None,
)

pipeline.to('cuda')

Seperti yang kami sebutkan sebelumnya, argumen fungsi diasumsikan sebagai objek sederhana yang mudah dipickle, tetapi pola ini melanggar asumsi tersebut. Untuk menangani ini, kami menambahkan logika khusus: setiap hasil cache mendapatkan ID terlampir. Jika objek tersebut digunakan sebagai argumen dalam panggilan lain, klien menggantinya dengan ID-nya, dan server kemudian dapat memulihkan objek sebenarnya berdasarkan ID tersebut.

Model pipeline yang dihasilkan akan berisi referensi ke vae. Untuk kesederhanaan, kami hanya mempickle-nya langsung di sini. Namun, saat memindahkan UntypedStorage yang sebenarnya ke memori bersama, kami mendeduplikasi data yang berulang.

Kami mungkin telah menggunakan mekanisme persistent_id dari pickle, tetapi saya tidak mencoba jalur ini. Itu agak disayangkan.

Pengujian Kinerja

Dan sekarang bagian yang semua orang suka lihat.

Kami menggunakan skrip pola VAE dari bagian terakhir untuk melakukan pengujian kami.

Ujivaedepthedgepipelineto('cuda')Total
tanpa, 1st1.180.981.411.650.916.16
tanpa, 2nd1.150.960.971.650.895.66
tanpa, 3rd1.150.960.981.610.915.65
w/o, 4th1.421.101.111.720.886.27
w/o, 5th1.281.081.101.720.926.13
w/, 1st5.445.175.417.290.8624.20
w/, 2nd0.000.010.010.200.871.12
w/, 3rd0.010.010.010.210.861.12
w/, 4th0.010.010.010.200.901.15
w/, 5th0.010.010.010.210.861.13

Seperti yang dapat Anda lihat, pemuatan awal dengan overmind memakan waktu 24,2 detik, yang secara signifikan lebih lama dibandingkan dengan pemuatan tanpa itu. Namun, pada pemuatan berikutnya, hanya biaya .to('cuda') yang masih ada.

Menambahkan ukuran semua file model yang diserialisasi, seluruh pipeline diperkirakan menggunakan sekitar 5808 megabyte memori. Sebuah benchmark cepat memberikan hasil yang serupa.

text
In [1]: t = torch.ones((5808, 1024, 1024), dtype=torch.uint8)

In [2]: %time a = t.cuda()
CPU times: user 976 ms, sys: 874 μs, total: 977 ms
Wall time: 976 ms

In [3]: %timeit a = t.cuda()
1.01 s ± 56.7 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

Diuji pada Intel i9-11900K + GeForce RTX 4090.

Efek Samping yang Tidak Terduga (Positif!)

Motivasi utama kami untuk membangun overmind adalah untuk memungkinkan pergantian cepat bobot model selama inferensi. Meskipun itu memenuhi tujuannya, kami menemukan beberapa keuntungan tambahan di sepanjang jalan.

Kami menerapkan beberapa instance dari aplikasi kami, satu untuk setiap GPU. Dengan demikian, akan ada 8 proses per node. Setelah kami menerapkan overmind, penggunaan memori sistem berkurang secara dramatis. Kami tidak mengalami kekurangan memori sistem, tetapi jika kami mengalaminya, ini akan menjadi kemenangan besar.

Kemudian, kami menemukan bahwa ini memberikan dorongan besar bagi pengembang algoritma dan pipeline kami. Untuk setiap loop modifikasi-verifikasi, kami bisa menghemat 10 hingga 20 detik waktu pemuatan, ini bisa bertambah menjadi angka yang besar. Yang lebih penting, detik yang dihemat dapat menjaga pengembang tetap dalam aliran.

Github

Kami membuka sumbernya di Github, kami akan senang jika ini membantu.

Lihat Apa yang Dapat Dipercepat oleh Inferensi
Overmind mendukung kecepatan di balik generasi AI 3D Meshy. Cobalah dan lihat hasilnya secara langsung.
Apakah postingan ini bermanfaat?

3D, Atas Permintaan

Hubungi Penjualan