Similarly, since serial messages are only occasional and not more than every 50ms, while AC cycles happen every 1/120 s (8.3ms),
get rid your calculations of powerDelay in ZeroCrossDetected and move them into the serial receive code.
Done. I only update the powerDelay values after I have read in a new set of channel values, see the updatePowerDelayValues() in the code. I'm turning the interrupts off during this process and I'm not sure if that is a good or bad idea.
I asked this next question earlier and I think I've seen to conflicting "answers" can someone give me the straight poop (I've changed the code this is no longer true but I would like to know)? "Given that the powerDelay array is only assigned values inside one of the ISRs and only read from the other ISR and the ISRs are not nested: Can I remove the volatile attribute from the declaration? That should give me a few extra cycles in speed shouldn't it?"
You can. You shouldn't. It won't make any difference...
In each ISR each element of powerDelay is accessed just once (read in one ISR; written in the other). Removing volatile only helps when there are multiple accesses. Removing volatile eliminates redundant loads from memory but there aren't any.
This would get better if powerDelay isn't volatile, and if it's smaller, but it still doesn't need to happen every half-cycle.
Or you could look at making the copy into a memcpy() call, which would "short circuit" the (correct) volatile declarations.
Maybe I'm just reading more into the second one than is really there.