머스크는 트위터를 인수했지만 기술력에 만족하지 못했습니다. RPC가 1000개가 넘으니 홈페이지가 너무 느리다고 생각하세요. 머스크가 말한 이유가 맞는지 여부를 언급하지 않고도 인터넷에서 사용자에게 제공되는 완전한 서비스에는 수많은 마이크로서비스 호출이 있을 것이라는 점을 알 수 있습니다.
WeChat 독서 추천을 예로 들면, 회상과 정렬의 두 단계로 나뉩니다.
요청이 도착하면 먼저 사용자 기능 마이크로서비스에서 기능을 가져오고 기능 심사를 위해 기능을 결합한 다음 관련 마이크로서비스를 호출하고 회수해야 합니다. 다중 채널 리콜이 있기 때문에 동시에 실행되는 유사한 리콜 프로세스가 많이 있을 것입니다. 다음은 여러 기능 마이크로서비스에서 관련 기능을 가져오고 이를 결합한 후 정렬 모델 서비스를 여러 번 호출하는 정렬 단계입니다. 최종 결과를 얻은 후 한편으로는 최종 결과가 호출자에게 반환되고, 다른 한편으로는 프로세스의 일부 로그가 보관을 위해 로그 시스템으로 전송됩니다.
읽기 추천은 전체 WeChat 독서 앱의 아주 작은 부분일 뿐입니다. 상대적으로 작은 서비스라도 그 뒤에는 수많은 마이크로서비스 호출이 있다는 것을 알 수 있습니다. 자세히 살펴보면 WeChat Reading 시스템 전체에 엄청난 수의 마이크로서비스 호출이 있을 것으로 예상할 수 있습니다.
많은 수의 마이크로서비스는 어떤 문제를 가져오나요?
일상 업무 요약에 따르면 주로 세 가지 과제가 있습니다.
① 관리: 주로 대규모 환경을 효율적으로 관리, 개발 및 배포하는 방법에 관한 것입니다. 알고리즘 마이크로서비스의 수.
② 성능: 마이크로서비스, 특히 알고리즘 마이크로서비스의 성능을 향상시키세요.
3 스케줄링: 여러 유사한 알고리즘 마이크로서비스 간에 효율적이고 합리적인 로드 밸런싱을 달성하는 방법.
첫 번째 요점은 우리가 일부 자동 패키징 및 배포를 제공한다는 것입니다. 파이프라인은 알고리즘 학생들이 알고리즘 마이크로서비스를 개발해야 하는 부담을 줄여줍니다. 이제 알고리즘 학생들은 Python 함수만 작성하면 되며 파이프라인은 자동으로 미리 작성된 일련의 마이크로서비스 템플릿을 가져와 알고리즘 학생들이 개발한 기능을 빠르게 작성합니다. 마이크로서비스.
두 번째 요점은 마이크로서비스의 자동 확장 및 축소에 관한 작업 백로그 인식 솔루션을 채택합니다. 특정 유형의 작업의 백로그 또는 유휴 정도를 적극적으로 감지합니다. 백로그가 특정 임계값을 초과하면 유휴 상태가 특정 임계값에 도달하면 마이크로서비스 프로세스 수가 감소합니다. 발동되기도 합니다.
세 번째 요점은 많은 수의 마이크로서비스를 함께 구성하여 완전한 상위 계층 서비스를 구축하는 방법입니다. 상위 계층 서비스는 DAG로 표시됩니다. DAG의 각 노드는 마이크로서비스에 대한 호출을 나타내고, 각 에지는 서비스 간 데이터 전송을 나타냅니다. DAG의 경우 DSL(도메인 특정 언어)도 DAG를 더 잘 설명하고 구성하기 위해 특별히 개발되었습니다. 그리고 우리는 브라우저에서 직접 상위 계층 서비스를 시각적으로 구축, 스트레스 테스트 및 배포할 수 있는 DSL을 중심으로 일련의 웹 기반 도구를 개발했습니다.
네 번째 성능 모니터링은 상위 서비스에 문제가 있을 때 문제를 찾아내는 것입니다. 자체 추적 시스템을 구축했습니다. 각 외부 요청에는 각 마이크로서비스의 요청 시간 소비를 확인할 수 있는 완전한 추적 세트가 있어 시스템의 성능 병목 현상을 발견할 수 있습니다.
일반적으로 알고리즘의 성능 시간은 딥러닝 모델에 소요됩니다. 알고리즘 마이크로서비스의 성능을 최적화하는 데 중점을 두는 부분이 큽니다. 깊이를 최적화합니다. 학습 모델이 성능을 추론합니다. 전용 추론 프레임워크를 선택하거나 딥 러닝 컴파일러, 커널 최적화 등을 시도해 볼 수 있습니다. 이러한 솔루션의 경우 이러한 솔루션이 완전히 필요한 것은 아니라고 생각합니다. 많은 경우 Python 스크립트를 직접 사용하여 온라인에 접속하지만 여전히 C++에 필적하는 성능을 달성할 수 있습니다.
완전히 필요하지 않은 이유는 이러한 솔루션이 실제로 더 나은 성능을 가져올 수 있지만 좋은 성능이 서비스의 유일한 요구 사항은 아니기 때문입니다. 사람과 자원 측면에서 잘 알려진 80/20 규칙이 있습니다. 즉, 20%의 사람이 80%의 자원을 생성합니다. 즉, 20%의 사람이 80%의 기여를 제공합니다. . 이는 마이크로서비스에도 적용됩니다.
마이크로서비스는 두 가지 범주로 나눌 수 있습니다. 첫째, 성숙하고 안정적인 서비스는 그 수가 많지 않고 20%만 차지할 수 있지만 트래픽의 80%를 차지합니다. 다른 유형은 아직 개발 및 반복 중인 일부 실험적인 서비스입니다. 80%를 차지하는 많은 서비스가 있지만 트래픽의 20%만 차지한다는 것이 중요한 점입니다. 따라서 빠른 개발과 출시에 대한 요구도 강할 것입니다.
앞서 언급한 Infer Framework, Kernel Optimization 등의 방법들은 필연적으로 추가 개발 비용이 필요합니다. 성숙하고 안정적인 서비스는 변경 사항이 상대적으로 적고 한 번의 최적화 후에도 오랫동안 사용할 수 있기 때문에 이러한 유형의 방법에 여전히 매우 적합합니다. 반면, 이러한 서비스는 트래픽이 많이 발생하기 때문에 작은 성능 개선만으로도 큰 영향을 미칠 수 있으므로 비용을 투자할 가치가 있습니다.
그러나 이러한 방법은 실험적인 서비스에는 그다지 적합하지 않습니다. 왜냐하면 실험적인 서비스는 자주 업데이트되고 모든 새로운 모델에 대해 새로운 최적화를 수행할 수는 없기 때문입니다. 실험적인 서비스를 위해 GPU 하이브리드 배포 시나리오를 위해 자체 개발한 Python 인터프리터인 PyInter를 개발했습니다. 코드를 수정하지 않고 Python 스크립트를 사용하여 직접 온라인에 접속할 수 있으며 동시에 성능은 C++에 가깝거나 심지어 이를 초과할 수 있습니다.
위 그림의 가로축은 동시 프로세스 수로 우리가 배포하는 모델 복사본 수를 나타냅니다. PyInter는 모델 복사본 수가 더 많습니다. 많은 경우 QPS는 onnxruntime을 능가합니다.
위 그림을 보면 모델 복사본 수가 많을 때 PyInter가 멀티 프로세스와 ONNXRuntime에 비해 메모리 사용량을 거의 80% 정도 줄이는 것을 확인할 수 있습니다. 모델에 관계없이 복사본 수에 관계없이 PyInter의 메모리 사용량은 변경되지 않습니다.
좀 더 기본적인 질문으로 돌아가 보겠습니다. Python이 정말 느린가요?
네, 파이썬은 정말 느리지만, 과학적인 계산을 할 때는 파이썬이 느리지 않습니다. 왜냐하면 실제 계산 장소는 파이썬이 아니라 MKL이나 cuBLAS 같은 전용 계산 라이브러리이기 때문입니다.
그렇다면 Python의 주요 성능 병목 현상은 어디에 있습니까? 주로 멀티스레딩의 GIL(Global Interpreter Lock)로 인해 멀티스레딩에서 동시에 하나의 스레드만 작동하게 됩니다. 이러한 형태의 멀티스레딩은 IO 집약적인 작업에 도움이 될 수 있지만 계산 집약적인 모델 배포에는 적합하지 않습니다.
멀티 프로세스로 전환하면 문제가 해결될 수 있나요?
사실 아닙니다. 다중 프로세스는 실제로 GIL 문제를 해결할 수 있지만 다른 새로운 문제도 가져올 것입니다. 우선, 여러 프로세스 간에 CUDA Context/모델을 공유하는 것이 어렵기 때문에 비디오 메모리가 많이 낭비됩니다. 이 경우 하나의 그래픽 카드에 여러 모델을 배포할 수 없습니다. 두 번째는 GPU의 문제입니다. GPU는 동시에 한 프로세스의 작업만 수행할 수 있으며, 여러 프로세스 간에 GPU를 자주 전환하는 것도 시간을 소모합니다.
Python 시나리오의 경우 이상적인 모델은 아래와 같습니다.
멀티 스레드 배포와 GIL의 영향 제거를 통해 이것이 PyInter의 주요 목적입니다. 디자인 아이디어는 실행을 위해 모델의 여러 복사본을 여러 스레드에 배치하는 동시에 각 Python 작업에 대해 별도의 격리된 Python 인터프리터를 생성하여 여러 작업의 GIL이 서로 간섭하지 않도록 하는 것입니다. 이는 다중 프로세스와 다중 스레드의 장점을 결합한 반면, GIL은 본질적으로 단일 프로세스 다중 스레드 모드이므로 비디오 메모리 개체를 공유할 수 있습니다. GPU 프로세스 전환 오버헤드가 없습니다.
PyInter 구현의 핵심은 프로세스 내에서 동적 라이브러리를 격리하는 것입니다. 여기서는 자체 개발한 동적 라이브러리 로더를 개발했습니다. dlopen은 "격리" 및 "공유" 두 가지 동적 라이브러리 로딩 방법을 지원합니다.
"격리" 모드에서 동적 라이브러리를 로드하면 동적 라이브러리가 서로 다른 가상 공간에 로드되며, 서로 다른 가상 공간은 서로 볼 수 없습니다. 동적 라이브러리가 "공유" 모드로 로드되면 각 가상 공간 내부를 포함하여 프로세스의 어느 곳에서나 동적 라이브러리를 보고 사용할 수 있습니다.
Python 인터프리터 관련 라이브러리를 "격리" 모드로 로드한 다음 cuda 관련 라이브러리를 "공유" 모드로 로드하여 인터프리터를 격리하는 동안 비디오 메모리 리소스 공유를 실현합니다.
여러 마이크로서비스는 동일한 중요성과 역할을 수행하므로 여러 마이크로서비스 간에 동적 로드 밸런싱을 달성하는 방법입니다. 동적 로드 밸런싱은 중요하지만 완벽하게 수행하는 것은 거의 불가능합니다.
동적 로드 밸런싱이 왜 중요한가요? 그 이유는 다음과 같습니다:
(1) 기계 하드웨어 차이(CPU/GPU);
(2) 요청 길이 차이(번역 2단어 / 번역 200단어); (3) 무작위 로드 밸런싱에서는 롱테일 효과가 분명합니다.
① P99/P50의 차이는 10배에 달할 수 있습니다. ② P999/P50의 차이는 20배에 달할 수 있습니다. (4) 마이크로서비스의 경우 롱테일은 전체 속도를 결정하는 핵심입니다. 요청을 처리하는 데 걸리는 시간은 컴퓨팅 성능, 요청 길이 등의 차이에 따라 크게 달라집니다. 마이크로서비스 수가 증가함에 따라 항상 롱테일에 도달하는 일부 마이크로서비스가 있게 되며 이는 전체 시스템의 응답 시간에 영향을 미칩니다. 동적 로드 밸런싱을 완벽하게 구현하기 어려운 이유는 무엇인가요? 옵션 1: 모든 머신에서 벤치마크를 실행합니다. 이 솔루션은 "동적"이 아니며 요청 길이의 차이에 대처할 수 없습니다. 그리고 성능을 반영할 수 있는 완벽한 벤치마크는 없습니다. 기계마다 모델마다 다르게 반응합니다. 옵션 2: 각 기계의 상태를 실시간으로 확인하고 부하가 가장 적은 기계에 작업을 보냅니다. 이 솔루션은 비교적 직관적이지만 문제는 분산 시스템에 실제 "실시간"이 없다는 것입니다. 한 시스템에서 다른 시스템으로 정보를 전송하는 데 확실히 시간이 걸립니다. 기계 상태가 변경될 수 있습니다. 예를 들어, 특정 순간에 특정 작업자 시스템이 가장 유휴 상태이고 작업 분배를 담당하는 여러 마스터 시스템이 모두 이를 감지하여 모두 가장 유휴 상태인 이 작업자에게 작업을 할당하고 가장 유휴 상태인 이 작업자는 즉시 다음과 같이 됩니다. 로드 밸런싱의 유명한 조석 효과. 옵션 3: 전역적으로 고유한 작업 대기열을 유지합니다. 작업 분배를 담당하는 모든 마스터는 작업을 대기열로 보내고 모든 작업자는 대기열에서 작업을 가져옵니다. 이 솔루션에서는 작업 대기열 자체가 단일 지점 병목 현상이 되어 수평 확장이 어려울 수 있습니다. 동적 로드 밸런싱이 완벽하기 어려운 근본적인 이유는 정보 전송에 시간이 걸리기 때문입니다. 상태가 관찰되면 이 상태는 "통과"되어야 합니다. YouTube에 제가 모든 사람에게 추천하는 동영상이 있습니다. "로드 밸런싱은 불가능합니다" https://www.youtube.com/watch?v=kpvbOzHUakA. 동적 로드 밸런싱 알고리즘과 관련하여 Power of 2 Choices 알고리즘은 두 명의 작업자를 무작위로 선택하고 더 유휴 상태인 작업자에게 작업을 할당합니다. 이 알고리즘은 현재 우리가 사용하는 동적 균등화 알고리즘의 기초입니다. 그러나 Power of 2 Choices 알고리즘에는 두 가지 주요 문제가 있습니다. 첫째, 각 작업이 할당되기 전에 작업자의 유휴 상태를 쿼리해야 하며, 추가로 RTT가 하나 더 추가됩니다. 선택된 직원은 매우 바쁩니다. 이러한 문제를 해결하기 위해 개선을 진행했습니다.
향상된 알고리즘 은 Joint-Idle-Queue입니다.
마스터 머신에 Idle-Queue와 Amnesia라는 두 부분을 추가했습니다. Idle-Queue는 현재 유휴 상태인 작업자를 기록하는 데 사용됩니다. Amnesia는 최근 어떤 Worker가 자신에게 하트비트 패킷을 보냈는지 기록합니다. Worker가 오랫동안 하트비트 패킷을 보내지 않으면 Amnesia는 이를 점차 잊어버리게 됩니다. 각 작업자는 유휴 상태인지 여부를 주기적으로 보고합니다. 유휴 작업자는 자신의 ID를 보고할 마스터를 선택하고 처리할 수 있는 수를 보고합니다. 또한 작업자는 마스터를 선택할 때 Power of 2 Choices 알고리즘을 사용합니다. 다른 마스터의 경우 작업자는 하트비트 패킷을 보고합니다.
새 작업이 도착하면 마스터는 유휴 대기열에서 무작위로 두 개를 선택하고 기록 대기 시간이 더 낮은 것을 선택합니다. 유휴 대기열이 비어 있으면 기억상실이 표시됩니다. Amnesia에서 무작위로 두 개를 선택하고 기록 대기 시간이 더 낮은 것을 선택합니다.
실제 효과 측면에서 이 알고리즘을 사용하면 P99/P50을 1.5배로 압축할 수 있으며 이는 Random 알고리즘보다 10배 더 좋습니다.
모델 서비스화를 실천하면서 세 가지 과제에 직면했습니다.
첫 번째는 수많은 마이크로서비스를 관리하는 방법, 개발을 최적화하는 방법, 그리고 우리의 솔루션은 온라인 및 배포 프로세스를 최대한 자동화하고, 반복적인 프로세스를 추출하여 자동화된 파이프라인 및 프로그램으로 전환합니다.
두 번째 측면은 모델 성능 최적화입니다. 딥 러닝 모델 마이크로서비스를 보다 효율적으로 실행하는 방법은 모델의 실제 요구 사항에서 시작하여 상대적으로 안정적이고 트래픽이 많은 서비스에 대한 맞춤형 최적화를 수행하는 것입니다. 실험적인 서비스는 PyInter를 사용하고 Python 스크립트를 직접 사용하여 C++의 성능을 달성할 수 있는 서비스를 시작합니다.
세 번째 문제는 작업 스케줄링입니다. 동적 로드 밸런싱을 달성하는 방법은 무엇입니까? 우리의 솔루션은 Power of 2 Choices를 기반으로 하며 JIQ 알고리즘을 개발하여 시간이 많이 걸리는 서비스의 롱테일 문제를 크게 완화합니다.
위 내용은 WeChat NLP 알고리즘 마이크로서비스 거버넌스의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!