>백엔드 개발 >Golang >Go: 간단한 최적화 참고사항

Go: 간단한 최적화 참고사항

Go语言进阶学习
Go语言进阶学习앞으로
2023-07-21 13:04:42992검색
클라우드 컴퓨팅 시대에 우리는 서버리스 애플리케이션(개발자가 서버를 관리하지 않고도 애플리케이션을 구축하고 실행할 수 있는 클라우드 네이티브 개발 모델)을 만드는 경우가 많습니다. 우리 프로젝트가 이 모델을 채택하면 인프라 유지 관리 예산이 목록의 최상위에 올 것입니다. 우리 서비스의 부하가 낮다면 사실상 무료입니다. 하지만 뭔가 잘못되면 그에 대한 대가를 많이 치르게 될 것입니다! 돈에 관해서라면, 당신은 어떤 식으로든 그것에 반응할 수밖에 없습니다.

VPS가 여러 서비스 애플리케이션을 실행하고 있지만 그 중 하나가 때때로 모든 리소스를 차지하여 SSH를 통해 서버에 액세스할 수 없는 경우. Kubernetes 클러스터 사용으로 전환하고 모든 애플리케이션에 대한 제한을 설정합니다. 그런 다음 OOM-killer가 메모리 "누수" 문제를 해결하면서 일부 응용 프로그램이 다시 시작되는 것을 확인했습니다.

물론 OOM이 항상 누수 문제인 것은 아니며 리소스 오버런이 발생할 수도 있습니다. 누출 문제는 프로그램 오류로 인해 발생할 가능성이 높습니다. 오늘 우리가 이야기할 주제는 이러한 상황을 최대한 피하는 방법입니다.

과도한 자원 소비는 지갑에 해를 끼치므로 즉각적인 조치가 필요합니다.

너무 일찍 최적화하지 마세요

이제 최적화에 대해 이야기해 보겠습니다. 왜 우리가 조기에 최적화하면 안 되는지 이해하셨기를 바랍니다!

  • 첫째, 최적화는 쓸모없는 작업일 수 있습니다. 전체 애플리케이션을 먼저 연구해야 하기 때문에 코드에 병목 현상이 발생하지 않을 가능성이 높습니다. 우리에게 필요한 것은 빠른 결과, MVP(Minimum Viable Product, 최소 실행 가능 제품)이며 이에 대한 문제점을 고려하겠습니다.
  • 둘째, 최적화에는 근거가 있어야 합니다. 즉, 모든 최적화는 벤치마크를 기반으로 해야 하며, 이를 통해 얼마나 많은 이익을 얻을 수 있는지 입증해야 합니다.
  • 셋째, 최적화는 복잡성을 가져올 수 있습니다. 당신이 알아야 할 것은 대부분의 최적화로 인해 코드의 가독성이 떨어진다는 것입니다. 이 균형을 유지해야 합니다.

최적화 제안

이제 Go의 표준 엔터티 분류에 따라 몇 가지 실용적인 제안을 제공합니다.

1. 배열 및 슬라이스

슬라이스에 대한 메모리를 미리 할당합니다.

세 번째 매개변수를 사용해 보세요: <code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: #000000;background: rgba(14, 210, 247, 0.15);"><span style="font-size: 15px;">make([]T, 0, len)</span>make([]T, 0, len)

정확한 요소 수를 알 수 없는 경우 조각의 수명이 짧습니다. 예, 런타임 중에 조각이 커지지 않도록 더 큰 크기를 할당할 수 있습니다.

복사 사용을 잊지 마세요

두 개 이상의 슬라이스를 병합할 때와 같이 복사할 때는 추가를 사용하지 마세요.

올바른 반복

많은 요소 또는 큰 요소가 포함된 슬라이스. 단일 요소를 가져오는 데 사용됩니다. 이렇게 하면 불필요한 중복을 피할 수 있습니다.

Multiplexing Slices

들어오는 슬라이스에서 일부 작업을 수행하고 수정된 결과를 반환하면 이를 반환할 수 있습니다. 이렇게 하면 새로운 메모리 할당이 방지됩니다.

🎜
슬라이스의 사용하지 않는 부분을 남겨두지 마세요

