Nice programing

최적화가 시기상조입니까?

nicepro 2020. 10. 8. 18:56
반응형

최적화가 시기상조입니까?


Knuth가 말했듯이,

97 % 정도의 작은 효율성은 잊어야합니다. 조기 최적화는 모든 악의 근원입니다.

이것은 "가장 효율적인 루프 메커니즘", "SQL 최적화 기술"과 같은 질문에 대한 Stack Overflow 답변에서 자주 나오는 것입니다. ( 등등 ). 이러한 최적화 팁 질문에 대한 표준 답변은 코드를 프로파일 링하고 먼저 문제가 있는지 확인하고 그렇지 않은 경우 새로운 기술이 필요하지 않은지 확인하는 것입니다.

내 질문은 특정 기술이 다르지만 특별히 모호하거나 난독 화되지 않은 경우 실제로 조기 최적화로 간주 될 수 있습니까?

다음은 Randall Hyde의 The Fallacy of Premature Optimization 이라는 관련 기사 입니다.


Don Knuth 는 컴퓨터 코드의 가장 중요한 기능이 프로그래머의 의도를 인간 독자에게 전달하는 것이라고 믿었 기 때문에 문학적 프로그래밍 운동을 시작했습니다 . 성능이라는 이름으로 코드를 이해하기 어렵게 만드는 코딩 관행은 조기 최적화입니다.

최적화라는 이름으로 도입 된 특정 관용구는 너무 인기가 많아서 모든 사람이이를 이해하고 너무 이른시기 가 아니라 예상 됩니다. 예는 다음과 같습니다.

  • 사용 포인터 연산 대신 배열 표기 이러한 관용구의 사용 등을 포함하여, C의

    for (p = q; p < lim; p++)
    
  • 다음과 같이 전역 변수를 Lua의 지역 변수 에 다시 바인딩

    local table, io, string, math
        = table, io, string, math
    

이러한 관용구를 넘어 위험에 처한 지름길을 선택하십시오 .

모든 최적화는

  • 프로그램이 너무 느립니다 (많은 사람들이이 부분을 잊어 버립니다).

  • 당신은이 측정 (프로필 또는 유사한) 것을 보여주는 최적화 물건을 향상시킬 수를 .

(메모리 최적화도 허용됩니다.)

질문에 대한 직접적인 답변 :

  • 만약 당신의 "다른"기술 이 프로그램을 이해하기 어렵게 만든 다면 , 그것은 조기 최적화 입니다.

편집 : 의견에 대한 응답으로 삽입 정렬과 같은 더 간단한 알고리즘 대신 퀵 정렬을 사용 하는 것은 모든 사람이 이해하고 기대하는 관용구 의 또 다른 예입니다 . (라이브러리 정렬 루틴을 사용하는 대신 자신 만의 정렬 루틴을 작성한다면, 아주 좋은 이유가 있기를 바랍니다.)


IMHO, 최적화의 90 %는 인식 된 현재 및 더 중요한 미래 요구 사항을 기반으로 설계 단계에서 발생해야합니다. 애플리케이션이 필요한 부하로 확장되지 않아 프로파일 러를 제거해야하는 경우 너무 늦게 남겨두고 IMO는 문제를 해결하지 못하는 동안 많은 시간과 노력을 낭비하게됩니다.

일반적으로 유일하게 가치있는 최적화는 속도 측면에서 수십 배의 성능 향상을 얻거나 스토리지 또는 대역폭 측면에서 승수를 얻는 최적화입니다. 이러한 유형의 최적화는 일반적으로 알고리즘 선택 및 저장 전략과 관련이 있으며 기존 코드로 되 돌리는 것이 매우 어렵습니다. 시스템을 구현하는 언어에 대한 결정에 영향을 미칠 수 있습니다.

따라서 제 조언은 코드가 아닌 요구 사항을 기반으로 초기에 최적화하고 앱의 가능한 연장 수명을 고려하십시오.


프로파일 링하지 않았다면 시기상조입니다.


내 질문은 특정 기술이 다르지만 특별히 모호하거나 난독 화되지 않은 경우 실제로 조기 최적화로 간주 될 수 있습니까?

