Yes, a trade-off exist. By this, I mean that code that is faster and
uglier is not necessary better - the quantitative benefits from "fast
code" needs to be weighted against the maintenance complexity of the
code changes needed to achieve that speed.
The trade-off comes from business cost. Code that is more complex
requires more skilled programmers (and programmers with a more focused
skill set, such as ones with CPU architecture and design knowledge),
takes more time to read and understand the code and to fix bugs.
The business cost of developing and maintaining such code could be
in the range of 10x - 100x over normally-written code.
This maintenance cost is justifiable in some industries, in which
customers are willing to pay a very high premium for very fast software.
Some speed optimizations make better return-on-investment (ROI) than
others. Namely, some optimizations techniques can be applied with
lesser impact on code maintainability (preserving higher-level structure
and lower-level readability) compared to normally-written code.
Thus, a business owner should:
- Look at the costs and benefits,
- Make measurements and calculations
- Have the programmer measure the program speed
- Have the programmer estimate the development time needed for optimization
- Make own estimate about the increased revenue from faster software
- Have software architects or QA managers gauge qualitatively the drawbacks from reduced intuitiveness and readability of source code
- And prioritize the low-hanging fruits of software optimization.
These trade-offs are highly specific to circumstances.
These cannot be optimally decided without the participation of managers and
product owners.
These are highly specific to platforms. For example, desktop and mobile
CPUs have different considerations. Server and client applications
also have different considerations.
Yes, it is generally true that faster code looks different from
normally-written code. Any code that is different will take more time
to read. Whether that implies ugliness is in the eyes of the beholder.
The techniques that I have some exposure with are: (without trying to
claim any level of expertise) short-vector optimization (SIMD),
fine-grained task parallelism, memory pre-allocation and object reuse.
SIMD typically has severe impacts on low-level readability, even though
it typically doesn't require higher-level structural changes (provided
that the API is designed with bottleneck-prevention in mind).
Some algorithms can be transformed into SIMD easily (the embarassingly-
vectorizable). Some algorithms require more computation rearrangements
in order to use SIMD. In extreme cases such as wavefront SIMD parallelism,
entirely new algorithms (and patentable implementations) have to be
written to to take advantage.
Fine-grained task parallelization requires rearranging algorithms into
data flow graphs, and repeatedly apply functional (computational)
decomposition to the algorithm until no further margin benefit can be
gained. Decomposed stages are typically chained with continuation-style,
a concept borrowed from functional programming.
By functional (computational) decomposition, algorithms which could
have been normally-written in a linear and conceptually clear sequence
(lines of code that are executable in the same order they are written)
have to be broken down into fragments, and distributed into multiple
functions or classes. (See algorithm objectification, below.) This
change will greatly impede fellow programmers who are not familiar
with the decomposition design process which gave rise to such code.
To make such code maintainable, the authors of such code must write
elaborate documentations of the algorithm - far beyond the kind of
code commenting or UML diagrams done for normally-written code.
This is similar to the way researchers write their academic papers.
No, fast code need not be in contradiction with object-orientedness.
Put in another way, it is possible to implement very fast software that
is still object-oriented. However, toward the lower-end of that
implementation (at the nuts-and-bolts level where the majority of
computation occurs), the object design may deviate significantly from
designs obtained from object-oriented design (OOD). The lower-level design
is geared toward algorithm-objectification.
A few benefits of object-oriented programming (OOP), such as
encapsulation, polymorphism, and composition, can still be reaped from
low-level algorithm-objectification. This is the main justification for
using OOP at this level.
Most benefits of object-oriented design (OOD) are lost. Most
importantly, there is no intuitiveness in the low-level design.
A fellow programmer cannot learn how to work with the lower-level
code without first fully understanding how the algorithm had been
transformed and decomposed in the first place, and this understanding
is not obtainable from the resulting code.