การเร่ง GPU ด้วย LiteRT

โดยทั่วไปแล้ว หน่วยประมวลผลกราฟิก (GPU) มักใช้สำหรับการเร่งความเร็วดีปเลิร์นนิง เนื่องจากมีปริมาณงานแบบขนานจำนวนมากเมื่อเทียบกับ CPU LiteRT ช่วยให้กระบวนการใช้การเร่งความเร็ว GPU ง่ายขึ้นโดยอนุญาตให้ผู้ใช้ ระบุการเร่งความเร็วฮาร์ดแวร์เป็นพารามิเตอร์เมื่อสร้างโมเดลที่คอมไพล์แล้ว (CompiledModel)

การเร่งความเร็ว GPU ของ LiteRT ช่วยให้คุณสร้างบัฟเฟอร์อินพุตและเอาต์พุตที่เหมาะกับ GPU ได้ บรรลุการคัดลอกเป็นศูนย์ด้วยข้อมูลในหน่วยความจำ GPU และเรียกใช้ งานแบบอะซิงโครนัสเพื่อเพิ่มการทำงานแบบคู่ขนานให้สูงสุด

เริ่มต้นใช้งาน

เพิ่มการพึ่งพา GPU

ทําตามขั้นตอนต่อไปนี้เพื่อเพิ่มการอ้างอิง GPU ลงในแอปพลิเคชัน Kotlin หรือ C++

Kotlin

สำหรับผู้ใช้ Kotlin ตัวเร่ง GPU จะมีในตัวและไม่จำเป็นต้องมีขั้นตอนเพิ่มเติมใดๆ นอกเหนือจากคำแนะนำเริ่มต้นใช้งาน

C++

สำหรับผู้ใช้ C++ คุณต้องสร้างการอ้างอิงของแอปพลิเคชันด้วยการเร่งความเร็ว LiteRT GPU cc_binary กฎที่จัดแพ็กเกจตรรกะของแอปพลิเคชันหลัก (เช่น main.cc) ต้องใช้คอมโพเนนต์รันไทม์ต่อไปนี้

  • ไลบรารีที่ใช้ร่วมกันของ LiteRT C API: แอตทริบิวต์ data ต้องมี ไลบรารีที่ใช้ร่วมกันของ LiteRT C API (//litert/c:litert_runtime_c_api_shared_lib) และคอมโพเนนต์เฉพาะ GPU (@litert_gpu//:jni/arm64-v8a/libLiteRtGpuAccelerator.so)
  • การอ้างอิงแอตทริบิวต์: โดยปกติแล้วแอตทริบิวต์ deps จะมีการอ้างอิง GLES gles_deps() และ linkopts มักจะมี gles_linkopts() ทั้ง 2 อย่างมีความเกี่ยวข้องอย่างมากกับการเร่งความเร็ว GPU เนื่องจาก LiteRT มักใช้ OpenGLES ใน Android
  • ไฟล์โมเดลและชิ้นงานอื่นๆ: รวมไว้ผ่านแอตทริบิวต์ data

ตัวอย่างcc_binaryกฎมีดังนี้

cc_binary(
    name = "your_application",
    srcs = [
        "main.cc",
    ],
    data = [
        ...
        # litert c api shared library
        "//litert/c:litert_runtime_c_api_shared_lib",
        # GPU accelerator shared library
        "@litert_gpu//:jni/arm64-v8a/libLiteRtGpuAccelerator.so",
    ],
    linkopts = select({
        "@org_tensorflow//tensorflow:android": ["-landroid"],
        "//conditions:default": [],
    }) + gles_linkopts(), # gles link options
    deps = [
        ...
        "//litert/cc:litert_tensor_buffer", # litert cc library
        ...
    ] + gles_deps(), # gles dependencies
)

การตั้งค่านี้ช่วยให้ไบนารีที่คอมไพล์โหลดและใช้ GPU แบบไดนามิกเพื่อการอนุมานแมชชีนเลิร์นนิงที่เร่งความเร็วได้

ใช้ GPU กับ CompiledModel API

หากต้องการเริ่มต้นใช้งานตัวเร่ง GPU ให้ส่งพารามิเตอร์ GPU เมื่อสร้างโมเดลที่คอมไพล์แล้ว (CompiledModel) ข้อมูลโค้ดต่อไปนี้แสดงการใช้งานพื้นฐานของกระบวนการทั้งหมด

C++

// 1. Load model
LITERT_ASSIGN_OR_RETURN(auto model, Model::CreateFromFile("mymodel.tflite"));

// 2. Create a compiled model targeting GPU
LITERT_ASSIGN_OR_RETURN(auto env, Environment::Create({}));
LITERT_ASSIGN_OR_RETURN(auto compiled_model, CompiledModel::Create(env, model, kLiteRtHwAcceleratorGpu));

// 3. Prepare input/output buffers
LITERT_ASSIGN_OR_RETURN(auto input_buffers, compiled_model.CreateInputBuffers());
LITERT_ASSIGN_OR_RETURN(auto output_buffers, compiled_model.CreateOutputBuffers());

// 4. Fill input data (if you have CPU-based data)
input_buffers[0].Write<float>(absl::MakeConstSpan(cpu_data, data_size));

// 5. Execute
compiled_model.Run(input_buffers, output_buffers);

// 6. Access model output
std::vector<float> data(output_data_size);
output_buffers.Read<float>(absl::MakeSpan(data));

Kotlin

// Load model and initialize runtime
val  model =
    CompiledModel.create(
        context.assets,
        "mymodel.tflite",
        CompiledModel.Options(Accelerator.GPU),
        env,
    )

// Preallocate input/output buffers
val inputBuffers = model.createInputBuffers()
val outputBuffers = model.createOutputBuffers()

// Fill the first input
inputBuffers[0].writeFloat(FloatArray(data_size) { data_value /* your data */ })

// Invoke
model.run(inputBuffers, outputBuffers)

// Read the output
val outputFloatArray = outputBuffers[0].readFloat()

ดูข้อมูลเพิ่มเติมได้ที่คำแนะนำเริ่มต้นใช้งาน C++ หรือเริ่มต้นใช้งาน Kotlin

การคัดลอกเป็นศูนย์พร้อมการเร่งด้วย GPU

การใช้การคัดลอกแบบไม่ต้องทำสำเนาช่วยให้ GPU เข้าถึงข้อมูลในหน่วยความจำของตัวเองได้โดยตรงโดยไม่ต้องให้ CPU คัดลอกข้อมูลนั้นอย่างชัดเจน การไม่คัดลอกข้อมูลไปยังและจากหน่วยความจำ CPU จะช่วยลดเวลาในการตอบสนองแบบต้นทางถึงปลายทางได้อย่างมาก

โค้ดต่อไปนี้เป็นตัวอย่างการใช้งาน GPU แบบ Zero-Copy ด้วย OpenGL ซึ่งเป็น API สำหรับการแสดงผลกราฟิกแบบเวกเตอร์ โค้ดจะส่งรูปภาพในรูปแบบบัฟเฟอร์ OpenGL ไปยัง LiteRT โดยตรง

// Suppose you have an OpenGL buffer consisting of:
// target (GLenum), id (GLuint), size_bytes (size_t), and offset (size_t)
// Load model and compile for GPU
LITERT_ASSIGN_OR_RETURN(auto model, Model::CreateFromFile("mymodel.tflite"));
LITERT_ASSIGN_OR_RETURN(auto env, Environment::Create({}));
LITERT_ASSIGN_OR_RETURN(auto compiled_model,
    CompiledModel::Create(env, model, kLiteRtHwAcceleratorGpu));

// Create a TensorBuffer that wraps the OpenGL buffer.
LITERT_ASSIGN_OR_RETURN(auto tensor_type, model.GetInputTensorType("input_tensor_name"));
LITERT_ASSIGN_OR_RETURN(auto gl_input_buffer, TensorBuffer::CreateFromGlBuffer(env,
    tensor_type, opengl_buffer.target, opengl_buffer.id, opengl_buffer.size_bytes, opengl_buffer.offset));
std::vector<TensorBuffer> input_buffers{gl_input_buffer};
LITERT_ASSIGN_OR_RETURN(auto output_buffers, compiled_model.CreateOutputBuffers());

// Execute
compiled_model.Run(input_buffers, output_buffers);

// If your output is also GPU-backed, you can fetch an OpenCL buffer or re-wrap it as an OpenGL buffer:
LITERT_ASSIGN_OR_RETURN(auto out_cl_buffer, output_buffers[0].GetOpenClBuffer());

การดำเนินการแบบอะซิงโครนัส

เมธอดแบบไม่พร้อมกันของ LiteRT เช่น RunAsync() ช่วยให้คุณกำหนดเวลาการอนุมานของ GPU ในขณะที่ทำงานอื่นๆ ต่อไปโดยใช้ CPU หรือ NPU ได้ ในไปป์ไลน์ที่ซับซ้อน มักใช้ GPU แบบอะซิงโครนัสควบคู่ไปกับ CPU หรือ NPU

ข้อมูลโค้ดต่อไปนี้สร้างขึ้นจากโค้ดที่ระบุไว้ในตัวอย่างการเร่งความเร็ว GPU แบบไม่คัดลอก โค้ดใช้ทั้ง CPU และ GPU แบบไม่พร้อมกัน และแนบ LiteRT Event ไปยังบัฟเฟอร์อินพุต LiteRT Event มีหน้าที่จัดการ Primitive การซิงค์ประเภทต่างๆ และ โค้ดต่อไปนี้จะสร้างออบเจ็กต์เหตุการณ์ LiteRT ที่มีการจัดการประเภท LiteRtEventTypeEglSyncFence Eventออบเจ็กต์นี้ช่วยให้มั่นใจได้ว่าเราจะไม่ อ่านจากบัฟเฟอร์อินพุตจนกว่า GPU จะทำงานเสร็จ ซึ่งทั้งหมดนี้จะดำเนินการโดยไม่ต้องใช้ CPU

LITERT_ASSIGN_OR_RETURN(auto env, Environment::Create({}));
LITERT_ASSIGN_OR_RETURN(auto compiled_model,
    CompiledModel::Create(env, model, kLiteRtHwAcceleratorGpu));

// 1. Prepare input buffer (OpenGL buffer)
LITERT_ASSIGN_OR_RETURN(auto gl_input,
    TensorBuffer::CreateFromGlBuffer(env, tensor_type, opengl_tex));
std::vector<TensorBuffer> inputs{gl_input};
LITERT_ASSIGN_OR_RETURN(auto outputs, compiled_model.CreateOutputBuffers());

// 2. If the GL buffer is in use, create and set an event object to synchronize with the GPU.
LITERT_ASSIGN_OR_RETURN(auto input_event,
    Event::CreateManagedEvent(env, LiteRtEventTypeEglSyncFence));
inputs[0].SetEvent(std::move(input_event));

// 3. Kick off the GPU inference
compiled_model.RunAsync(inputs, outputs);

// 4. Meanwhile, do other CPU work...
// CPU Stays busy ..

// 5. Access model output
std::vector<float> data(output_data_size);
outputs[0].Read<float>(absl::MakeSpan(data));

โมเดลที่รองรับ

LiteRT รองรับการเร่งความเร็วด้วย GPU กับโมเดลต่อไปนี้ ผลการทดสอบประสิทธิภาพ อิงตามการทดสอบที่ดำเนินการในอุปกรณ์ Samsung Galaxy S24

โมเดล การเร่งความเร็ว GPU ของ LiteRT GPU ของ LiteRT (มิลลิวินาที)
hf_mms_300m มอบสิทธิ์ทั้งหมด 19.6
hf_mobilevit_small มอบสิทธิ์ทั้งหมด 8.7
hf_mobilevit_small_e2e มอบสิทธิ์ทั้งหมด 8.0
hf_wav2vec2_base_960h มอบสิทธิ์ทั้งหมด 9.1
hf_wav2vec2_base_960h_dynamic มอบสิทธิ์ทั้งหมด 9.8
isnet มอบสิทธิ์ทั้งหมด 43.1
timm_efficientnet มอบสิทธิ์ทั้งหมด 3.7
timm_nfnet มอบสิทธิ์ทั้งหมด 9.7
timm_regnety_120 มอบสิทธิ์ทั้งหมด 12.1
torchaudio_deepspeech มอบสิทธิ์ทั้งหมด 4.6
torchaudio_wav2letter มอบสิทธิ์ทั้งหมด 4.8
torchvision_alexnet มอบสิทธิ์ทั้งหมด 3.3
torchvision_deeplabv3_mobilenet_v3_large มอบสิทธิ์ทั้งหมด 5.7
torchvision_deeplabv3_resnet101 มอบสิทธิ์ทั้งหมด 35.1
torchvision_deeplabv3_resnet50 มอบสิทธิ์ทั้งหมด 24.5
torchvision_densenet121 มอบสิทธิ์ทั้งหมด 13.9
torchvision_efficientnet_b0 มอบสิทธิ์ทั้งหมด 3.6
torchvision_efficientnet_b1 มอบสิทธิ์ทั้งหมด 4.7
torchvision_efficientnet_b2 มอบสิทธิ์ทั้งหมด 5.0
torchvision_efficientnet_b3 มอบสิทธิ์ทั้งหมด 6.1
torchvision_efficientnet_b4 มอบสิทธิ์ทั้งหมด 7.6
torchvision_efficientnet_b5 มอบสิทธิ์ทั้งหมด 8.6
torchvision_efficientnet_b6 มอบสิทธิ์ทั้งหมด 11.2
torchvision_efficientnet_b7 มอบสิทธิ์ทั้งหมด 14.7
torchvision_fcn_resnet50 มอบสิทธิ์ทั้งหมด 19.9
torchvision_googlenet มอบสิทธิ์ทั้งหมด 3.9
torchvision_inception_v3 มอบสิทธิ์ทั้งหมด 8.6
torchvision_lraspp_mobilenet_v3_large มอบสิทธิ์ทั้งหมด 3.3
torchvision_mnasnet0_5 มอบสิทธิ์ทั้งหมด 2.4
torchvision_mobilenet_v2 มอบสิทธิ์ทั้งหมด 2.8
torchvision_mobilenet_v3_large มอบสิทธิ์ทั้งหมด 2.8
torchvision_mobilenet_v3_small มอบสิทธิ์ทั้งหมด 2.3
torchvision_resnet152 มอบสิทธิ์ทั้งหมด 15.0
torchvision_resnet18 มอบสิทธิ์ทั้งหมด 4.3
torchvision_resnet50 มอบสิทธิ์ทั้งหมด 6.9
torchvision_squeezenet1_0 มอบสิทธิ์ทั้งหมด 2.9
torchvision_squeezenet1_1 มอบสิทธิ์ทั้งหมด 2.5
torchvision_vgg16 มอบสิทธิ์ทั้งหมด 13.4
torchvision_wide_resnet101_2 มอบสิทธิ์ทั้งหมด 25.0
torchvision_wide_resnet50_2 มอบสิทธิ์ทั้งหมด 13.4
u2net_full มอบสิทธิ์ทั้งหมด 98.3
u2net_lite มอบสิทธิ์ทั้งหมด 51.4
hf_distil_whisper_small_no_cache มอบสิทธิ์บางส่วน 251.9
hf_distilbert มอบสิทธิ์บางส่วน 13.7
hf_tinyroberta_squad2 มอบสิทธิ์บางส่วน 17.1
hf_tinyroberta_squad2_dynamic_batch มอบสิทธิ์บางส่วน 52.1
snapml_StyleTransferNet มอบสิทธิ์บางส่วน 40.9
timm_efficientformer_l1 มอบสิทธิ์บางส่วน 17.6
timm_efficientformerv2_s0 มอบสิทธิ์บางส่วน 16.1
timm_pvt_v2_b1 มอบสิทธิ์บางส่วน 73.5
timm_pvt_v2_b3 มอบสิทธิ์บางส่วน 246.7
timm_resnest14d มอบสิทธิ์บางส่วน 88.9
torchaudio_conformer มอบสิทธิ์บางส่วน 21.5
torchvision_convnext_tiny มอบสิทธิ์บางส่วน 8.2
torchvision_maxvit_t มอบสิทธิ์บางส่วน 194.0
torchvision_shufflenet_v2 มอบสิทธิ์บางส่วน 9.5
torchvision_swin_tiny มอบสิทธิ์บางส่วน 164.4
torchvision_video_resnet2plus1d_18 มอบสิทธิ์บางส่วน 6832.0
torchvision_video_swin3d_tiny มอบสิทธิ์บางส่วน 2617.8
yolox_tiny มอบสิทธิ์บางส่วน 11.2