음 .. 비용면에서 동일한 두 가지 기술 (사용, 읽기, 수정에 대한 동일한 노력)이 준비되어 있고 하나가 더 효율적입니다. 아니요, 더 효율적인 것을 사용하는 것은이 경우에 이르지 않습니다.

코드 작성을 중단하여 일반적인 프로그래밍 구조 / 라이브러리 루틴에 대한 대안을 찾다가, 작성중인 내용의 상대적인 속도를 알고있는 경우에도 어딘가에 더 효율적인 버전이있을 가능성이 실제로는 중요하지 않습니다. .. 그건 조기.


다음은 조기 최적화를 피하는 전체 개념에서 볼 수있는 문제입니다.

말하는 것과하는 것 사이에는 단절이 있습니다.

나는 많은 성능 튜닝을 수행하여 잘 설계된 코드에서 큰 요소를 짜내 어 조기 최적화없이 수행 된 것처럼 보입니다. 여기에 예가 있습니다.

거의 모든 경우에 최적이 아닌 성능의 이유는 내가 galloping generality 라고 부르는 이유 인데, 이는 추상적 인 다중 계층 클래스와 철저한 객체 지향 디자인을 사용하기 때문입니다. 단순한 개념은 덜 우아 하지만 완전히 충분합니다.

알림 기반 아키텍처, 단순히 객체의 부울 속성을 설정하는 것만으로도 활동의 무한한 파급 효과를 가질 수있는 정보 은닉과 같은 이러한 추상적 인 디자인 개념을 가르치는 교재에서 주어진 이유는 무엇입니까? 효율성 .

그래서, 그것이 조기 최적화였습니까?


먼저 코드가 작동하도록합니다. 둘째, 코드가 올바른지 확인하십시오. 셋째, 빨리하십시오.

3 단계 이전에 수행 된 코드 변경 은 확실히 시기상조입니다. 이전에 만든 디자인 선택 (예 : 적합한 데이터 구조 사용)을 분류하는 방법을 완전히 확신하지 못합니다. 프로파일 링을 사용하고 결과를 비교할 올바른 (빈번하게 느리지 만) 참조 구현을 사용할 수있는 단계입니다.


당신이 말하는 것처럼 보이는 것은 해시 기반 조회 컨테이너를 사용하는 것과 같은 최적화와 많은 키 조회가 수행 될 때 배열과 같은 인덱싱 된 컨테이너를 사용하는 것입니다. 입니다 하지 조기 최적화,하지만 뭔가 당신은 설계 단계에서 결정해야합니다.

Knuth 규칙이 ​​사용하는 최적화 유형은 가장 일반적인 코드 경로의 길이를 최소화하고, 예를 들어 어셈블리에서 다시 작성하거나 코드를 단순화하여 덜 일반적으로 만드는 등 가장 많이 실행되는 코드를 최적화하는 것입니다. 그러나 이렇게하는 것은 코드의 어떤 부분이 이런 종류의 최적화가 필요한지 확신 할 때까지 아무 소용이 없습니다. 최적화는 코드를 이해하거나 유지하기 어렵게 만들 것입니다. 따라서 "조기 최적화는 모든 악의 근원"입니다.

Knuth also says it is always better to, instead of optimizing, change the algorithms your program uses, the approach it takes to a problem. For example whereas a little tweaking might give you a 10% increase of speed with optimization, changing fundamentally the way your program works might make it 10x faster.

In reaction to a lot of the other comments posted on this question: algorithm selection != optimization


데이터베이스 관점에서 설계 단계에서 최적의 설계를 고려하지 않는 것은 기껏해야 어리석은 일입니다. 데이터베이스는 쉽게 리팩터링되지 않습니다. 설계가 잘못되면 (최적화를 고려하지 않는 설계는 조기 최적화라는 말도 안되는 넌센스 뒤에 숨어도 상관없이) 데이터베이스가 너무 기본적이기 때문에 거의 복구 할 수 없습니다. 전체 시스템의 운영. 응용 프로그램 전체에서 커서를 사용했기 때문에 백만 명의 사용자와 사람들이 소리를지를 때까지 기다리는 것보다 예상되는 상황에 맞는 최적의 코드를 고려하여 올바르게 디자인하는 것이 훨씬 저렴합니다. sargeable 코드 사용, 가능한 최상의 인덱스 선택 등과 같은 기타 최적화는 디자인 타임에만 수행하는 것이 좋습니다. 빠르고 더러운 것을 그렇게 부르는 이유가 있습니다. 잘 작동하지 않기 때문에 빠른 코드를 좋은 코드 대신 사용하지 마십시오. 또한 솔직히 데이터베이스의 성능 조정을 이해하면 성능이 좋지 않은 코드를 작성하는 데 걸리는 시간보다 같은 시간에 잘 수행 될 가능성이 더 높은 코드를 작성할 수 있습니다. 좋은 성능의 데이터베이스 디자인을 배우는 데 시간을 할애하지 않는 것은 모범 사례가 아니라 개발자의 게으름입니다.


