This is normally talked in the context of C/C++ and Data Oriented Design, but it really is about assembly, so I'll repeat:
There are 3 kinds of jump/conditional branch:
- Those that chose between one element or the other. Example min() max(), if dontInherit then x = 0; else x = 1 etc. Unless the amount of conditional data is massive, these are often best treated with branchless instructions (i.e. SSE's maxps, minps, usage of & and |; substract, '&' then add, cmov, fsel in ppc, etc)
- Those that are internal to the code for book keeping. i.e. if pointer != nullptr then pointer->doSomething(). These are also avoidable. A common pattern is to create a shared dummy pointer that does nothing and create a rule that no pointer in that context can be null. Hence you no longer have to check about pointer validity. Of course this requires more work, so it's only useful when dealing with hotspots or having lots of cache misses.
- Those that are inherent to the logic of the application (i.e. if enemyOnSight then engage() else patrol() ) these are not avoidable, and probably best kept as conditional jumps / whithin branches anyway.
Now, assuming that you've already done your job to reduce the number of jump using the 3 guidelines above, it's hard to tell. A jump instruction in itself is not expensive (1 to 3 clocks if I recall?). The actual problem is the pipeline stall, which can be very costly and CPUs attempt to mitigate through branch prediction and speculative execution.
Since branch prediction rely on heuristics that may change on each processor design and vendor; two jump that predicts correctly will probably be faster than one jump that misspredicts. And you have no sure way to tell how a CPU will predict (unless you're target a specific HW, like what happens with consoles).
What you CAN do, is that most prediction algorithms rely on repetitive patterns. And which branch will be taken depends on the DATA you'll feed your program, not on the code executing it.
For example if we call a jump as T for Taken, and N for not taken; if your code behaves like this while running: TTTNTTTNTTTNTTTN it will predict very well. Same if the pattern is TTNNNTTTNTTNNNTTTN (for a good guideline look at n-gram examples)
If the data fed to your code is very random (like noise) with no distinguishable pattern, it will be very hard to predict, no matter how good or advanced the branch prediction algorithms are.
For example I've heard from engineers the CPU branch predictors were being terrible when processing human DNA sequences because of their inherently bad predictability.
Why is this important? Although you may not have full control on your data, assuming you will work on it very often (i.e. if it's a game, you will loop on it every frame), you can preprocess: Simply sorting your data so that the code executes everything in a predictable manner helps the CPU a lot. This is the best way to optimize your application rather than dealing with how many jumps your code actually has.
If you're writing in pure assembly, sometimes though, you can place an additional jmp instruction (or flip a jz for a jnz, etc) because you know the general case and help the branch predictor by biasing it towards certain outcome.
This is often a terrible idea because you have no control on the machine it will run on (and hence how the branch predictor actually works) and you rarely know in advance the data you will be feeding it. Furthermore you have no way to tell how these little changes will affect the performance of the overall program. But there MIGHT be a few isolated cases where these little hacks actually work and make a difference.