My main question is when should I trade the easy but somewhat inefficient ways of doing tasks for big giant complicated beasts that do things extremely quickly but destroy any possible ways of upgrading and make the code excessively difficult and prone to rewriting anyway by the next developer?
"Worry about how the code looks and its readability/understandability now, and performance later if absolutely necessary" is the easy way out, and generally unhelpful. a good design will be easy to maintain, easy to read, and efficient.
performance is one common component of a good design. if your program is slow and wasteful, it's really not reusable. when you need to fix that mess, you force updates on your clients, unless it's just too time consuiming for them to update. that slow program becomes the big mess that is too costly to improve. then they choose an alternative becasue it does not suit their needs. diagnosing, updating, and dealing with side effects of improvements to a bad design often outweigh the initial development time of writing it to be efficient, work correctly, and has a genrally good design. that program is highly reusable and requires low maintentance (win).
so, the short answer to your question is "don't be wasteful. write for reuse. it's ok to be lazy when prototyping/developing proofs of concepts, but don't use that prototype for production code.".
do be aware of and avoid wasteful designs when writing production programs and programs that you intend to reuse. during implementation is the ideal time to write your program to not be wasteful - you have a clear idea of the details and its operation, and it's really painful and ineffective to fix after it's written. a lot of people believe a little profiling (maybe) at the end or if there is a problem is adequete, when it's usually too time consuming to redesign/change and the inefficiencies are so many and so widespread that you don't understand the program well based on the results of a profile. this approach takes little time during implementation and (assuming you have done this enough times) typically results in a design that is several times faster, and is reusable in many more contexts. not being wasteful, choosing good algorithms, giving thought to your implementations, and reusing the right implementations are all components of good design; all of which improves readability, maintainability, and reuse more often than hurts it.