최대의 요점은 일반적으로 최적화가 복잡하고 복잡 하다는 것 입니다. 그리고 일반적으로 아키텍트 / 디자이너 / 프로그래머 / 메인테이너는 무슨 일이 일어나고 있는지 이해하기 위해 명확하고 간결한 코드가 필요합니다.

특정 최적화가 명확하고 간결하다면 자유롭게 실험 해보세요 (하지만 돌아가서 해당 최적화가 효과적인지 확인하세요). 요점은 성능의 이점이 최적화 작성 및 유지 비용을 능가 할 때까지 개발 프로세스 전반에 걸쳐 코드를 명확하고 간결하게 유지하는 것입니다.


성능 문제가 확인 된 경우에만 최적화하려고합니다.

조기 최적화에 대한 나의 정의는 '성능 문제로 알려지지 않은 코드에 낭비되는 노력'입니다. 확실히 최적화를위한 시간과 장소가 있습니다. 그러나 비결은 애플리케이션의 성능에 영향을 미치고 추가 비용이 성능 저하보다 중요한 경우에만 추가 비용을 지출하는 것입니다.

코드 (또는 DB 쿼리)를 작성할 때 '효율적인'코드를 작성하려고 노력합니다 (즉, 의도 된 기능을 수행하는 코드, 합리적인 가장 간단한 논리로 빠르고 완벽하게 수행합니다.) '효율적인'코드가 반드시 '최적화 된'코드와 동일하지는 않습니다. 암호. 최적화는 종종 코드에 추가 복잡성을 도입하여 해당 코드의 개발 및 유지 관리 비용을 모두 증가시킵니다.

내 조언 : 이점을 정량화 할 수있을 때만 최적화 비용을 지불하십시오.


프로그래밍 할 때 많은 매개 변수가 중요합니다. 이들 중 :

  • 가독성
  • 유지 보수성
  • 복잡성
  • 견고 함
  • 단정
  • 공연
  • 개발 시간

최적화 (성능을 추구)는 종종 다른 매개 변수를 희생하고 이러한 영역의 "손실"과 균형을 이루어야합니다.

잘 수행되는 잘 알려진 알고리즘을 선택할 수있는 옵션이 있으면 사전에 "최적화"하는 비용이 허용되는 경우가 많습니다.


최적화는 매우 높은 수준에서 매우 낮은 수준까지 다양한 수준에서 발생할 수 있습니다.

  1. 좋은 아키텍처, 느슨한 결합, 모듈성 등으로 시작하십시오.

  2. 문제에 적합한 데이터 구조와 알고리즘을 선택하십시오.

  3. 메모리를 최적화하여 캐시에 더 많은 코드 / 데이터를 넣으십시오. 메모리 하위 시스템은 CPU보다 10 ~ 100 배 느리고 데이터가 디스크에 페이징되면 1000 ~ 10,000 배 느립니다. 메모리 소비에주의를 기울이면 개별 명령어를 최적화하는 것보다 큰 이점을 얻을 수 있습니다.

  4. 각 함수 내에서 흐름 제어 문을 적절하게 사용합니다. (불변 표현식을 루프 본문 외부로 이동하십시오. 스위치 / 케이스 등에 가장 일반적인 값을 먼저 넣으십시오.)

  5. 각 문 내에서 올바른 결과를 산출하는 가장 효율적인 식을 사용하십시오. (곱하기 vs. 시프트 등)

나누기 식을 사용할지 시프트 식을 사용할 지에 대한 짤막한 선택이 반드시 조기 최적화 는 아닙니다 . 아키텍처, 데이터 구조, 알고리즘, 메모리 공간 및 흐름 제어를 먼저 최적화하지 않고 그렇게하는 경우에는 시기상조입니다.

