ncnn Neural Network Inference Framework
repository·master·Indexed 12 days ago
https://github.com/tencent/ncnnA 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.
What's inside ncnn
- 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.
Overview of ncnn features
masterncnn 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.
Key features of PNNX
masterPNNX offers several advantages for model deployment and interoperability:
- Human-readable format: Uses a
.paramfile that is easy to read and edit. - Efficient storage: Uses a
.binformat 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,
torchfunctions, 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.
- Human-readable format: Uses a
What is element packing in ncnn
masterElement 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, thencnn::Matstructure treats the wide-sized value as a single element. Consequently, the logical width or dimension of theMatis reduced by theelempackfactor.Example: If you want to store 40
floatvalues (whereelemsizeis 4):- Using
elempack = 1:Matwidth is 40. - Using
elempack = 4:Matwidth is 10.
Common Packing Mappings:
Type elemsizeelempackdouble8 1 float4 1 int4 1 short2 1 signed char1 1 ARM NEON Mappings:
Type elemsizeelempackfloat64x2_t16 2 float32x4_t16 4 int32x4_t16 4 float16x4_t8 4 int8x8_t8 8 - Using
What is PNNX (PyTorch Neural Network eXchange)
masterPNNX 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.Use zero-copy on unified memory devices
masterOn devices with unified memory, you can access GPU memory directly from the CPU by using the
.mapped()method on anncnn::VkMat. This allows you to use the pointer returned bymapped().datadirectly without explicit copies.ncnn::VkMat blob_gpu; ncnn::Mat mapped = blob_gpu.mapped(); // use mapped.data directlyUse expressions in Reshape layers for dynamic shapes
masterIn ncnn, the
Reshapelayer can use anexpression(indicated by the prefix6=) 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
pnnxtool,pnnx.ExpressionandTensor.reshape/Tensor.viewoperators are automatically fused into a single ncnnReshapelayer 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)"Optimize AHB Import Performance via Caching
masterCreating an
ImportAndroidHardwareBufferPipelineis 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 theAHardwareBuffer*as a key.Note:
VkAndroidHardwareBufferImageAllocatordoes not take a reference on the AHB. If you cache the allocator, you must callAHardwareBuffer_acquire(ahb)to ensure the buffer remains valid, andAHardwareBuffer_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);Interface selection guide for forward behavior
masterThe
ncnn::Layerbase class provides four interfaces. You must implement the one marked as must for your specific combination ofone_blob_onlyandsupport_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 falsetrueoptional must truefalsemust truetrueoptional must Interface Definitions:
virtual int forward(const std::vector<Mat>& bottom_blobs, std::vector<Mat>& top_blobs, const Option& opt) const;(Multi-input/output)virtual int forward(const Mat& bottom_blob, Mat& top_blob, const Option& opt) const;(Single-input/output)virtual int forward_inplace(std::vector<Mat>& bottom_top_blobs, const Option& opt) const;(Multi-input/output, in-place)virtual int forward_inplace(Mat& bottom_top_blob, const Option& opt) const;(Single-input/output, in-place)
Optimize memory usage with Light Mode
masterBy 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 resultCin Light Mode:A's result is reclaimed whenBstarts computing.B's result is reclaimed whenCstarts computing.- Only the final result
Cis retained in memory.
Use the ncnn SimpleVK loader for Vulkan acceleration
masterncnn includes a built-in Vulkan loader called
SimpleVK(enabled via theNCNN_SIMPLEVKCMake option whenNCNN_VULKANis 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
libvulkanlinkage.For most users, manual management of the Vulkan instance is unnecessary; simply enabling the Vulkan compute option in the
ncnn::Netconfiguration is sufficient.ncnn::Net net; net.opt.use_vulkan_compute = true; net.load_param("model.param"); net.load_param("model.bin");Perform PNNX shape propagation
masterPNNX 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
inputshapecommand line option.Providing
inputshapeallows PNNX to transform dynamic operations (likevieworreshapebased on input size) into static, optimized operations.pnnx shufflenet_v2_x1_0.pt inputshape=[1,3,224,224]