# InferenceSet IO Example

The following C++ example demonstrates how to load a pre-compiled QPC file, setup a runtime environment, and perform inference on an AIC100 device.

This example contains a single C++ file `InferenceSetIOBuffersExample.cpp` and a `CMakeLists.txt` that  can be used for compiling as part of `Qualcomm Cloud AI 100`
distributed Platform SDK.

<details class="sd-sphinx-override sd-dropdown sd-card sd-mb-3 sd-border-1">
<summary class="sd-summary-title sd-card-header sd-bg-light sd-bg-text-light">
<span class="sd-summary-icon"><span class="svg-1 sd-octicon sd-octicon-beaker"><svg version="1.1" width="1.0em" height="1.0em" class="sd-octicon sd-octicon-beaker" viewbox="0 0 16 16" aria-hidden="true"><path fill-rule="evenodd" d="M5 5.782V2.5h-.25a.75.75 0 010-1.5h6.5a.75.75 0 010 1.5H11v3.282l3.666 5.76C15.619 13.04 14.543 15 12.767 15H3.233c-1.776 0-2.852-1.96-1.899-3.458L5 5.782zM9.5 2.5h-3V6a.75.75 0 01-.117.403L4.73 9h6.54L9.617 6.403A.75.75 0 019.5 6V2.5zm-6.9 9.847L3.775 10.5h8.45l1.175 1.847a.75.75 0 01-.633 1.153H3.233a.75.75 0 01-.633-1.153z"></path></svg></span></span>InferenceSetIOBuffersExample.cpp<div class="sd-summary-down docutils">
<span class="svg-2 sd-octicon sd-octicon-chevron-down"><svg version="1.1" width="1.5em" height="1.5em" class="sd-octicon sd-octicon-chevron-down" viewbox="0 0 24 24" aria-hidden="true"><path fill-rule="evenodd" d="M5.22 8.72a.75.75 0 000 1.06l6.25 6.25a.75.75 0 001.06 0l6.25-6.25a.75.75 0 00-1.06-1.06L12 14.44 6.28 8.72a.75.75 0 00-1.06 0z"></path></svg></span></div>
<div class="sd-summary-up docutils">
<span class="svg-3 sd-octicon sd-octicon-chevron-up"><svg version="1.1" width="1.5em" height="1.5em" class="sd-octicon sd-octicon-chevron-up" viewbox="0 0 24 24" aria-hidden="true"><path fill-rule="evenodd" d="M18.78 15.28a.75.75 0 000-1.06l-6.25-6.25a.75.75 0 00-1.06 0l-6.25 6.25a.75.75 0 101.06 1.06L12 9.56l5.72 5.72a.75.75 0 001.06 0z"></path></svg></span></div>
</summary><div class="sd-summary-content sd-card-body docutils">
<p class="sd-card-text">InferenceSetIOBuffersExample.cpp</p>
<div class="highlight-default notranslate"><div class="highlight"><pre class="pre codeblock"><code>1     //-----------------------------------------------------------------------------
2     // Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
3     // SPDX-License-Identifier: BSD-3-Clause-Clear
4     //-----------------------------------------------------------------------------
5
6     #include &lt;string&gt;
7     #include &lt;vector&gt;
8     #include &lt;iostream&gt;
9     #include &lt;random&gt;
10    #include &quot;QAicApi.hpp&quot;
11
12    namespace {
13
14    /**
15    * used to generate random data into input buffers
16    * Input buffers are uint8_t arrays
17    */
18    struct RandomGen final {
19        static constexpr const int from = std::numeric_limits&lt;uint8_t&gt;::min();
20        static constexpr int to = std::numeric_limits&lt;uint8_t&gt;::max();
21        std::random_device randdev;
22        std::mt19937 gen;
23        std::uniform_int_distribution&lt;uint8_t&gt; distr;
24        explicit RandomGen() : gen(randdev()), distr(from, to) {}
25        [[nodiscard]] auto next() { return distr(gen); }
26    };
27
28    /**
29    * Simple helper to return true if the buffer mapping instance is an input one
30    * @param bufmap buffer mapping instance
31    * @return true if the instance is an input buffer one.
32    */
33    [[nodiscard]] bool isInputBuffer(const qaic::rt::BufferMapping &amp;bufmap) {
34        return bufmap.ioType == BUFFER_IO_TYPE_INPUT;
35    }
36
37    /**
38    * Helper function to print input/output buffer counts so far, zero based
39    * @param bufmap buffer map instance
40    * @param inputCount input count to use if the instance is input
41    * @param outputCount output count to use if the instance is output
42    * @return string formatted using the above info.
43    */
44    [[nodiscard]] std::string getPrintName(const qaic::rt::BufferMapping &amp;bufmap,
45                                        const std::size_t inputCount,
46                                        const std::size_t outputCount) {
47        using namespace std::string_literals;
48        return isInputBuffer(bufmap) ? (&quot;Input &quot;s + std::to_string(inputCount))
49                                    : (&quot;Output &quot;s + std::to_string(outputCount));
50    }
51
52    /**
53    * Populate input, output vectors with QBuffer information
54    * @param bufmap Buffer mapping instance
55    * @param buf Actual QBufffer that was generated at callsite/caller.
56    * @param inputBuffers Vector to use in case this is input instance
57    * @param outputBuffers Vector to use in case this is an output instance
58    */
59    void populateVector(const qaic::rt::BufferMapping &amp;bufmap, const QBuffer &amp;buf,
60                        std::vector&lt;QBuffer&gt; &amp;inputBuffers,
61                        std::vector&lt;QBuffer&gt; &amp;outputBuffers) {
62        if (isInputBuffer(bufmap)) {
63            inputBuffers.push_back(buf);
64        } else {
65            outputBuffers.push_back(buf);
66        }
67    }
68
69    /**
70    * Given a buffer and size, populate it with random [0..128] random data
71    * @param buf buffer to populate
72    * @param sz size of this buffer
73    */
74    void populateBufWithRandom(uint8_t *buf, const std::size_t sz) {
75    RandomGen gen;
76        for (auto iter = buf; iter &lt; buf + sz; ++iter) {
77            *iter = gen.next();
78        }
79    }
80
81    /**
82    * Prepare buffers, vectors given a single buffer mapping. Depending on the
83    * input/output instance of the buffer mapping, handle logic accordingly.
84    * Only inputbuffers needs to be populated with random data.
85    * @param bufmap buffer mapping passed as const
86    * @param inputCount input buffers counter
87    * @param outputCount output buffers counter
88    * @param inputBuffers input vector of QBuffer to append to new QBuffer
89    * @param outputBuffers output vector of QBuffer to append to new QBuffer
90    */
91    void prepareBuffers(const qaic::rt::BufferMapping &amp;bufmap,
92                        std::size_t &amp;inputCount, std::size_t &amp;outputCount,
93                        std::vector&lt;QBuffer&gt; &amp;inputBuffers,
94                        std::vector&lt;QBuffer&gt; &amp;outputBuffers) {
95        std::cout &lt;&lt; getPrintName(bufmap, inputCount, outputCount) &lt;&lt; &#39;\n&#39;;
96        std::cout &lt;&lt; &quot;\tname = &quot; &lt;&lt; bufmap.bufferName &lt;&lt; &#39;\n&#39;;
97        std::cout &lt;&lt; &quot;\tsize = &quot; &lt;&lt; bufmap.size &lt;&lt; &#39;\n&#39;;
98        QBuffer buf{bufmap.size, new uint8_t[bufmap.size]}; // Need to dealloc
99        populateVector(bufmap, buf, inputBuffers, outputBuffers);
100       //
101       // Provide the input to the inference in &quot;inputBuffers&quot;. Here random data
102       // is used. For providing input as file, use a different api. This example
103       // is for input in memory.
104       //
105       if (isInputBuffer(bufmap)) {
106           populateBufWithRandom(buf.buf, buf.size);
107           ++inputCount;
108       } else {
109           ++outputCount;
110       }
111   }
112
113   /**
114   * Given input and output buffers, release all heap allocated
115   * @param inputBuffers vector of QBuffers - inputs
116   * @param outputBuffers vector of Qbuffers - outputs
117   */
118   void releaseBuffers(std::vector&lt;QBuffer&gt; &amp;inputBuffers,
119                       std::vector&lt;QBuffer&gt; &amp;outputBuffers) {
120       const auto release([](const QBuffer &amp;qbuf) { delete[] qbuf.buf; });
121       std::for_each(inputBuffers.begin(), inputBuffers.end(), release);
122       std::for_each(outputBuffers.begin(), outputBuffers.end(), release);
123   }
124
125   /**
126   * Given buffer mapping instance, return true if this instance does not
127   * contain input or output buffers (e.g. it contains uninitialized or invalid)
128   * @param bufmap buffer mapping instance
129   * @return true if the buffer mapping instance does not container a valid buffer
130   */
131   [[nodiscard]] bool notInputOrOutput(const qaic::rt::BufferMapping &amp;bufmap) {
132       const std::initializer_list&lt;QAicBufferIoTypeEnum&gt; bufTypes{
133           BUFFER_IO_TYPE_INPUT, BUFFER_IO_TYPE_OUTPUT};
134       const auto func([type = bufmap.ioType](const auto v) { return v == type; });
135       return std::none_of(bufTypes.begin(), bufTypes.end(), func);
136   }
137
138   } // namespace
139
140   int main([[maybe_unused]] int argc, [[maybe_unused]] char *argv[]) {
141       QID qid = 0;
142       std::vector&lt;QID&gt; qidList{qid};
143
144       // *** QPC ***
145       constexpr const char *qpcPath =
146           &quot;/opt/qti-aic/test-data/aic100/v2/2nsp/2nsp-conv-hmx&quot;; //
147       auto qpc = qaic::rt::Qpc::Factory(qpcPath);
148
149       // *** CONTEXT ***
150       constexpr QAicContextProperties_t *NullProp = nullptr;
151       auto context = qaic::rt::Context::Factory(NullProp, qidList);
152
153       // *** INFERENCE SET ***
154       constexpr uint32_t setSize = 10;
155       constexpr uint32_t numActivations = 1;
156       auto inferenceSet = qaic::rt::InferenceSet::Factory(
157           context, qpc, qidList.at(0), setSize, numActivations);
158
159       // *** SETUP IO BUFFERS ***
160       qaic::rt::shInferenceHandle submitHandle;
161       auto status = inferenceSet-&gt;getAvailable(submitHandle);
162       if (status != QS_SUCCESS) {
163           std::cerr &lt;&lt; &quot;Error obtaining Inference Handle\n&quot;;
164           return -1;
165       }
166       std::size_t numInputBuffers = 0;
167       std::size_t numOutputBuffers = 0;
168       std::vector&lt;QBuffer&gt; inputBuffers, outputBuffers;
169       const auto &amp;bufferMappings = qpc-&gt;getBufferMappings();
170       for (const auto &amp;bufmap : bufferMappings) {
171           if (notInputOrOutput(bufmap)) {
172               continue;
173           }
174           prepareBuffers(bufmap, numInputBuffers, numOutputBuffers, inputBuffers,
175                       outputBuffers);
176       }
177       submitHandle-&gt;setInputBuffers(inputBuffers);
178       submitHandle-&gt;setOutputBuffers(outputBuffers);
179
180       // *** SUBMIT ***
181       constexpr uint32_t inferenceId = 0; // also named as request ID
182       status = inferenceSet-&gt;submit(submitHandle, inferenceId);
183       std::cout &lt;&lt; status &lt;&lt; &#39;\n&#39;;
184
185       // *** COMPLETION ***
186       qaic::rt::shInferenceHandle completedHandle;
187       status = inferenceSet-&gt;getCompletedId(completedHandle, inferenceId);
188       std::cout &lt;&lt; status &lt;&lt; &#39;\n&#39;;
189       status = inferenceSet-&gt;putCompleted(std::move(completedHandle));
190       std::cout &lt;&lt; status &lt;&lt; &#39;\n&#39;;
191
192       // *** GET OUTPUT ***
193       //
194       // At this point, the output is available in &quot;outputBuffers&quot; and can be
195       // consumed.
196       //
197
198       // *** Release user allocated buffers ***
199       releaseBuffers(inputBuffers, outputBuffers);
200   }
</code><span class="copyclip"><svg xmlns="http://www.w3.org/2000/svg" class="copyclipicon" width="25px" height="25px" viewbox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><title>Copy to clipboard</title><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg></span></pre></div>
</div>
</div>
</details><details class="sd-sphinx-override sd-dropdown sd-card sd-mb-3 sd-border-1">
<summary class="sd-summary-title sd-card-header sd-bg-light sd-bg-text-light">
<span class="sd-summary-icon"><span class="svg-4 sd-octicon sd-octicon-beaker"><svg version="1.1" width="1.0em" height="1.0em" class="sd-octicon sd-octicon-beaker" viewbox="0 0 16 16" aria-hidden="true"><path fill-rule="evenodd" d="M5 5.782V2.5h-.25a.75.75 0 010-1.5h6.5a.75.75 0 010 1.5H11v3.282l3.666 5.76C15.619 13.04 14.543 15 12.767 15H3.233c-1.776 0-2.852-1.96-1.899-3.458L5 5.782zM9.5 2.5h-3V6a.75.75 0 01-.117.403L4.73 9h6.54L9.617 6.403A.75.75 0 019.5 6V2.5zm-6.9 9.847L3.775 10.5h8.45l1.175 1.847a.75.75 0 01-.633 1.153H3.233a.75.75 0 01-.633-1.153z"></path></svg></span></span>CMakeLists.txt<div class="sd-summary-down docutils">
<span class="svg-5 sd-octicon sd-octicon-chevron-down"><svg version="1.1" width="1.5em" height="1.5em" class="sd-octicon sd-octicon-chevron-down" viewbox="0 0 24 24" aria-hidden="true"><path fill-rule="evenodd" d="M5.22 8.72a.75.75 0 000 1.06l6.25 6.25a.75.75 0 001.06 0l6.25-6.25a.75.75 0 00-1.06-1.06L12 14.44 6.28 8.72a.75.75 0 00-1.06 0z"></path></svg></span></div>
<div class="sd-summary-up docutils">
<span class="svg-6 sd-octicon sd-octicon-chevron-up"><svg version="1.1" width="1.5em" height="1.5em" class="sd-octicon sd-octicon-chevron-up" viewbox="0 0 24 24" aria-hidden="true"><path fill-rule="evenodd" d="M18.78 15.28a.75.75 0 000-1.06l-6.25-6.25a.75.75 0 00-1.06 0l-6.25 6.25a.75.75 0 101.06 1.06L12 9.56l5.72 5.72a.75.75 0 001.06 0z"></path></svg></span></div>
</summary><div class="sd-summary-content sd-card-body docutils">
<p class="sd-card-text">CMakeLists.txt</p>
<div class="highlight-default notranslate"><div class="highlight"><pre class="pre codeblock"><code>1    # ==============================================================================
2    # Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
3    # SPDX-License-Identifier: BSD-3-Clause-Clear
4    # ==============================================================================
5
6    project(inference-set-io-buffers-example)
7    cmake_minimum_required (VERSION 3.15)
8    set(CMAKE_CXX_STANDARD 17)
9
10   include_directories(&quot;/opt/qti-aic/dev/inc&quot;)
11
12   add_executable(inference-set-io-buffers-example InferenceSetIOBuffersExample.cpp)
13   set_target_properties(
14       inference-set-io-buffers-example
15       PROPERTIES
16       LINK_FLAGS &quot;-Wl,--no-as-needed&quot;
17   )
18   target_compile_options(inference-set-io-buffers-example PRIVATE
19                       -fstack-protector-all
20                       -Werror
21                       -Wall
22                       -Wextra
23                       -Wunused-variable
24                       -Wunused-parameter
25                       -Wnon-virtual-dtor
26                       -Wno-missing-field-initializers)
27   target_link_libraries(inference-set-io-buffers-example PRIVATE
28                       pthread
29                       dl)
</code><span class="copyclip"><svg xmlns="http://www.w3.org/2000/svg" class="copyclipicon" width="25px" height="25px" viewbox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><title>Copy to clipboard</title><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg></span></pre></div>
</div>
</div>
</details>
## Main flow

