xref: /dpdk/doc/guides/prog_guide/writing_efficient_code.rst (revision 703a62a602ff75d24ac73a0cc429d195d2cbd13a)
15630257fSFerruh Yigit..  SPDX-License-Identifier: BSD-3-Clause
25630257fSFerruh Yigit    Copyright(c) 2010-2014 Intel Corporation.
3fc1f2750SBernard Iremonger
4fc1f2750SBernard IremongerWriting Efficient Code
5fc1f2750SBernard Iremonger======================
6fc1f2750SBernard Iremonger
748624fd9SSiobhan ButlerThis chapter provides some tips for developing efficient code using the DPDK.
8fc1f2750SBernard IremongerFor additional and more general information,
9fc1f2750SBernard Iremongerplease refer to the *Intel® 64 and IA-32 Architectures Optimization Reference Manual*
10fc1f2750SBernard Iremongerwhich is a valuable reference to writing efficient code.
11fc1f2750SBernard Iremonger
12fc1f2750SBernard IremongerMemory
13fc1f2750SBernard Iremonger------
14fc1f2750SBernard Iremonger
1548624fd9SSiobhan ButlerThis section describes some key memory considerations when developing applications in the DPDK environment.
16fc1f2750SBernard Iremonger
17fc1f2750SBernard IremongerMemory Copy: Do not Use libc in the Data Plane
18fc1f2750SBernard Iremonger~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
19fc1f2750SBernard Iremonger
2048624fd9SSiobhan ButlerMany libc functions are available in the DPDK, via the Linux* application environment.
21fc1f2750SBernard IremongerThis can ease the porting of applications and the development of the configuration plane.
22fc1f2750SBernard IremongerHowever, many of these functions are not designed for performance.
23fc1f2750SBernard IremongerFunctions such as memcpy() or strcpy() should not be used in the data plane.
24fc1f2750SBernard IremongerTo copy small structures, the preference is for a simpler technique that can be optimized by the compiler.
25fc1f2750SBernard IremongerRefer to the *VTune™ Performance Analyzer Essentials* publication from Intel Press for recommendations.
26fc1f2750SBernard Iremonger
27fc1f2750SBernard IremongerFor specific functions that are called often,
28fc1f2750SBernard Iremongerit is also a good idea to provide a self-made optimized function, which should be declared as static inline.
29fc1f2750SBernard Iremonger
3048624fd9SSiobhan ButlerThe DPDK API provides an optimized rte_memcpy() function.
31fc1f2750SBernard Iremonger
32fc1f2750SBernard IremongerMemory Allocation
33fc1f2750SBernard Iremonger~~~~~~~~~~~~~~~~~
34fc1f2750SBernard Iremonger
35fc1f2750SBernard IremongerOther functions of libc, such as malloc(), provide a flexible way to allocate and free memory.
36fc1f2750SBernard IremongerIn some cases, using dynamic allocation is necessary,
37fc1f2750SBernard Iremongerbut it is really not advised to use malloc-like functions in the data plane because
38fc1f2750SBernard Iremongermanaging a fragmented heap can be costly and the allocator may not be optimized for parallel allocation.
39fc1f2750SBernard Iremonger
40fc1f2750SBernard IremongerIf you really need dynamic allocation in the data plane, it is better to use a memory pool of fixed-size objects.
41fc1f2750SBernard IremongerThis API is provided by librte_mempool.
42fc1f2750SBernard IremongerThis data structure provides several services that increase performance, such as memory alignment of objects,
43fc1f2750SBernard Iremongerlockless access to objects, NUMA awareness, bulk get/put and per-lcore cache.
44fc1f2750SBernard IremongerThe rte_malloc () function uses a similar concept to mempools.
45fc1f2750SBernard Iremonger
46fc1f2750SBernard IremongerConcurrent Access to the Same Memory Area
47fc1f2750SBernard Iremonger~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
48fc1f2750SBernard Iremonger
49fc1f2750SBernard IremongerRead-Write (RW) access operations by several lcores to the same memory area can generate a lot of data cache misses,
50fc1f2750SBernard Iremongerwhich are very costly.
51fc1f2750SBernard IremongerIt is often possible to use per-lcore variables, for example, in the case of statistics.
52fc1f2750SBernard IremongerThere are at least two solutions for this:
53fc1f2750SBernard Iremonger
54fc1f2750SBernard Iremonger*   Use RTE_PER_LCORE variables. Note that in this case, data on lcore X is not available to lcore Y.
55fc1f2750SBernard Iremonger
56fc1f2750SBernard Iremonger*   Use a table of structures (one per lcore). In this case, each structure must be cache-aligned.
57fc1f2750SBernard Iremonger
58fc1f2750SBernard IremongerRead-mostly variables can be shared among lcores without performance losses if there are no RW variables in the same cache line.
59fc1f2750SBernard Iremonger
60fc1f2750SBernard IremongerNUMA
61fc1f2750SBernard Iremonger~~~~
62fc1f2750SBernard Iremonger
63fc1f2750SBernard IremongerOn a NUMA system, it is preferable to access local memory since remote memory access is slower.
6448624fd9SSiobhan ButlerIn the DPDK, the memzone, ring, rte_malloc and mempool APIs provide a way to create a pool on a specific socket.
65fc1f2750SBernard Iremonger
66fc1f2750SBernard IremongerSometimes, it can be a good idea to duplicate data to optimize speed.
67fc1f2750SBernard IremongerFor read-mostly variables that are often accessed,
68fc1f2750SBernard Iremongerit should not be a problem to keep them in one socket only, since data will be present in cache.
69fc1f2750SBernard Iremonger
70fc1f2750SBernard IremongerDistribution Across Memory Channels
71fc1f2750SBernard Iremonger~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
72fc1f2750SBernard Iremonger
73fc1f2750SBernard IremongerModern memory controllers have several memory channels that can load or store data in parallel.
74fc1f2750SBernard IremongerDepending on the memory controller and its configuration,
75fc1f2750SBernard Iremongerthe number of channels and the way the memory is distributed across the channels varies.
76fc1f2750SBernard IremongerEach channel has a bandwidth limit,
77fc1f2750SBernard Iremongermeaning that if all memory access operations are done on the first channel only, there is a potential bottleneck.
78fc1f2750SBernard Iremonger
79fc1f2750SBernard IremongerBy default, the  :ref:`Mempool Library <Mempool_Library>` spreads the addresses of objects among memory channels.
80fc1f2750SBernard Iremonger
817a888932SEelco ChaudronLocking memory pages
827a888932SEelco Chaudron~~~~~~~~~~~~~~~~~~~~
837a888932SEelco Chaudron
847a888932SEelco ChaudronThe underlying operating system is allowed to load/unload memory pages at its own discretion.
857a888932SEelco ChaudronThese page loads could impact the performance, as the process is on hold when the kernel fetches them.
867a888932SEelco Chaudron
877a888932SEelco ChaudronTo avoid these you could pre-load, and lock them into memory with the ``mlockall()`` call.
887a888932SEelco Chaudron
897a888932SEelco Chaudron.. code-block:: c
907a888932SEelco Chaudron
917a888932SEelco Chaudron    if (mlockall(MCL_CURRENT | MCL_FUTURE)) {
927a888932SEelco Chaudron        RTE_LOG(NOTICE, USER1, "mlockall() failed with error \"%s\"\n",
937a888932SEelco Chaudron                strerror(errno));
947a888932SEelco Chaudron    }
957a888932SEelco Chaudron
96fc1f2750SBernard IremongerCommunication Between lcores
97fc1f2750SBernard Iremonger----------------------------
98fc1f2750SBernard Iremonger
99fc1f2750SBernard IremongerTo provide a message-based communication between lcores,
10048624fd9SSiobhan Butlerit is advised to use the DPDK ring API, which provides a lockless ring implementation.
101fc1f2750SBernard Iremonger
102fc1f2750SBernard IremongerThe ring supports bulk and burst access,
103fc1f2750SBernard Iremongermeaning that it is possible to read several elements from the ring with only one costly atomic operation
10429e30cbcSThomas Monjalon(see :doc:`ring_lib`).
105fc1f2750SBernard IremongerPerformance is greatly improved when using bulk access operations.
106fc1f2750SBernard Iremonger
107fc1f2750SBernard IremongerThe code algorithm that dequeues messages may be something similar to the following:
108fc1f2750SBernard Iremonger
109fc1f2750SBernard Iremonger.. code-block:: c
110fc1f2750SBernard Iremonger
111fc1f2750SBernard Iremonger    #define MAX_BULK 32
112fc1f2750SBernard Iremonger
113fc1f2750SBernard Iremonger    while (1) {
114fc1f2750SBernard Iremonger        /* Process as many elements as can be dequeued. */
115ecaed092SBruce Richardson        count = rte_ring_dequeue_burst(ring, obj_table, MAX_BULK, NULL);
116fc1f2750SBernard Iremonger        if (unlikely(count == 0))
117fc1f2750SBernard Iremonger            continue;
118fc1f2750SBernard Iremonger
119fc1f2750SBernard Iremonger        my_process_bulk(obj_table, count);
120fc1f2750SBernard Iremonger   }
121fc1f2750SBernard Iremonger
122fc1f2750SBernard IremongerPMD Driver
123fc1f2750SBernard Iremonger----------
124fc1f2750SBernard Iremonger
12548624fd9SSiobhan ButlerThe DPDK Poll Mode Driver (PMD) is also able to work in bulk/burst mode,
126fc1f2750SBernard Iremongerallowing the factorization of some code for each call in the send or receive function.
127fc1f2750SBernard Iremonger
128fc1f2750SBernard IremongerAvoid partial writes.
129fc1f2750SBernard IremongerWhen PCI devices write to system memory through DMA,
130fc1f2750SBernard Iremongerit costs less if the write operation is on a full cache line as opposed to part of it.
131fc1f2750SBernard IremongerIn the PMD code, actions have been taken to avoid partial writes as much as possible.
132fc1f2750SBernard Iremonger
133fc1f2750SBernard IremongerLower Packet Latency
134fc1f2750SBernard Iremonger~~~~~~~~~~~~~~~~~~~~
135fc1f2750SBernard Iremonger
136fc1f2750SBernard IremongerTraditionally, there is a trade-off between throughput and latency.
137fc1f2750SBernard IremongerAn application can be tuned to achieve a high throughput,
138fc1f2750SBernard Iremongerbut the end-to-end latency of an average packet will typically increase as a result.
139fc1f2750SBernard IremongerSimilarly, the application can be tuned to have, on average,
140fc1f2750SBernard Iremongera low end-to-end latency, at the cost of lower throughput.
141fc1f2750SBernard Iremonger
142fc1f2750SBernard IremongerIn order to achieve higher throughput,
14348624fd9SSiobhan Butlerthe DPDK attempts to aggregate the cost of processing each packet individually by processing packets in bursts.
144fc1f2750SBernard Iremonger
145fc1f2750SBernard IremongerUsing the testpmd application as an example,
146fc1f2750SBernard Iremongerthe burst size can be set on the command line to a value of 16 (also the default value).
147fc1f2750SBernard IremongerThis allows the application to request 16 packets at a time from the PMD.
148fc1f2750SBernard IremongerThe testpmd application then immediately attempts to transmit all the packets that were received,
149fc1f2750SBernard Iremongerin this case, all 16 packets.
150fc1f2750SBernard Iremonger
151fc1f2750SBernard IremongerThe packets are not transmitted until the tail pointer is updated on the corresponding TX queue of the network port.
152fc1f2750SBernard IremongerThis behavior is desirable when tuning for high throughput because
153fc1f2750SBernard Iremongerthe cost of tail pointer updates to both the RX and TX queues can be spread across 16 packets,
154fc1f2750SBernard Iremongereffectively hiding the relatively slow MMIO cost of writing to the PCIe* device.
155fc1f2750SBernard IremongerHowever, this is not very desirable when tuning for low latency because
156fc1f2750SBernard Iremongerthe first packet that was received must also wait for another 15 packets to be received.
157fc1f2750SBernard IremongerIt cannot be transmitted until the other 15 packets have also been processed because
158fc1f2750SBernard Iremongerthe NIC will not know to transmit the packets until the TX tail pointer has been updated,
159fc1f2750SBernard Iremongerwhich is not done until all 16 packets have been processed for transmission.
160fc1f2750SBernard Iremonger
161fc1f2750SBernard IremongerTo consistently achieve low latency, even under heavy system load,
162fc1f2750SBernard Iremongerthe application developer should avoid processing packets in bunches.
163fc1f2750SBernard IremongerThe testpmd application can be configured from the command line to use a burst value of 1.
164fc1f2750SBernard IremongerThis will allow a single packet to be processed at a time, providing lower latency,
165fc1f2750SBernard Iremongerbut with the added cost of lower throughput.
166fc1f2750SBernard Iremonger
167fc1f2750SBernard IremongerLocks and Atomic Operations
168fc1f2750SBernard Iremonger---------------------------
169fc1f2750SBernard Iremonger
170*703a62a6SPhil YangThis section describes some key considerations when using locks and atomic
171*703a62a6SPhil Yangoperations in the DPDK environment.
172*703a62a6SPhil Yang
173*703a62a6SPhil YangLocks
174*703a62a6SPhil Yang~~~~~
175*703a62a6SPhil Yang
176*703a62a6SPhil YangOn x86, atomic operations imply a lock prefix before the instruction,
177fc1f2750SBernard Iremongercausing the processor's LOCK# signal to be asserted during execution of the following instruction.
178fc1f2750SBernard IremongerThis has a big impact on performance in a multicore environment.
179fc1f2750SBernard Iremonger
180fc1f2750SBernard IremongerPerformance can be improved by avoiding lock mechanisms in the data plane.
181fc1f2750SBernard IremongerIt can often be replaced by other solutions like per-lcore variables.
182fc1f2750SBernard IremongerAlso, some locking techniques are more efficient than others.
183fc1f2750SBernard IremongerFor instance, the Read-Copy-Update (RCU) algorithm can frequently replace simple rwlocks.
184fc1f2750SBernard Iremonger
185*703a62a6SPhil YangAtomic Operations: Use C11 Atomic Builtins
186*703a62a6SPhil Yang~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
187*703a62a6SPhil Yang
188*703a62a6SPhil YangDPDK generic rte_atomic operations are implemented by __sync builtins. These
189*703a62a6SPhil Yang__sync builtins result in full barriers on aarch64, which are unnecessary
190*703a62a6SPhil Yangin many use cases. They can be replaced by __atomic builtins that conform to
191*703a62a6SPhil Yangthe C11 memory model and provide finer memory order control.
192*703a62a6SPhil Yang
193*703a62a6SPhil YangSo replacing the rte_atomic operations with __atomic builtins might improve
194*703a62a6SPhil Yangperformance for aarch64 machines.
195*703a62a6SPhil Yang
196*703a62a6SPhil YangSome typical optimization cases are listed below:
197*703a62a6SPhil Yang
198*703a62a6SPhil YangAtomicity
199*703a62a6SPhil Yang^^^^^^^^^
200*703a62a6SPhil Yang
201*703a62a6SPhil YangSome use cases require atomicity alone, the ordering of the memory operations
202*703a62a6SPhil Yangdoes not matter. For example, the packet statistics counters need to be
203*703a62a6SPhil Yangincremented atomically but do not need any particular memory ordering.
204*703a62a6SPhil YangSo, RELAXED memory ordering is sufficient.
205*703a62a6SPhil Yang
206*703a62a6SPhil YangOne-way Barrier
207*703a62a6SPhil Yang^^^^^^^^^^^^^^^
208*703a62a6SPhil Yang
209*703a62a6SPhil YangSome use cases allow for memory reordering in one way while requiring memory
210*703a62a6SPhil Yangordering in the other direction.
211*703a62a6SPhil Yang
212*703a62a6SPhil YangFor example, the memory operations before the spinlock lock are allowed to
213*703a62a6SPhil Yangmove to the critical section, but the memory operations in the critical section
214*703a62a6SPhil Yangare not allowed to move above the lock. In this case, the full memory barrier
215*703a62a6SPhil Yangin the compare-and-swap operation can be replaced with ACQUIRE memory order.
216*703a62a6SPhil YangOn the other hand, the memory operations after the spinlock unlock are allowed
217*703a62a6SPhil Yangto move to the critical section, but the memory operations in the critical
218*703a62a6SPhil Yangsection are not allowed to move below the unlock. So the full barrier in the
219*703a62a6SPhil Yangstore operation can use RELEASE memory order.
220*703a62a6SPhil Yang
221*703a62a6SPhil YangReader-Writer Concurrency
222*703a62a6SPhil Yang^^^^^^^^^^^^^^^^^^^^^^^^^
223*703a62a6SPhil Yang
224*703a62a6SPhil YangLock-free reader-writer concurrency is one of the common use cases in DPDK.
225*703a62a6SPhil Yang
226*703a62a6SPhil YangThe payload or the data that the writer wants to communicate to the reader,
227*703a62a6SPhil Yangcan be written with RELAXED memory order. However, the guard variable should
228*703a62a6SPhil Yangbe written with RELEASE memory order. This ensures that the store to guard
229*703a62a6SPhil Yangvariable is observable only after the store to payload is observable.
230*703a62a6SPhil Yang
231*703a62a6SPhil YangCorrespondingly, on the reader side, the guard variable should be read
232*703a62a6SPhil Yangwith ACQUIRE memory order. The payload or the data the writer communicated,
233*703a62a6SPhil Yangcan be read with RELAXED memory order. This ensures that, if the store to
234*703a62a6SPhil Yangguard variable is observable, the store to payload is also observable.
235*703a62a6SPhil Yang
236fc1f2750SBernard IremongerCoding Considerations
237fc1f2750SBernard Iremonger---------------------
238fc1f2750SBernard Iremonger
239fc1f2750SBernard IremongerInline Functions
240fc1f2750SBernard Iremonger~~~~~~~~~~~~~~~~
241fc1f2750SBernard Iremonger
242fc1f2750SBernard IremongerSmall functions can be declared as static inline in the header file.
243fc1f2750SBernard IremongerThis avoids the cost of a call instruction (and the associated context saving).
244fc1f2750SBernard IremongerHowever, this technique is not always efficient; it depends on many factors including the compiler.
245fc1f2750SBernard Iremonger
246fc1f2750SBernard IremongerBranch Prediction
247fc1f2750SBernard Iremonger~~~~~~~~~~~~~~~~~
248fc1f2750SBernard Iremonger
249fc1f2750SBernard IremongerThe Intel® C/C++ Compiler (icc)/gcc built-in helper functions likely() and unlikely()
250fc1f2750SBernard Iremongerallow the developer to indicate if a code branch is likely to be taken or not.
251fc1f2750SBernard IremongerFor instance:
252fc1f2750SBernard Iremonger
253fc1f2750SBernard Iremonger.. code-block:: c
254fc1f2750SBernard Iremonger
255fc1f2750SBernard Iremonger    if (likely(x > 1))
256fc1f2750SBernard Iremonger        do_stuff();
257fc1f2750SBernard Iremonger
258fc1f2750SBernard IremongerSetting the Target CPU Type
259fc1f2750SBernard Iremonger---------------------------
260fc1f2750SBernard Iremonger
26148624fd9SSiobhan ButlerThe DPDK supports CPU microarchitecture-specific optimizations by means of CONFIG_RTE_MACHINE option
26248624fd9SSiobhan Butlerin the DPDK configuration file.
263fea1d908SJohn McNamaraThe degree of optimization depends on the compiler's ability to optimize for a specific microarchitecture,
264fc1f2750SBernard Iremongertherefore it is preferable to use the latest compiler versions whenever possible.
265fc1f2750SBernard Iremonger
266fc1f2750SBernard IremongerIf the compiler version does not support the specific feature set (for example, the Intel® AVX instruction set),
267fc1f2750SBernard Iremongerthe build process gracefully degrades to whatever latest feature set is supported by the compiler.
268fc1f2750SBernard Iremonger
269fc1f2750SBernard IremongerSince the build and runtime targets may not be the same,
270fc1f2750SBernard Iremongerthe resulting binary also contains a platform check that runs before the
271fc1f2750SBernard Iremongermain() function and checks if the current machine is suitable for running the binary.
272fc1f2750SBernard Iremonger
273fc1f2750SBernard IremongerAlong with compiler optimizations,
274fc1f2750SBernard Iremongera set of preprocessor defines are automatically added to the build process (regardless of the compiler version).
275fc1f2750SBernard IremongerThese defines correspond to the instruction sets that the target CPU should be able to support.
276fc1f2750SBernard IremongerFor example, a binary compiled for any SSE4.2-capable processor will have RTE_MACHINE_CPUFLAG_SSE4_2 defined,
277fc1f2750SBernard Iremongerthus enabling compile-time code path selection for different platforms.
278