물론 목표 성능 임계 값을 정의하지 않으면 모든 최적화가 시기상조입니다.

대부분의 경우 다음 중 하나입니다.

A) 높은 수준의 최적화를 수행하여 목표 성능 임계 값에 도달 할 수 있으므로 표현식을 조작 할 필요가 없습니다.

또는

B) 가능한 모든 최적화를 수행 한 후에도 목표 성능 임계 값을 충족하지 못하며 낮은 수준의 최적화는 가독성 손실을 정당화하기에 충분한 성능 차이를 만들지 않습니다.

In my experience, most optimization problems can be solved at either the architecture/design or data-structure/algorithm level. Optimizing for memory footprint is often (though not always) called for. But it's rarely necessary to optimize the flow control & expression logic. And in those cases where it actually is necessary, it's rarely sufficient.


Norman's answer is excellent. Somehow, you routinely do some "premature optimization" which are, actually, best practices, because doing otherwise is known to be totally inefficient.

For example, to add to Norman's list:

  • Using StringBuilder concatenation in Java (or C#, etc.) instead of String + String (in a loop);
  • Avoiding to loop in C like: for (i = 0; i < strlen(str); i++) (because strlen here is a function call walking the string each time, called on each loop);
  • It seems in most JavaScript implementations, it is faster to do too for (i = 0 l = str.length; i < l; i++) and it is still readable, so OK.

And so on. But such micro-optimizations should never come at the cost of readability of code.


The need to use a profiler should be left for extreme cases. The engineers of the project should be aware of where performance bottlenecks are.

I think "premature optimisation" is incredibly subjective.

If I am writing some code and I know that I should be using a Hashtable then I will do that. I won't implement it in some flawed way and then wait for the bug report to arrive a month or a year later when somebody is having a problem with it.

Redesign is more costly than optimising a design in obvious ways from the start.

Obviously some small things will be missed the first time around but these are rarely key design decisions.

Therefore: NOT optimising a design is IMO a code smell in and of itself.


It's worth noting that Knuth's original quote came from a paper he wrote promoting the use of goto in carefully selected and measured areas as a way to eliminate hotspots. His quote was a caveat he added to justify his rationale for using goto in order to speed up those critical loops.

[...] again, this is a noticeable saving in the overall running speed, if, say, the average value of n is about 20, and if the search routine is performed about a million or so times in the program. Such loop optimizations [using gotos] are not difficult to learn and, as I have said, they are appropriate in just a small part of a program, yet they often yield substantial savings. [...]

And continues:

The conventional wisdom shared by many of today's software engineers calls for ignoring efficiency in the small; but I believe this is simply an overreaction to the abuses they see being practiced by pennywise-and-pound-foolish programmers, who can't debug or maintain their "optimized" programs. In established engineering disciplines a 12% improvement, easily obtained, is never considered marginal; and I believe the same viewpoint should prevail in software engineering. Of course I wouldn't bother making such optimizations on a oneshot job, but when it's a question of preparing quality programs, I don't want to restrict myself to tools that deny me such efficiencies [i.e., goto statements in this context].

Keep in mind how he used "optimized" in quotes (the software probably isn't actually efficient). Also note how he isn't just criticizing these "pennywise-and-pound-foolish" programmers, but also the people who react by suggesting you should always ignore small inefficiencies. Finally, to the frequently-quoted part:

There is no doubt that the grail of efficiency leads to abuse. Programmers waste enormous amounts of time thinking about, or worrying about, the speed of noncritical parts of their programs, and these attempts at efficiency actually have a strong negative impact when debugging and maintenance are considered. We should forgot about small efficiencies, say 97% of the time; premature optimization is the root of all evil.

... and then some more about the importance of profiling tools:

It is often a mistake to make a priori judgments about what parts of a program are really critical, since the universal experience of programmers who have been using measurement tools has been that their intuitive guesses fail. After working with such tools for seven years, I've become convinced that all compilers written from now on should be designed to provide all programmers with feedback indicating what parts of their programs are costing the most; indeed, this feedback should be supplied automatically unless it has been specifically turned off.

People have misused his quote all over the place, often suggesting that micro-optimizations are premature when his entire paper was advocating micro-optimizations! One of the groups of people he was criticizing who echo this "conventional wisdom" as he put of always ignoring efficiencies in the small are often misusing his quote which was originally directed, in part, against such types who discourage all forms of micro-optimization.

Yet it was a quote in favor of appropriately applied micro-optimizations when used by an experienced hand holding a profiler. Today's analogical equivalent might be like, "People shouldn't be taking blind stabs at optimizing their software, but custom memory allocators can make a huge difference when applied in key areas to improve locality of reference," or, "Handwritten SIMD code using an SoA rep is really hard to maintain and you shouldn't be using it all over the place, but it can consume memory much faster when applied appropriately by an experienced and guided hand."

Any time you're trying to promote carefully-applied micro-optimizations as Knuth promoted above, it's good to throw in a disclaimer to discourage novices from getting too excited and blindly taking stabs at optimization, like rewriting their entire software to use goto. That's in part what he was doing. His quote was effectively a part of a big disclaimer, just like someone doing a motorcycle jump over a flaming fire pit might add a disclaimer that amateurs shouldn't try this at home while simultaneously criticizing those who try without proper knowledge and equipment and get hurt.

What he deemed "premature optimizations" were optimizations applied by people who effectively didn't know what they were doing: didn't know if the optimization was really needed, didn't measure with proper tools, maybe didn't understand the nature of their compiler or computer architecture, and most of all, were "pennywise-and-pound-foolish", meaning they overlooked the big opportunities to optimize (save millions of dollars) by trying to pinch pennies, and all while creating code they can no longer effectively debug and maintain.

If you don't fit in the "pennywise-and-pound-foolish" category, then you aren't prematurely optimizing by Knuth's standards, even if you're using a goto in order to speed up a critical loop (something which is unlikely to help much against today's optimizers, but if it did, and in a genuinely critical area, then you wouldn't be prematurely optimizing). If you're actually applying whatever you're doing in areas that are genuinely needed and they genuinely benefit from it, then you're doing just great in the eyes of Knuth.


Premature optimization to me means trying to improve the efficiency of your code before you have a working system, and before you have actually profiled it and know where the bottleneck is. Even after that, readability and maintainability should come before optimization in many cases.


I don't think that recognized best practices are premature optimizations. It's more about burning time on the what ifs that are potential performance problems depending on the usage scenarios. A good example: If you burn a week trying to optimize reflecting over an object before you have proof that it is a bottleneck you are prematurely optimizing.


Unless you find that you need more performance out of your application, due to either a user or business need, there's little reason to worry about optimizing. Even then, don't do anything until you've profiled your code. Then attack the parts which take the most time.


The way I see it is, if you optimize something without knowing how much performance you can gain in different scenario IS a premature optimization. The goal of code should really making it easiest for human to read.


As I posted on a similar question, the rules of optimisation are:

1) Don't optimise

2) (for experts only) Optimise later