슬라이스에서 작은 조각을 잘라서 사용해야 하는 경우 슬라이스의 주요 부분도 그대로 유지됩니다. 올바른 접근 방식은 이 작은 조각의 새 복사본을 사용하고 이전 조각을 GC에 던지는 것입니다.

2. 올바른 문자열 접합

하나의 명령문으로 문자열 접합이 가능하다면 <code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: #000000;background: rgba(14, 210, 247, 0.15);"><span style="font-size: 15px;">+</span> 操作符。如果需要在循环中执行此操作,使用 <span style="font-size: 15px;">string.Builder</span>,并使用它的 <span style="font-size: 15px;">Grow</span> 方法预先指定 <span style="font-size: 15px;">Builder</span> 的大小,减少内存分配次数。

转换优化

string 和 []byte 在底层结构上非常相近,有时这两种类型之间可以通过强转换来避免内存分配。

字符串驻留

可以池化字符串,从而帮助编译器只存储一次相同的字符串。

避免分配

我们可以使用 map(级联)而不是复合键,我们可以使用字节切片。尽量不使用 <span style="font-size: 15px;">fmt</span>+ 연산자. 루프에서 이 작업을 수행해야 하는 경우

🎜string.Builder🎜🎜 및 🎜🎜Grow🎜🎜 방법이 미리 지정되어 있습니다🎜🎜Builder🎜🎜 크기를 줄여 메모리 할당 수를 줄입니다. 🎜🎜🎜변환 최적화🎜🎜🎜string과 []byte는 기본 구조에서 매우 유사하며 때로는 메모리 할당을 피하기 위해 이 두 유형 간에 강력한 변환을 사용할 수 있습니다. 🎜🎜🎜String resident🎜🎜🎜는 문자열을 풀링할 수 있으므로 컴파일러가 동일한 문자열을 한 번만 저장할 수 있습니다. 🎜🎜🎜할당 방지🎜🎜🎜복합 키 대신 맵(캐스케이드)을 사용할 수 있으며 바이트 슬라이스를 사용할 수 있습니다. 🎜🎜 fmt 🎜🎜 패키지. 모든 메소드가 리플렉션을 사용하기 때문입니다. 🎜🎜

3. 구조

큰 구조 복사를 피하세요

작은 구조는 4개 필드 이하, 기계어 크기는 1개 이하라는 것을 알고 있습니다.

몇 가지 일반적인 복사 시나리오

  • 인터페이스에 투영
  • 채널 수신 및 전송
  • 지도의 요소 교체
  • 슬라이스에 요소 추가
  • 반복(범위)
포인터를 통해 구조체 필드에 액세스하지 마세요.

역참조는 비용이 많이 들기 때문에 특히 루프에서 가능한 한 적게 수행해야 합니다. 또한 빠른 레지스터를 사용하는 기능도 상실됩니다.

작은 구조물 다루기

이 작품은 편집자에 의해 최적화되었기 때문에 가격이 저렴합니다.

정렬을 사용하여 구조 크기 줄이기

구조를 정렬(필드 크기에 따라 올바른 순서로 배열)하여 구조 자체의 크기를 줄일 수 있습니다.

4. 함수

인라인 함수를 사용하거나 직접 인라인하세요.

컴파일러에서 인라인할 수 있는 작은 함수를 작성해 보세요. 함수에 직접 코드를 삽입하는 것보다 훨씬 더 빠릅니다. 이는 특히 핫 경로의 경우에 해당됩니다.

인라인이 아닌 것은 무엇인가요

  • recovery
  • 블록 선택
  • 유형 선언
  • defer
  • goroutine
  • for-range
합리적 함수 매개변수를 현명하게 선택하세요.

복사가 최적화되므로 작은 매개변수를 사용해 보세요. GC 로드에서 복제와 스택 증가의 균형을 유지하십시오. 많은 수의 매개변수를 피하고 프로그램이 빠른 레지스터를 사용하도록 하십시오(그 수는 제한되어 있습니다).

이름이 지정된 반환 값

이것은 함수 본문에서 이러한 변수를 선언하는 것보다 더 효율적인 것 같습니다.

중간 결과 저장

컴파일러가 코드를 최적화하도록 돕고 중간 결과를 저장하면 코드를 최적화할 수 있는 더 많은 옵션이 제공됩니다.

defer를 신중하게 사용하세요

