How to Load ncnn Model Parameters from Memory Using load_param_mem()

The ncnn::Net::load_param_mem() function wraps a resident memory buffer containing a text-based .param file in a DataReaderFromMemory instance to eliminate file-system I/O, while binary weight buffers loaded via load_model() must be 4-byte aligned to avoid undefined behavior on strict-alignment CPU architectures.

Tencent/ncnn provides load_param_mem() for embedded and mobile deployments where reading from disk is impractical or where models reside in encrypted or pre-loaded memory segments. This method, implemented in src/net.cpp, streams the ASCII parameter definition directly from the supplied pointer while delegating weight loading to separate memory-aware functions that enforce 32-bit alignment.

How load_param_mem() Works Internally

Wrapping Memory in DataReaderFromMemory

The implementation constructs a lightweight DataReaderFromMemory object that maintains a cursor into the supplied buffer. Located in src/net.cpp at lines 58-63, the method casts the const char* to an unsigned byte pointer and forwards it to the internal load_param(DataReader&) dispatcher:

const unsigned char* mem = reinterpret_cast<const unsigned char*>(_mem);
DataReaderFromMemory dr(mem);
return load_param(dr);

DataReaderFromMemory (defined in src/datareader.cpp) implements the abstract DataReader interface by advancing an internal pointer as scan() and read() operations consume data, eliminating temporary copies or buffering.

Parsing the Parameter Dictionary

Once wrapped, the buffer feeds into ParamDict::load_param(), found in src/paramdict.cpp (lines 63-84). This parser reads key-value pairs using sscanf patterns such as "%d=" to extract layer IDs, followed by type-specific scans for integers and floats. Because the data is treated as null-terminated text, the parser performs no speculative pointer casting, allowing the buffer to reside at any memory address regardless of alignment.

Language Bindings and C API

The functionality is exposed uniformly across language boundaries:

  • C API: ncnn_net_load_param_memory() in src/c_api.cpp (line 1539) acts as a thin wrapper that unpacks the opaque ncnn_net_t handle and forwards to the C++ method.
  • Python: The binding in python/src/main.cpp casts the method pointer explicitly:
    py::def("load_param_mem", (int (Net::*)(const char*)) &Net::load_param_mem).

Memory Alignment Requirements for ncnn Models

Understanding alignment constraints requires distinguishing between the text-based parameter metadata and the binary weight payloads.

Text Parameter Buffers Require No Alignment

The .param format contains only ASCII characters representing layer configurations. Since load_param_mem() processes this data through sscanf and character-level pointer arithmetic, the buffer can begin at any address. DataReaderFromMemory treats the input as a byte stream, making it safe to pass pointers from std::string::c_str(), static ROM arrays, or mmap'd regions without adjustment.

Binary Weight Buffers Require 4-Byte Alignment

Binary weight data loaded via load_model(const unsigned char* mem) demands stricter handling. According to docs/developer-guide/param-and-model-file-structure.md (lines 59-70), the .bin format stores weight blobs sequentially with 32-bit (4-byte) padding between sections to maintain alignment.

When you supply a memory buffer for weights, the framework interprets raw bytes as float, unsigned short, or uint8 arrays via pointer casting. On ARM architectures with strict alignment requirements, an unaligned float* access triggers a bus error or undefined behavior. To ensure safety:

  • Allocate aligned storage using fastMalloc_with_alignment(size, 4) from src/allocator.cpp (lines 1322-1334).
  • Use POSIX aligned allocation: posix_memalign(&ptr, 4, size) guarantees the buffer starts at a 4-byte boundary.
  • Preserve file mapping alignment: When using mmap or CreateFileMapping, most operating systems return page-aligned addresses that satisfy the 4-byte requirement.

Practical Implementation Examples

Loading Parameters from a std::string

Use this pattern when you have embedded the .param content as a C++ string literal or read it into a std::string buffer:

#include "net.h"
#include <fstream>
#include <sstream>

int main()
{
    ncnn::Net net;

    // Read the param file into memory
    std::ifstream ifs("model.param");
    std::stringstream buffer;
    buffer << ifs.rdbuf();
    std::string param_text = buffer.str();

    // Load directly from memory
    int ret = net.load_param_mem(param_text.c_str());
    if (ret != 0) {
        fprintf(stderr, "load_param_mem failed\n");
        return -1;
    }

    // Load weights from disk (or use load_model(mem) with aligned buffer)
    net.load_model("model.bin");
    return 0;
}