When is optimisation premature? Usually.

The exception is perhaps in your design, or in well encapsulated code that is heavily used. In the past I've worked on some time critical code (an RSA implementation) where looking at the assembler that the compiler produced and removing a single unnecessary instruction in an inner loop gave a 30% speedup. But, the speedup from using more sophisticated algorithms was orders of magnitude more than that.

Another question to ask yourself when optimising is "am I doing the equivalent of optimising for a 300 baud modem here?". In other words, will Moore's law make your optimisation irrelevant before too long. Many problems of scaling can be solved just by throwing more hardware at the problem.

Last but not least it's premature to optimise before the program is going too slowly. If it's web application you're talking about, you can run it under load to see where the bottlenecks are - but the likelihood is that you will have the same scaling problems as most other sites, and the same solutions will apply.

edit: Incidentally, regarding the linked article, I would question many of the assumptions made. Firstly it's not true that Moore's law stopped working in the 90s. Secondly, it's not obvious that user's time is more valuable than programmer's time. Most users are (to say the least) not frantically using every CPU cycle available anyhow, they are probably waiting for the network to do something. Plus there is an opportunity cost when programmer's time is diverted from implementing something else, to shaving a few milliseconds off something that the program does while the user is on the phone. Anything longer than that isn't usually optimisation, it's bug fixing.

참고URL : https://stackoverflow.com/questions/385506/when-is-optimisation-premature

반응형