defer를 사용하지 않거나 최소한 루프에서 사용하지 마세요.

핫 경로 도움말

핫 경로, 특히 수명이 짧은 객체에 메모리를 할당하지 마세요. 가장 일반적인 브랜치를 만듭니다(if, switch)

5. Map

메모리를 미리 할당하세요

slice과 동일하게 맵을 초기화할 때 크기를 지정합니다.

빈 구조체를 값으로 사용 ​​

struct{}는 아무것도 아니므로(메모리를 차지하지 않음) 예를 들어 신호를 전달할 때 사용하는 것이 매우 유용합니다.

Clear map

지도는 늘어날 수만 있고 줄어들 수는 없습니다. 지도를 재설정해야 할 때 모든 요소를 ​​삭제하는 것은 도움이 되지 않습니다.

키와 값에 포인터를 사용하지 마세요. ​​

맵에 포인터가 포함되어 있지 않으면 GC가 귀중한 시간을 낭비하지 않습니다. 문자열도 포인터를 사용하므로 문자열 대신 바이트 배열을 키로 사용해야 합니다.

수정 횟수 줄이기

다시 말하지만 포인터를 사용하고 싶지는 않지만 맵과 슬라이스의 조합을 사용하여 맵에 키를 저장하고 슬라이스에 값을 저장할 수 있습니다. 이렇게 하면 제한 없이 값을 변경할 수 있습니다.

6. 인터페이스

메모리 할당 계산

인터페이스에 값을 할당하려면 먼저 값을 어딘가에 복사한 다음 포인터를 붙여넣어야 한다는 점을 기억하세요. 핵심은 복사하는 것입니다. 인터페이스를 박싱하고 언박싱하는 비용은 구조체 크기 할당과 거의 동일하다는 것이 밝혀졌습니다.

최적 유형 선택

경우에 따라 인터페이스 박싱 및 언박싱 중에 할당이 없는 경우도 있습니다. 예를 들어 변수 및 상수에 대한 작은 또는 부울 값, 하나의 간단한 필드가 있는 구조, 포인터(맵, 채널, func 포함)

메모리 할당 방지

다른 곳과 마찬가지로 불필요한 할당을 피하십시오. 예를 들어 두 번 박싱하는 대신 하나의 인터페이스를 다른 인터페이스에 할당하는 것입니다.

필요할 때만 사용하세요.

자주 호출되는 함수 매개변수 및 반환 결과에 인터페이스 사용을 피하세요. 추가 압축 풀기 작업이 필요하지 않습니다. 인라인을 방지하므로 인터페이스 메서드 호출 사용 빈도를 줄입니다.

7. 포인터, 채널, 경계 검사

불필요한 역참조 방지

특히 루프에서는 비용이 너무 많이 들기 때문에 더욱 그렇습니다. 역참조는 우리가 비용을 들여 수행하고 싶지 않은 작업입니다.

채널을 사용하는 것은 비효율적입니다.

채널 동기화는 다른 동기화 기본 방법보다 느립니다. 또한 선택 사례가 많을수록 프로그램 속도가 느려집니다. 그러나 선택, 케이스 및 기본값이 최적화되었습니다.

불필요한 경계 검사를 피하세요

이것도 비용이 많이 들기 때문에 피해야 합니다. 예를 들어, 최대 슬라이스 인덱스를 여러 번이 아닌 한 번만 확인(가져오기)하세요. 지금은 극단적인 선택을 시도해 보는 것이 가장 좋습니다.

요약

이 글 전체에서 우리는 동일한 최적화 규칙 중 일부를 보았습니다.

컴파일러가 올바른 결정을 내릴 수 있도록 도와주세요. 그러면 컴파일러는 감사할 것입니다. 컴파일 타임에 메모리를 할당하고, 중간 결과를 사용하고, 코드를 읽기 쉬운 상태로 유지하십시오.

암시적 최적화를 위해서는 벤치마크가 필수라는 점을 다시 한번 강조합니다. 컴파일러가 버전 간에 너무 빨리 변경되거나 그 반대의 경우도 있기 때문에 어제 작동했던 것이 내일 작동하지 않는 경우.

Go에 내장된 분석 및 추적 도구를 사용하는 것을 잊지 마세요.

위 내용은 Go: 간단한 최적화 참고사항의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 Go语言进阶学习에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제