C # /. NET 프로그램 최적화를위한 팁
요즘 최적화는 잃어버린 예술처럼 보입니다. 모든 프로그래머가 코드에서 최대한의 효율성을 뽑아 내던 때가 아니 었나요? 눈 속에서 5 마일을 걷는 동안 자주 그렇게합니까?
잃어버린 예술을 되 찾는 정신에서 C # /. NET 코드를 최적화하기 위해 간단하거나 복잡한 변경에 대해 알고있는 몇 가지 팁은 무엇입니까? 수행하려는 작업에 따라 매우 광범위한 것이기 때문에 팁에 컨텍스트를 제공하는 데 도움이됩니다. 예를 들면 :
- 많은 문자열을 함께 연결할 때
StringBuilder
대신 사용하십시오. 이에 대한 경고는 하단의 링크를 참조하십시오. string.Compare
다음과 같이하는 대신 두 문자열을 비교하는 데 사용 합니다.string1.ToLower() == string2.ToLower()
지금까지 측정하는 일반적인 합의가 핵심입니다. 이런 종류의 요점을 놓치게됩니다. 측정은 무엇이 잘못되었는지 또는 병목 현상이 발생한 경우 어떻게해야하는지 알려주지 않습니다. 한 번 문자열 연결 병목 현상이 발생하여 어떻게해야할지 몰랐으므로이 팁이 유용합니다.
이 글을 게시하기위한 나의 요점은 일반적인 병목 현상에 대한 장소를 확보하고 문제가 발생하기 전에 피할 수있는 방법입니다. 누구나 맹목적으로 따라야하는 플러그 앤 플레이 코드에 관한 것이 아니라, 적어도 어느 정도는 성능을 고려해야하며,주의해야 할 몇 가지 일반적인 함정이 있다는 것을 이해하는 것이 더 중요합니다.
팁이 왜 유용하고 어디에 적용되어야하는지 아는 것도 유용 할 수 있습니다. 을 위해 StringBuilder
팁 나는 오래전에했던 도움을 발견 존 소총의 사이트에 여기를 .
요즘 최적화는 잃어버린 예술처럼 보입니다.
예를 들어 현미경의 제조가 예술로 실행되는 날이 하루에 한 번있었습니다. 광학 원리는 제대로 이해되지 않았습니다. 부품의 표준화가 없었습니다. 튜브와 기어, 렌즈는 고도로 숙련 된 작업자가 수작업으로 만들어야했습니다.
오늘날 현미경은 공학 분야로 생산됩니다. 물리학의 기본 원리는 매우 잘 이해되고 있으며 기성 부품이 널리 사용 가능하며 현미경 제작 엔지니어는 기기가 수행하도록 설계된 작업에 최적으로 최적화하는 방법에 대해 정보에 입각 한 선택을 할 수 있습니다.
성능 분석이 "잃어버린 예술"이라는 것은 아주 좋은 것입니다. 그 예술은 예술 로서 실행 되었습니다 . 최적화는 그것이 무엇인지에 대해 접근해야합니다 : 견고한 엔지니어링 원칙을주의 깊게 적용하여 해결할 수 있는 엔지니어링 문제 .
나는 사람들이 vbscript / jscript / 활성 서버 페이지 / VB / C # 코드를 최적화하는 데 사용할 수있는 "팁과 트릭"목록에 대해 수년에 걸쳐 수십 번 요청을 받았습니다. 나는 항상 이것을 거부합니다. "팁과 트릭"을 강조하는 것은 성능에 접근하는 잘못된 방법입니다. 그런 식으로 이해하기 어렵고, 추론하기 어렵고, 유지하기 어려운 코드로 이어지며, 일반적으로 해당하는 간단한 코드보다 눈에 띄게 빠르지 않습니다.
성능에 접근하는 올바른 방법은 다른 문제와 마찬가지로 엔지니어링 문제로 접근하는 것입니다.
- 의미 있고 측정 가능하며 고객 중심의 목표를 설정하십시오.
- 현실적이지만 통제되고 반복 가능한 조건에서 이러한 목표에 대한 성능을 테스트하는 테스트 스위트를 빌드하십시오.
- 이러한 제품군에서 목표를 달성하지 못하는 것으로 나타나면 프로파일 러와 같은 도구를 사용하여 이유를 파악하십시오.
- 프로파일 러가 가장 성능이 떨어지는 하위 시스템으로 식별하는 것을 최적화하십시오. 각각의 성능 영향을 명확하게 이해할 수 있도록 모든 변경 사항에 대해 계속 프로파일 링하십시오.
- (1) 목표를 달성하고 소프트웨어를 배송하거나, (2) 목표를 달성 할 수있는 수준으로 수정하거나, (3) 목표를 달성 할 수 없어 프로젝트가 취소 될 때까지 반복합니다.
이는 기능 추가와 같은 다른 엔지니어링 문제를 해결하는 것과 동일합니다. 기능에 대한 고객 중심 목표 설정, 견고한 구현에 대한 진행 상황 추적, 신중한 디버깅 분석을 통해 발견 된 문제 해결, 다음까지 반복 당신은 발송하거나 실패합니다. 성능은 기능입니다.
복잡한 현대 시스템의 성능 분석에는 사소하거나 비현실적인 상황에 좁게 적용 할 수있는 트릭으로 가득 찬 가방이 아니라 견고한 엔지니어링 원칙에 대한 규율과 초점이 필요합니다. 팁과 요령을 적용하여 실제 성능 문제를 한 번도 해결 한 적이 없습니다.
좋은 프로파일 러를 얻으십시오.
좋은 프로파일 러없이 C # (실제로 모든 코드)을 최적화하려고 시도하지 마십시오. 실제로 샘플링 및 추적 프로파일 러를 모두 보유하면 크게 도움이됩니다.
좋은 프로파일 러가 없으면 잘못된 최적화를 만들 수 있으며 가장 중요한 것은 처음에 성능 문제가 아닌 루틴을 최적화하는 것입니다.
프로파일 링의 처음 세 단계는 항상 1) 측정, 2) 측정, 3) 측정 ...입니다.
최적화 지침 :
- 필요한 경우가 아니면하지 마십시오
- 개발자 대신 새로운 하드웨어를 문제에 던지는 것이 더 저렴하다면하지 마십시오.
- 생산과 동등한 환경에서 변화를 측정 할 수 없다면 그렇게하지 마십시오.
- CPU 와 메모리 프로파일 러 를 사용하는 방법을 모르는 경우에는하지 마십시오.
- 코드를 읽을 수 없거나 유지 관리 할 수 없게 만들 경우에는하지 마십시오.
프로세서가 계속 빨라짐에 따라 대부분의 애플리케이션에서 주요 병목 현상은 CPU가 아니라 대역폭입니다. 즉, 오프 칩 메모리 대역폭, 디스크 대역폭 및 네트워크 대역폭입니다.
맨 끝에서 시작 : YSlow를 사용하여 웹 사이트가 최종 사용자에게 느린 이유를 확인한 다음 뒤로 이동하여 데이터베이스 액세스를 너무 넓지 (열), 너무 깊지 않게 (행) 수정하십시오.
CPU 사용을 최적화하기 위해 무엇이든 할 가치가있는 매우 드문 경우에는 메모리 사용에 부정적인 영향을 미치지 않도록주의하십시오. 개발자가 CPU주기를 절약하기 위해 결과를 캐시하기 위해 메모리를 사용하려고 시도한 '최적화'를 보았습니다. 순 효과는 캐시 페이지 및 데이터베이스 결과에 사용 가능한 메모리를 줄이는 것이 었으며 이로 인해 응용 프로그램이 훨씬 느려졌습니다! (측정 규칙을 참조하십시오.)
또한 최적화되지 않은 '멍청한'알고리즘이 '영리한'최적화 알고리즘을 능가하는 경우도 보았습니다. 얼마나 훌륭한 컴파일러 작성자와 칩 설계자가 '비효율적 인'루핑 코드를 파이프 라이닝을 사용하여 온칩 메모리에서 완전히 실행할 수있는 매우 효율적인 코드로 바꾸는 지 과소 평가하지 마십시오. '효율적'이라고 생각했던 언 래핑 된 내부 루프를 거꾸로 세는 '영리한'트리 기반 알고리즘은 실행 중에 온칩 메모리에 머 무르지 못했기 때문에 쉽게 이길 수 있습니다. (측정 규칙을 참조하십시오.)
ORM으로 작업 할 때 N + 1 Selects를 알고 있어야합니다.
List<Order> _orders = _repository.GetOrders(DateTime.Now);
foreach(var order in _orders)
{
Print(order.Customer.Name);
}
고객이 열심히로드하지 않으면 데이터베이스로 여러 번 왕복 할 수 있습니다.
- 마법 번호를 사용하지 말고 열거를 사용하십시오.
- 값을 하드 코딩하지 마십시오.
- 형식이 안전하고 권투 및 개봉을 방지하므로 가능한 경우 제네릭을 사용하십시오.
- 꼭 필요한 곳에 오류 처리기를 사용하십시오.
- 폐기, 폐기, 폐기하십시오. CLR은 데이터베이스 연결을 닫는 방법을 알지 못하므로 사용 후 연결을 닫고 관리되지 않는 리소스를 폐기하십시오.
- 상식을 사용하십시오!
좋아, 내가 가장 좋아하는 것을 던져야한다. 작업이 사람의 상호 작용을 위해 충분히 길면 디버거에서 수동 중단을 사용하십시오.
Vs. 프로파일 러는 무슨 일이 일어나고 있는지 실제로 이해하는 데 사용할 수있는 호출 스택과 변수 값을 제공합니다.
이 작업을 10 ~ 20 회 수행하면 어떤 최적화가 실제로 어떤 차이를 만들 수 있는지 알 수 있습니다.
방법을 병목으로 식별했지만 어떻게해야할지 모르겠다면 본질적으로 멈춘 것입니다.
So I'll list a few things. All of these things are not silver bullets and you will still have to profile your code. I'm just making suggestions for things you could do and can sometimes help. Especially the first three are important.
- Try solving the problem using just (or: mainly) low-level types or arrays of them.
- Problems are often small - using a smart but complex algorithm does not always make you win, especially if the less-smart algorithm can be expressed in code that only uses (arrays of) low level types. Take for example InsertionSort vs MergeSort for n<=100 or Tarjan's Dominator finding algorithm vs using bitvectors to naively solve the data-flow form of the problem for n<=100. (the 100 is of course just to give you some idea - profile!)
- Consider writing a special case that can be solved using just low-level types (often problem instances of size < 64), even if you have to keep the other code around for larger problem instances.
- Learn bitwise arithmetic to help you with the two ideas above.
- BitArray can be your friend, compared to Dictionary, or worse, List. But beware that the implementation is not optimal; You can write a faster version yourself. Instead of testing that your arguments are out of range etc., you can often structure your algorithm so that the index can not go out of range anyway - but you can not remove the check from the standard BitArray and it is not free.
- As an example of what you can do with just arrays of low level types, the BitMatrix is a rather powerful structure that can be implemented as just an array of ulongs and you can even traverse it using an ulong as "front" because you can take the lowest order bit in constant time (compared with the Queue in Breadth First Search - but obviously the order is different and depends on the index of the items rather than purely the order in which you find them).
- Division and modulo are really slow unless the right hand side is a constant.
- Floating point math is not in general slower than integer math anymore (not "something you can do", but "something you can skip doing")
- Branching is not free. If you can avoid it using a simple arithmetic (anything but division or modulo) you can sometimes gain some performance. Moving a branch to outside a loop is almost always a good idea.
People have funny ideas about what actually matters. Stack Overflow is full of questions about, for example, is ++i
more "performant" than i++
. Here's an example of real performance tuning, and it's basically the same procedure for any language. If code is simply written a certain way "because it's faster", that's guessing.
Sure, you don't purposely write stupid code, but if guessing worked, there would be no need for profilers and profiling techniques.
The truth is that there is no such thing as the perfect optimised code. You can, however, optimise for a specific portion of code, on a known system (or set of systems) on a known CPU type (and count), a known platform (Microsoft? Mono?), a known framework / BCL version, a known CLI version, a known compiler version (bugs, specification changes, tweaks), a known amount of total and available memory, a known assembly origin (GAC? disk? remote?), with known background system activity from other processes.
In the real world, use a profiler, and look at the important bits; usually the obvious things are anything involving I/O, anything involving threading (again, this changes hugely between versions), and anything involving loops and lookups, but you might be surprised at what "obviously bad" code isn't actually a problem, and what "obviously good" code is a huge culprit.
Tell the compiler what to do, not how to do it. As an example, foreach (var item in list)
is better than for (int i = 0; i < list.Count; i++)
and m = list.Max(i => i.value);
is better than list.Sort(i => i.value); m = list[list.Count - 1];
.
By telling the system what you want to do it can figure out the best way to do it. LINQ is good because its results aren't computed until you need them. If you only ever use the first result, it doesn't have to compute the rest.
Ultimately (and this applies to all programming) minimize loops and minimize what you do in loops. Even more important is to minimize the number of loops inside your loops. What's the difference between an O(n) algorithm and an O(n^2) algorithm? The O(n^2) algorithm has a loop inside of a loop.
I don't really try to optimize my code but at times I will go through and use something like reflector to put my programs back to source. It is interesting to then compare what I wrong with what the reflector will output. Sometimes I find that what I did in a more complicated form was simplified. May not optimize things but helps me to see simpler solutions to problems.
참고URL : https://stackoverflow.com/questions/2473666/tips-for-optimizing-c-net-programs
'developer tip' 카테고리의 다른 글
Ruby on Rails에서 호스트 이름 또는 IP 가져 오기 (0) | 2020.10.13 |
---|---|
임베디드 개발에 C ++ 대신 C를 사용하는 이유가 있습니까? (0) | 2020.10.13 |
jQuery를 사용하여 JSON 객체를 만드는 방법 (0) | 2020.10.13 |
Windows 시작시 WAMP가 자동으로 시작되도록합니다 (로그온 또는 UAC 간섭없이). (0) | 2020.10.13 |
Spring Boot : PasswordEncoder를 지정하는 방법은 무엇입니까? (0) | 2020.10.13 |