The main function has the following sections:

- Helper functions
- QID
- QPC
- Context
- Inference set
- Setup input and output buffers
- Submit
- Completion
- Get output
- Release user allocated buffers.

### Helper functions

The `InferenceSetIOBuffersExample.cpp` example uses the following helper functions and constructs:

- `RandomGen` : Random data generator - to generate random input
buffer data.
- `isInputBuffer` : Query if a specific *BufferMapping* instance is
an input one.
- `getPrintName` : Return Input or Output strings for standard output
printing.
- `populateVector` : Populate vector - to populate inputs or output
*vector &lt;&gt;* containers.
- `populateBufWithRandom` : Populate buffer with random data - using
the above mentioned Random data generator, given a buffer, populate
it with random values.
- `prepareBuffers` : Prepare buffers - iterates over the
*BufferMappings* container and for each *BufferMapping* instance,
populate input/output *vector &lt;&gt;* as well as invoke helper function
to populate inputs buffers with random data.
- `releaseBuffers` : Release buffers - iterates over allocated
inputs/outputs and release/delete buffers (return memory back to the
system’s Heap).
- `notInputOrOutput` : Not input our Output boolean function - Given
a *BufferMapping* instance return true if this instance is not Input,
nor Output buffer instance. (For example could be invalid, or
uninitialized). We skip these kind of instances.