Key source: Net::load_param_mem in src/net.cpp (lines 58-63).

Loading via the C API

For C projects or FFI interoperability, use the ncnn_net_load_param_memory wrapper:

#include "c_api.h"

int main()
{
    ncnn_net_t net = ncnn_net_create();
    
    const char *param_buf = "7767517\n..."
                            "Input            data             0 1 data 0=224 1=224 2=3\n"
                            "...";
    
    int rc = ncnn_net_load_param_memory(net, param_buf);
    if (rc != 0) {
        fprintf(stderr, "C API load_param_memory failed: %d\n", rc);
        ncnn_net_destroy(net);
        return -1;
    }
    
    // Proceed to load binary weights...
    ncnn_net_load_model(net, "model.bin");
    
    ncnn_net_destroy(net);
    return 0;
}

Key source: ncnn_net_load_param_memory in src/c_api.cpp (line 1539).

Loading Aligned Binary Weights from Memory

When bypassing file I/O for the .bin weights, explicitly enforce 4-byte alignment:

#include <cstdlib>
#include <fstream>

int load_aligned_weights(ncnn::Net& net, const char* filename)
{
    // Determine file size
    std::ifstream file(filename, std::ios::binary | std::ios::ate);
    size_t size = file.tellg();
    file.seekg(0, std::ios::beg);
    
    // Allocate 4-byte aligned buffer
    void* aligned_mem = nullptr;
    if (posix_memalign(&aligned_mem, 4, size) != 0) {
        return -1;
    }
    
    // Read data into aligned buffer
    file.read(reinterpret_cast<char*>(aligned_mem), size);
    file.close();
    
    // Load model from aligned memory
    int ret = net.load_model(reinterpret_cast<const unsigned char*>(aligned_mem));
    
    // Cleanup (note: ncnn does not take ownership; free when net is destroyed)
    free(aligned_mem);
    return ret;
}

Key consideration: The pointer passed to load_model() must reference the start of a 4-byte aligned block to satisfy the expectations documented in docs/developer-guide/param-and-model-file-structure.md.

Summary

  • load_param_mem(const char* mem) enables zero-copy loading of text-based .param data by wrapping the pointer in a DataReaderFromMemory and streaming it through ParamDict::load_param().
  • No alignment constraints apply to the parameter text buffer because parsing occurs via character-oriented sscanf operations.
  • 4-byte alignment is mandatory for binary weight buffers consumed by load_model(const unsigned char* mem) to prevent undefined behavior when the framework casts raw bytes to float or unsigned short pointers.
  • Allocation helpers such as fastMalloc_with_alignment() (from src/allocator.cpp) or POSIX posix_memalign() ensure compliance with the binary format's 32-bit padding requirements.
  • Cross-language support is available through the C API (ncnn_net_load_param_memory) and Python bindings, both delegating to the core C++ implementation in src/net.cpp.

Frequently Asked Questions

Does load_param_mem() copy the buffer or just reference it?

The function only references the buffer. The DataReaderFromMemory instance created on the stack in src/net.cpp stores the pointer and reads directly from it during the parsing phase. You must ensure the memory remains valid and unmodified for the duration of the load_param() call.

What happens if I pass a misaligned binary buffer to load_model()?

On architectures with strict alignment enforcement (such as 32-bit ARM), dereferencing an unaligned float* causes a bus error or SIGBUS crash. On x86_64, the CPU may handle unaligned access with significant performance penalties, but the behavior remains undefined according to the C++ standard.

Can I use load_param_mem() with encrypted model files?

Yes. Because load_param_mem() accepts a const char* to already-resident memory, you can decrypt a .param file into a heap or stack buffer and pass the plaintext pointer to the function. This avoids writing sensitive parameter definitions to temporary files.

How do I check if my memory buffer is properly aligned?

Verify that the address modulo 4 equals zero: (reinterpret_cast<uintptr_t>(ptr) % 4) == 0. When loading weights from external sources (such as network streams or compressed archives), copy the data into a buffer allocated with posix_memalign(&ptr, 4, size) or aligned_alloc(4, size) to guarantee compliance with ncnn's binary format requirements.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →