We had already discussed “Interrupts” in article “Interrupt – An important feature“. I would highly recommend reading it as a pre-request to this post.
As explained on Interrupt – An important feature, an interrupt is a signal that is generated by a module or a process of a microcontroller or a microprocessor. The microcontroller or microprocessor needs to stop it normal execution as soon as it receives the interrupt signal so that it will start processing the respective interrupt service routine i.e., ISR.
So, when the interrupt occurs, the processor must stop what it is currently doing, save its current state, and execute the interrupt handler (ISR) to handle the interrupt request. The interrupt latency is the time between when the interrupt signal is generated and when the interrupt handler begins to execute i.e. executes the first instruction of ISR.
Interrupt latency have direct impact on the performance of the system, especially in real-time systems where timely response is very important and critical. So, we should always try to minimize the interrupt latency. In this blog post, we will discuss different approaches to reduce the interrupt latency.
Reducing the interrupt latency should be our one of the main goals. We should consider below mentioned ways to target this goal i.e., reducing the interrupt latency.
Some interrupts are more critical than others, and they should be handled with higher priority. By prioritizing interrupts, the system can quickly respond to high-priority interrupts while deferring lower-priority interrupts to a later time. This can help us to ensure that critical system functions will get serviced as soon as possible. This further help us to minimize the impact of lower-priority interrupts on system performance.
To use interrupt prioritization effectively, it is important to carefully define the priority levels for each and every interrupt. So, the processor will always serve the higher priority ISR first and lower priority ISR later.
Use DMA transfer
If a peripheral has access to DMA, then we should use DMA instead of interrupt. Using DMA can decrease interrupt latency by reducing the amount of time the processor spends servicing interrupts. With DMA, data transfers between peripherals and memory can be performed without direct intervention by the processor, allowing the processor to perform other tasks. By using DMA, we will reduce the number of interrupts need to be executed by the processor and thus it helps us in decreasing the interrupt latency.
Let us understand this with example. Without DMA any peripheral may need to transfer a block of data to another memory segment byte-by-byte using interrupts to signal after each byte transfer. This can cause a significant amount of interrupt overhead and also delays the execution of other code. But, with DMA, the peripheral will transfer the entire block of data to memory in a single operation without involving the processor. In short, the DMA transfer happens in background and do not affect the main processing cycles of processor. This results in reduced number of interrupts required and so decreases the interrupt latency.
Keeping ISR short
Interrupt Service Routines (ISRs) should be kept as short as possible. This will lead to minimize the time the processor needs in servicing the interrupts.
As we know, when an interrupt is triggered then the processor temporarily suspends the execution of the current code, saves the context, and jumps to the ISR. If the ISR is long and takes a long time to execute, then we are delaying the execution of other interrupts and also the execution our normal code flow. Due to this, the performance and response time of the system gets decreased.
So, we should keep the ISR sort to minimize the response time, debugging time and also our frustration level.
Avoid using loops in ISR
Loops in an Interrupt Service Routine (ISR) should be avoided because they can cause the ISR to take a long time to execute. The side effect it is already discussed in our previous section.
Additionally, loops in ISRs can make the system harder to debug. It is so because the behavior of the ISR becomes unpredictable depending on the state of the system when the interrupt is executed. This makes it very difficult to reproduce and diagnose issues related to the ISR (that’s why some issues are seen as Ghost bugs which are very hard to replicate and are seen once in a while).
Avoid time consuming instructions
We should avoid time-consuming instructions such as memory allocation, dynamic memory allocation (e.g., malloc or calloc), and heavy computational operations (e.g., sqrt() etc).
To avoid these time-consuming instructions in the ISRs, we can use static memory allocation instead of dynamic memory allocation. We can also preallocate buffers and data structures. By doing this we will minimize the number of instructions that are required to perform the necessary operations in the ISR.
Additionally, we can use efficient algorithms and data structures that can perform the required operations in a short amount of time.
Never disable the interrupts
When interrupts are disabled, the processor will not respond to any new interrupt signals until interrupts are re-enabled. This means that any interrupts that occur while interrupts are disabled will be wither processed after long time or there is possibility that it may get lost.
Both the cases are very dangerous. Our moto is to reduce the interrupt latency but by disabling the interrupt, we may end up in increasing it. In some processors, the interrupt may also get lost which itself is very dangerous.
By disabling interrupts, we are making some unpredictable behavior in multi-threaded systems. E.g. If one thread disables interrupts, it can prevent other threads from servicing their interrupts, leading to delays and reduced performance.
Interrupt coalescing is a technique that can be used to reduce interrupt latency by minimizing the number of interrupts that the system needs to handle.
Interrupt coalescing works by delaying the processing of interrupts and grouping them together into larger, more efficient batches. This means that instead of handling many small interrupts separately, the system can handle a larger batch of interrupts all at once. By reducing the number of interrupts that the system needs to handle, interrupt coalescing can help reduce the overall interrupt latency and improve system performance.
For example, let’s say that a network interface card generates an interrupt every time a packet is received. If the system needs to process each of these interrupts separately, it could lead to a significant amount of interrupt handling overhead and increased latency. With interrupt coalescing, the system can wait until it has received a batch of packets before handling the interrupt. This means that instead of handling each interrupt separately, the system can handle a larger batch of packets all at once, reducing the overall interrupt handling overhead.
Interrupt coalescing can be implemented in hardware or software, depending on the specific system architecture and requirements. Some network cards, for example, have built-in interrupt coalescing features that allow them to group packets together before generating an interrupt. In other systems, interrupt coalescing may be implemented in software through the use of specialized interrupt handling routines that group interrupts together based on certain criteria.