### QID

The first part of `main()` initializes the device ID as `QID 0` which is
usually the first enumerated device ID.

Though the API is capable of accepting a list of *QID* ‘s, this example only passes a single one in the `vector<> int` container.

### QPC

This part of the code hardcodes the path to the [QPC](https://docs.qualcomm.com/doc/80-99100-3/topic/index_runtime.html#reference-to-qpc). You can change the path or pass it too
the program using other means like an environment variable or command line
arguments.

The `qaic::rt::Qpc::Factory` API accepts a path to the *QPC* and
returns pack a *QPC* object to use in the next steps.

As a reminder, the QPC is a container file that includes various
parts of the compiled network.

### Context

`QAIC` runtime requires a [Context](https://docs.qualcomm.com/doc/80-99100-3/topic/index_runtime.html#reference-to-context) object
to be created and passed around to various APIs. This section of the example does the following:

- Uses the `qaic::rt::Context::Factory` API to obtain a new instance of *Context*.
- Passes the  `NullProp` for no special `QAicContextProperties_t` attributes.
- Passes the  *QID* vector that was instantiated in the previous section.

### Inference set

This section of the example creates an instance of [InferenceSet](https://docs.qualcomm.com/doc/80-99100-3/topic/index_runtime.html#reference-to-inference-set). *InferenceSet* is considered as a top-level entity when it comes to
running Inferences on Hardware. This example sets the ses of the software and hardware backlog as 10 possible pending buffers.

A single activation is requested. This means that the provided
program (encapsulated in *QPC*), is activated as a single instance
on the hardware. Use the *QID* when creating the
InferenceSet instance.

### Setup input and output buffers

The next section creates the input and output buffers. Your application allocates the buffers when it runs and also needs to deallocate the buffers
at application teardown.  This part has the following subsections:

1. Obtains [InferenceHandle](https://docs.qualcomm.com/doc/80-99100-3/topic/index_runtime.html#reference-to-inference-handle) to submit
the input and output buffers during inference.
2. Allocates the buffers using  the `bufferMappings` container. It iterates
over each `bufferMappings` instance to obtain information that helps allocate new buffers.
3. Once the vectors of the allocated input and output buffers are obtained, it uses the `InferenceHandle` to submit them during inference.

In this example, the helper functions populate input buffers with
random data just to demonstrate the capabilities of the system.

### Submit

This is the part of the example where the actual submission request happens. The `inferenceSet` is used to submit the request, passing `submitHandle`
and user defined `inferenceId` (which was picked as ID 0)

### Completion

This is a blocking call to wait on inference completion and device
output’s buffers received in the application.

It uses `inferenceSet` to obtain the `completedHandle` by passing the
`inferenceId` from the previous section.

You are  responsible for returning the `completedHandle` back to the runtime
pool and by calling `putCompleted` using `inferenceSet`.

### Get output

This example does not do anything with the obtained output buffers although your
real-life application consumes such output data.

### Release user allocated buffers

Because the buffers are user-allocated buffers using the system’s heap,
the you are in charge of properly releasing these buffers as
demonstrated in the last phase of this example.

## Compile and run commands

Copy the `InferenceSetIOBuffersExample.cpp` and `CMakeLists.txt` in
a folder.

Then compile the example with following commands:

mkdir build
    cd build
    cmake ..
    make -j 8
    Copy to clipboard

Finally, run the executable `./inference-set-io-buffers-example`, accordingly change the `qpcPath`.

Last Published: May 01, 2026

[Previous Topic
CPP API](https://docs.qualcomm.com/bundle/publicresource/80-99100-3/topics/index_Cpp-API.md) [Next Topic
Profiling support in runtime](https://docs.qualcomm.com/bundle/publicresource/80-99100-3/topics/index_features.md)