| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | ||
| 6 | 7 | 8 | 9 | 10 | 11 | 12 |
| 13 | 14 | 15 | 16 | 17 | 18 | 19 |
| 20 | 21 | 22 | 23 | 24 | 25 | 26 |
| 27 | 28 | 29 | 30 |
- dma hft
- hft개발
- fep개발
- low latency trading
- 프랍데스크
- low latency
- DMA개발
- 트레이딩 엔지니어링
- korea inbound
- KRX HFT
- HFT system
- 속도 차익거래
- 자동매매
- dma development
- Arbitrage Trading
- mmlp
- system trading
- dma fep
- 주문fep
- 저지연시스템
- 주문 오더북
- high frequency trading
- hft tuning
- algo trading
- trading engineering
- 시스템 트레이딩
- ultra low latency
- DMA trading
- krx nxt
- Korea krx
- Today
- Total
Fontes Fintech
epoll vs. Busy Polling in Low-Latency Trading Systems | Trading Engineering — Deep Dive #5 본문
epoll vs. Busy Polling in Low-Latency Trading Systems | Trading Engineering — Deep Dive #5
폰테스 핀테크 :: 금융거래의 안전한 미래 2026. 9. 24. 00:09
epoll vs. Busy Polling in Low-Latency Trading Systems
Trading Engineering — Deep Dive #5
When developing network applications on Linux, epoll() is one of the most commonly used mechanisms for handling I/O events.
It is highly efficient for general-purpose servers.
However, low-latency trading systems sometimes require a different approach.
When market data or order messages must be processed with extremely low latency, the way the CPU waits for data can itself become part of the latency problem.
This article compares epoll and Busy Polling / Busy Looping, and explains why some low-latency systems deliberately use CPU resources to minimize waiting time.
1. How a Typical Network Application Works
In a conventional server application, there is no reason to continuously consume CPU resources while waiting for network data.
A typical flow looks like this:
Application
↓
epoll()
│
│ Waiting...
│
↓
Network Event
│
↓
read()
│
↓
Process Data
When there is no data, the application waits inside epoll_wait().
This allows the CPU to perform other work.
In other words:
When there is no data, the CPU does not need to keep running the application.
This is a very efficient design for web servers, API servers, and conventional TCP applications.
2. What Is epoll?
Linux epoll is an I/O event notification mechanism designed to efficiently monitor multiple file descriptors.
For example, a single process may manage many sockets:
Socket 1 ─┐
Socket 2 ─┤
Socket 3 ─┤
Socket 4 ─┤──→ epoll_wait()
Socket 5 ─┤
Socket 6 ─┘
When data becomes available on one or more sockets, epoll_wait() returns the corresponding events.
A simplified example looks like this:
struct epoll_event events[64];
int n = epoll_wait(
epfd,
events,
64,
-1
);
for (int i = 0; i < n; i++) {
int fd = events[i].data.fd;
if (events[i].events & EPOLLIN) {
read(fd, buffer, sizeof(buffer));
}
}
For general-purpose network applications, this is an extremely useful model.
3. The Core Idea of epoll: Wait for Events
One of the key characteristics of epoll_wait() is that the application can wait until an event occurs.
Conceptually:
CPU
│
↓
epoll_wait()
│
│
│ Sleep / Wait
│
↓
Packet Arrival
│
↓
Wake-up
│
↓
Process Packet
While no data is available, the CPU does not need to continuously execute the application.
This reduces CPU consumption and allows the system to use those resources elsewhere.
4. What Is Busy Polling?
Busy Polling takes a fundamentally different approach.
Instead of waiting for an event, the application repeatedly checks whether data is available.
For example:
while (running) {
if (data_available()) {
process_data();
}
}
Even when there is no data, the loop continues executing.
In other words:
The CPU keeps running and continuously checks whether data has arrived.
This is also commonly referred to as a Busy Loop or Polling Loop.
5. epoll vs. Busy Polling
At a high level:
epollBusy Polling
| When no data is available | Wait | Keep checking |
| CPU usage | Low | High |
| Wake-up process | Required | Not required |
| Implementation | Common | Specialized |
| Latency | Generally higher | Can be very low |
| CPU resources | Efficient | Dedicated |
| Power consumption | Lower | Higher |
The important point is:
Busy Polling is not automatically faster in every environment.
The result depends on the workload, hardware, operating system configuration, and application architecture.
6. Why Can Busy Polling Be Faster?
One important factor is the waiting and wake-up path.
A simplified event-driven flow may look like:
Packet Arrival
↓
Kernel
↓
Event Notification
↓
Wake-up
↓
Scheduler
↓
Application
↓
read()
↓
Process
With Busy Polling:
Application
↓
Polling
↓
Data Arrived?
↙ ↘
No Yes
│ │
└─ Loop ↓
Process
Because the application is already running, it does not depend on the same sleep/wakeup mechanism.
In certain environments, this can reduce latency and, particularly, tail latency.
7. But Busy Polling Has a Cost
There is an important trade-off.
Busy Polling continuously consumes CPU resources.
For example:
Core 0
└── Network Polling Thread
└── High CPU Utilization
Even when no data arrives, the CPU continues executing the polling loop.
This can result in:
- Higher CPU utilization
- Higher power consumption
- Less CPU capacity for other workloads
- Increased heat
- Potential CPU resource contention
In other words:
Busy Polling is a strategy for spending CPU resources to reduce waiting time.
8. Trading Systems Change the Equation
For a general-purpose server, conserving CPU resources is often important.
For a latency-sensitive trading system, however, minimizing latency may be the primary objective.
Consider a simplified trading flow:
Market Data
↓
Packet Arrival
↓
Decode
↓
Strategy
↓
Risk Check
↓
Order
↓
FEP
If this entire path needs to be processed within a very short time, the mechanism used to wait for incoming packets becomes important.
In some high-rate market data environments, data may arrive so frequently that the application spends very little time actually idle.
In such an environment, continuously polling for packets can be a reasonable architectural choice.
9. Is Busy Polling Simply "Wasting CPU"?
Not necessarily.
For general-purpose applications, it is easy to think:
CPU 100%
↓
Bad
But a low-latency system may intentionally dedicate an entire CPU core to network processing:
Core 0 → Network Polling
Core 1 → Market Data
Core 2 → Strategy
Core 3 → Order
In this configuration, high CPU utilization on Core 0 is not necessarily a problem.
The important question is:
What is the CPU actually doing?
If the CPU is continuously polling a high-rate network interface in order to minimize latency, the utilization is a deliberate part of the architecture.
10. epoll and Polling Are Not Always Mutually Exclusive
It is useful to avoid thinking of epoll and Busy Polling as completely opposing technologies.
Linux networking provides various mechanisms for polling and event notification, and some systems combine different techniques depending on their latency requirements.
For extremely latency-sensitive systems, engineers may consider approaches such as:
- Socket Busy Polling
- NIC polling
- Kernel bypass
- User-space networking
- DPDK
- Onload-style technologies
Conceptually, there are several levels of optimization:
Traditional I/O
↓
epoll
↓
Polling Optimization
↓
Busy Polling
↓
Kernel Bypass
↓
User-Space Networking
Not every system needs to go to the bottom of this stack.
11. Busy Polling and CPU Affinity
Busy Polling is closely related to CPU Affinity, which we discussed in Deep Dive #3.
For example:
Core 0
└── Network Polling Thread
Core 1
└── Market Data Thread
Core 2
└── Strategy Thread
Core 3
└── Order Thread
Assigning dedicated responsibilities to CPU cores can reduce thread migration and help maintain cache locality.
However, if the polling thread shares a CPU core with unrelated work:
Core 0
├── Polling
├── Logging
├── Monitoring
└── Other Tasks
the polling thread must compete for CPU resources.
Therefore, Busy Polling should be considered together with CPU topology and thread placement.
12. NUMA Matters Too
In Deep Dive #4, we discussed NUMA and Memory Locality.
Now the concepts begin to connect:
CPU Affinity
↓
Busy Polling
↓
NUMA Locality
For example, if a NIC is attached to NUMA Node 0:
NUMA Node 0
NIC
↓
Polling Thread
↓
Market Data
↓
Strategy
Keeping the processing path within the same NUMA domain may help maintain locality.
On the other hand:
NIC
↓
NUMA Node 0
↓
Interconnect
↓
NUMA Node 1
↓
Strategy
may introduce additional cross-NUMA data movement.
This connects the concepts from the previous articles:
CPU → Cache → Memory → NUMA → NIC
as one continuous data path.
13. TCP and UDP Have Different Characteristics
Busy Polling should also be considered in the context of the transport protocol.
For example, market data is often distributed using UDP multicast:
Exchange
↓
Multicast
↓
NIC
↓
UDP
↓
Market Data
Order transmission or session-based communication may use TCP:
Application
↓
TCP
↓
NIC
↓
Broker / Exchange
TCP and UDP have different characteristics, so the appropriate polling strategy depends on the entire system design.
For UDP-based market data, packet loss, sequence numbers, gap detection, and recovery mechanisms must also be considered.
Therefore:
UDP does not automatically mean Busy Polling.
14. Do Not Measure Latency Alone
Suppose Busy Polling reduces latency.
Does that automatically mean the system has improved?
Not necessarily.
Other metrics should be evaluated as well.
Latency
- Average
- P50
- P95
- P99
- P99.9
CPU
- CPU utilization
- CPU cycles
- IPC
- Context switches
- CPU migration
Network
- Packet rate
- Packet loss
- Receive queue
- NIC utilization
Application
- Throughput
- Queue depth
- Processing time
- End-to-end latency
For trading systems, tail latency can be more meaningful than the average.
15. When Should You Use epoll?
epoll remains an excellent choice for many systems.
It is particularly suitable when:
- Many sockets must be managed
- CPU efficiency matters
- Latency requirements are moderate
- The system is a general-purpose server
- Many connections are handled concurrently
- Scalability is important
For example:
Client 1 ─┐
Client 2 ─┤
Client 3 ─┤
Client 4 ─┤
Client 5 ─┤
↓
epoll
↓
Event Loop
This architecture is efficient and widely applicable.
16. When Should You Consider Busy Polling?
Busy Polling may be worth considering when:
- Extremely low latency is critical
- Packet rates are very high
- Dedicated CPU cores are available
- CPU resources can be traded for latency
- CPU/NIC topology can be controlled
- The workload resembles HFT or high-rate market data processing
But the final decision should always be based on measurement.
If Busy Polling significantly increases CPU usage while producing little or no latency improvement, it may not be a useful optimization.
17. The Most Important Concept: Trade-off
The choice can be viewed as a trade-off:
CPU Efficiency
↑
│
epoll ●
│
│
│
│ ● Busy Polling
│
└────────────────────→
Low Latency
General-purpose systems may prioritize CPU efficiency.
Certain HFT systems may prioritize extremely low and predictable latency.
There is no single approach that is optimal for every system.
18. FontesFintech Engineering Approach
When designing network processing for a low-latency system, the important question is not simply which API to use.
The important question is:
Where is the latency actually coming from?
A simplified end-to-end path might look like:
NIC
↓
Kernel / User Space
↓
Polling / Event Notification
↓
Packet Processing
↓
Market Data
↓
Strategy
↓
Risk
↓
Order
↓
FEP
We need to understand where latency is introduced along this path.
For example:
How frequently does data arrive?
How long does the CPU actually spend waiting?
Is event wake-up a meaningful part of the latency?
Can a dedicated CPU core be allocated?
What is the NIC-to-CPU NUMA topology?
Does the latency improvement from Busy Polling justify the additional CPU cost?
These questions should be answered through measurement before changing the architecture.
Conclusion
epoll() and Busy Polling are not simply "slow" and "fast" alternatives.
They represent two different approaches to using CPU resources while waiting for data.
epoll allows the CPU to wait efficiently when there is no work.
Busy Polling continuously consumes CPU resources in order to minimize waiting time and can provide very low and predictable latency in certain environments.
Therefore, in low-latency trading systems:
Saving CPU is not always the same as optimizing performance.
Sometimes dedicating an entire CPU core to polling can be a reasonable engineering trade-off.
But that decision should always be validated through measurement and benchmarking.
Key Takeaway
In low-latency systems, how you wait for data can be as important as how you process it.
Wait Less. Poll Smarter. Measure Everything.
Next: Trading Engineering — Deep Dive #6
Interrupts, Polling and Network Latency
In the next article, we will go one level deeper:
What actually happens between the moment a network packet arrives at the NIC and the moment application code begins processing it?
We will follow the path:
NIC Interrupt → IRQ → Kernel → NAPI → Socket → Application
and examine why the choice between Interrupts and Polling matters in low-latency systems.
