ncnn Neural Network Inference Framework

repository·master·Indexed 12 days ago

https://github.com/tencent/ncnn

A high-performance neural network inference framework optimized for mobile, embedded, and desktop deployment. It features no third-party runtime dependencies and supports CPU and Vulkan GPU backends. Includes the benchncnn tool for measuring inference performance and benchncnn_llm for benchmarking Large Language Models.

Tokens
114.1K
Snippets
354
Records
491
Agent score
94%

What's inside ncnn

  1. Overview of ncnn

    master
    ncnn is a high-performance neural network inference framework optimized for mobile, embedded, and desktop deployment. It is designed to have no third-party runtime dependencies and supports both CPU and Vulkan GPU backends. Developers can use it to deploy deep learning models efficiently on phones, PCs, browsers, and edge devices.
  2. Overview of ncnn features

    master

    ncnn is a high-performance neural network inference framework with the following characteristics:

    • Zero Dependencies: No third-party runtime dependencies; no requirement for BLAS or NNPACK.
    • Multi-Language: Pure C++ implementation with a C API and Python bindings.
    • Hardware Optimization: Optimized for mobile/embedded CPUs (ARM NEON, multi-core) and Vulkan GPU acceleration.
    • Memory Efficient: Low memory footprint via explicit blob/workspace allocator design.
    • Flexible Graphs: Supports multi-input, multi-output, and multi-branch architectures.
    • Advanced Inference: Supports fp16 arithmetic, int8 quantization, and custom layers.
  3. Key features of PNNX

    master

    PNNX offers several advantages for model deployment and interoperability:

    • Human-readable format: Uses a .param file that is easy to read and edit.
    • Efficient storage: Uses a .bin format stored within a zip archive.
    • High fidelity: Maintains a one-to-one mapping between PNNX operators and PyTorch Python APIs.
    • Operator preservation: Preserves math expressions, torch functions, and miscellaneous modules as single operators rather than breaking them down into many small pieces.
    • Advanced capabilities: Supports tensor shape propagation, model optimization, and custom operator support.
    • Python inference: Allows for inference via exported PyTorch Python code.
  4. What is element packing in ncnn

    master

    Element packing is a technique used to store multiple short-sized values as a single long-sized value. This is designed to map efficiently to SIMD (Single Instruction, Multiple Data) registers, which use wide registers to process multiple values simultaneously.

    When using elempack > 1, the ncnn::Mat structure treats the wide-sized value as a single element. Consequently, the logical width or dimension of the Mat is reduced by the elempack factor.

    Example: If you want to store 40 float values (where elemsize is 4):

    • Using elempack = 1: Mat width is 40.
    • Using elempack = 4: Mat width is 10.

    Common Packing Mappings:

    Typeelemsizeelempack
    double81
    float41
    int41
    short21
    signed char11

    ARM NEON Mappings:

    Typeelemsizeelempack
    float64x2_t162
    float32x4_t164
    int32x4_t164
    float16x4_t84
    int8x8_t88
  5. What is PNNX (PyTorch Neural Network eXchange)

    master
    PNNX is an open standard for PyTorch model interoperability. It provides an open model format that defines the computation graph and high-level operators in a way that strictly matches the PyTorch Python API. Unlike ONNX, PNNX aims to provide a human-readable and editable format, avoid the addition of 'glue operators' during export, and reduce the burden on hardware/software by avoiding unnecessary compatibility parameters.
  6. Use zero-copy on unified memory devices

    master

    On devices with unified memory, you can access GPU memory directly from the CPU by using the .mapped() method on an ncnn::VkMat. This allows you to use the pointer returned by mapped().data directly without explicit copies.

    ncnn::VkMat blob_gpu;
    ncnn::Mat mapped = blob_gpu.mapped();
    
    // use mapped.data directly
  7. Use expressions in Reshape layers for dynamic shapes

    master

    In ncnn, the Reshape layer can use an expression (indicated by the prefix 6=) to define dynamic shapes or subscript values based on input shapes. This is more efficient than using multiple arithmetic operators because it reduces model complexity and avoids the overhead of kernel calls for simple single-digit operations by performing them directly on the CPU.

    When using the pnnx tool, pnnx.Expression and Tensor.reshape/Tensor.view operators are automatically fused into a single ncnn Reshape layer with an expression string.

    Example Conversion:

    Python (pnnx):

    shape = [(B.size(0) + 2), (A.size(1) * 2), -1]
    out = A.reshape(*shape)

    ncnn.param:

    Reshape          reshape  2 1 A B out 6="-1,*(0h,2),+(1c,2)"
    Reshape          reshape  2 1 A B out 6="-1,*(0h,2),+(1c,2)"
  8. Optimize AHB Import Performance via Caching

    master

    Creating an ImportAndroidHardwareBufferPipeline is expensive (~24 ms median on Adreno 830). Since the pipeline parameters (sampler, rotation, target size) are usually stable for a camera session, you should cache the allocator and pipeline using the AHardwareBuffer* as a key.

    Note: VkAndroidHardwareBufferImageAllocator does not take a reference on the AHB. If you cache the allocator, you must call AHardwareBuffer_acquire(ahb) to ensure the buffer remains valid, and AHardwareBuffer_release(ahb) when evicting the cache.

    Caching Pattern Example

    struct CacheEntry {
        ncnn::VkAndroidHardwareBufferImageAllocator* alloc;
        ncnn::ImportAndroidHardwareBufferPipeline*   pipe;
        ncnn::VkImageMat                             src;
    };
    static std::unordered_map<AHardwareBuffer*, CacheEntry> cache;  // camera-thread only
    
    auto it = cache.find(ahb);
    if (it == cache.end()) {
        AHardwareBuffer_acquire(ahb);
        auto* a = new ncnn::VkAndroidHardwareBufferImageAllocator(vkdev, ahb);
        auto  s = ncnn::VkImageMat::from_android_hardware_buffer(a);
        auto* p = new ncnn::ImportAndroidHardwareBufferPipeline(vkdev);
        p->create(a, 1, 1, width, height, opt);
        cache[ahb] = { a, p, std::move(s) };
        it = cache.find(ahb);
    }
    const CacheEntry& e = it->second;
    cmd.record_import_android_hardware_buffer(e.pipe, e.src, dst);
  9. Interface selection guide for forward behavior

    master

    The ncnn::Layer base class provides four interfaces. You must implement the one marked as must for your specific combination of one_blob_only and support_inplace. Implementing the optional version can improve performance by avoiding deep copies.

    one_blob_onlysupport_inplace1 (Multi-blob forward)2 (Single-blob forward)3 (In-place multi-blob)4 (In-place single-blob)
    falsefalsemust
    falsetrueoptionalmust
    truefalsemust
    truetrueoptionalmust

    Interface Definitions:

    1. virtual int forward(const std::vector<Mat>& bottom_blobs, std::vector<Mat>& top_blobs, const Option& opt) const; (Multi-input/output)
    2. virtual int forward(const Mat& bottom_blob, Mat& top_blob, const Option& opt) const; (Single-input/output)
    3. virtual int forward_inplace(std::vector<Mat>& bottom_top_blobs, const Option& opt) const; (Multi-input/output, in-place)
    4. virtual int forward_inplace(Mat& bottom_top_blob, const Option& opt) const; (Single-input/output, in-place)
  10. Optimize memory usage with Light Mode

    master

    By default, ncnn keeps blobs (intermediate results) in memory. For most deep networks, you only need the final result or specific branch results. Enabling Light Mode allows ncnn to automatically reclaim memory for blobs that are no longer needed after a layer's computation is complete. This significantly reduces the memory footprint during inference.

    Example behavior: In a network A -> B -> C, when requesting result C in Light Mode:

    1. A's result is reclaimed when B starts computing.
    2. B's result is reclaimed when C starts computing.
    3. Only the final result C is retained in memory.
  11. Use the ncnn SimpleVK loader for Vulkan acceleration

    master

    ncnn includes a built-in Vulkan loader called SimpleVK (enabled via the NCNN_SIMPLEVK CMake option when NCNN_VULKAN is on).

    SimpleVK allows you to use Vulkan without requiring the full Vulkan SDK on the development machine or the target system. It can dynamically load the Vulkan runtime or graphics drivers at runtime, making it easier to distribute applications without explicit libvulkan linkage.

    For most users, manual management of the Vulkan instance is unnecessary; simply enabling the Vulkan compute option in the ncnn::Net configuration is sufficient.

    ncnn::Net net;
    net.opt.use_vulkan_compute = true;
    net.load_param("model.param");
    net.load_param("model.bin");
  12. Perform PNNX shape propagation

    master

    PNNX can resolve all tensor shapes in a model graph and constantify common expressions when shapes are known. This is an optional process enabled by providing the inputshape command line option.

    Providing inputshape allows PNNX to transform dynamic operations (like view or reshape based on input size) into static, optimized operations.

    pnnx shufflenet_v2_x1_0.pt inputshape=[1,3,224,224]