지연 시간이 짧은 유닉스 / 리눅스


11

작업 사양을 기반으로 한 대부분의 낮은 대기 시간 / 고주파 프로그래밍 작업은 유닉스 플랫폼에서 구현 된 것으로 보입니다. 많은 사양에서 그들은 "낮은 대기 시간 리눅스"유형의 경험을 가진 사람들을 위해 특별히 요청합니다.

이것이 실시간 리눅스 OS를 의미하지 않는다고 가정하면 사람들이 이것이 무엇을 언급 할 수 있는지 도와 줄 수 있습니까? CPU 친화력을 스레드에 설정할 수 있다는 것을 알고 있지만 그보다 훨씬 더 많은 것을 요구한다고 가정합니다.

커널 튜닝? (어쨌든 solarflare와 같은 제조업체가 커널 바이 패스 네트워크 카드를 생산한다고 들었음)?

프로세스 간 DMA 또는 공유 메모리는 어떻습니까? 사람들이 저에게 간단한 아이디어를 줄 수 있다면 Google에서 연구를 수행 할 수 있습니다.

(이 질문에는 아마도 고주파 거래에 익숙한 사람이 필요할 것입니다)


2
커널 튜닝은 비 실시간 OS를 가능한 한 실시간으로 만드는 방법입니다. 스레드 고정도 필수입니다. 이 기사에서 자세한 내용을 읽을 수 있습니다 : coralblocks.com/index.php/2014/04/…
rdalmeida

답변:


26

IB 및 헤지 펀드 환경에서 HFT 그룹을 지원하기 위해 상당한 양의 작업을 수행했습니다. sysadmin보기에서 대답하려고하지만이 중 일부는 그러한 환경에서의 프로그래밍에도 적용됩니다.

"낮은 지연 시간"지원을 언급 할 때 고용주가 일반적으로 찾고있는 몇 가지 사항이 있습니다. 이 중 일부는 "원시 속도"질문 (구입할 10g 카드의 종류와 넣는 슬롯을 알고 있습니까?)이지만 고주파 거래 환경이 기존과 다른 방식에 관한 것입니다. 유닉스 환경. 몇 가지 예 :

  • 유닉스는 전통적으로 리소스에 대한 굶주림없이 많은 수의 프로세스 실행을 지원하도록 조정되었지만 HFT 환경에서는 컨텍스트 전환 등을 위해 최소한의 오버 헤드로 하나의 애플리케이션 을 실행하려고 할 수 있습니다. 전형적인 작은 예로, Intel CPU에서 하이퍼 스레딩을 켜면 더 많은 프로세스를 한 번에 실행할 수 있지만 각 개별 프로세스가 실행되는 속도에 상당한 성능 영향을 미칩니다. 프로그래머는 마찬가지로 스레딩 및 RPC와 같은 추상화 비용을 살펴보고 더 모 놀리 식 솔루션이 덜 깨끗하지만 오버 헤드를 피할 수있는 곳을 찾아야합니다.

  • TCP / IP는 일반적으로 연결 끊김을 방지하고 사용 가능한 대역폭을 효율적으로 사용하도록 조정됩니다. 보다 제한된 링크에서 가능한 최대 대역폭 을 얻는 대신 매우 빠른 링크에서 가능한 가장 낮은 대기 시간 을 얻는 것이 목표 라면 네트워크 스택의 조정을 조정하는 것이 좋습니다. 프로그래밍 측면에서 사용 가능한 소켓 옵션을 살펴보고 대기 시간을 줄이는 것보다 대역폭 및 안정성에 대해 더 튜닝 된 기본값을 찾는 것이 좋습니다.

  • 네트워킹과 마찬가지로 스토리지와 마찬가지로 응용 프로그램 문제로 인해 스토리지 성능 문제를 알리는 방법을 알고 프로그램 성능을 방해 할 가능성이 가장 적은 I / O 사용 패턴을 알아 봅니다. 예를 들어, 비동기 IO 사용의 복잡성으로 인해 비용이 많이 드는 부분과 단점은 무엇인지 알아보십시오.

  • 마지막으로, 더 고통스럽게도 : Unix 관리자 는 모니터링하는 환경의 상태에 대해 최대한 많은 정보를 원 하므로 SNMP 에이전트와 같은 도구, Nagios와 같은 활성 모니터링 도구 및 sar (1)과 같은 데이터 수집 도구를 실행하고 싶습니다. 컨텍스트 스위치를 최소화하고 디스크 및 네트워크 IO 사용을 엄격하게 제어해야하는 환경에서는 모니터링 비용과 모니터링되는 박스의 베어 메탈 성능간에 올바른 균형을 찾아야합니다. 마찬가지로, 코딩을 더 쉽게 만들지 만 성능 비용이 많이 드는 기술을 사용하고 있습니까?

마지막으로, 시간이 흘러도 다른 것들이 있습니다. 경험을 통해 배우는 속임수와 세부 사항. 그러나 이것들은 더 전문화되어 있습니다 (epoll을 사용할 때? 이론적으로 동일한 PCIe 컨트롤러를 가진 두 대의 HP 서버 모델이 다르게 작동하는 이유는 무엇입니까?) .


1
감사합니다. 프로그래밍 답변에 관심이 있었지만 매우 유용하고 유익했습니다.
user997112

5
@ user997112 이것은 프로그래밍 답변입니다. 만약 그렇게 보이지 않는다면, 계속 읽을 때까지 계속 읽으십시오 :)
Tim Post

15

@jimwise의 뛰어난 하드웨어 / 설정 튜닝 답변 외에도 "낮은 대기 시간 Linux"는 다음을 의미합니다.

  • 결정 성 (GC가 시작되는 동안 놀라운 지연이 없음), 저수준 시설 (I / O, 신호)에 대한 액세스, 언어 능력 (TMP 및 STL의 완전한 사용, 유형 안전)을 이유로 C ++.
  • 메모리 초과 속도 선호 :> 512Gb 이상의 RAM이 일반적입니다. 데이터베이스는 인 메모리, 캐시 된 선행 또는 이국적인 NoSQL 제품입니다.
  • 알고리즘 선택 : 가능한 빠른 객체 대 제곱 / 이해 가능한 / 확장 가능 (예 : 잠금없는 객체 비트 속성 대신 잠금없는 다중 비트 어레이)
  • 다른 코어의 프로세스 간 공유 메모리와 같은 OS 기능을 완벽하게 사용합니다.
  • 안전한. HFT 소프트웨어는 일반적으로 증권 거래소에 함께 위치하므로 맬웨어 가능성은 용납 할 수 없습니다.

이러한 기술 중 다수는 게임 개발과 겹치며, 이는 금융 소프트웨어 산업이 최근에 중복 된 게임 프로그래머를 흡수하는 이유 중 하나입니다 (최소한 임대료를 지불 할 때까지).

기본 요구 사항은 보안 (주식, 상품, FX) 가격과 같은 매우 높은 대역폭의 시장 데이터를 수신 한 다음 보안, 가격을 기반으로 매우 빠른 구매 / 판매 / 무 결정 결정을 내릴 수 있어야합니다. 그리고 현재 보유.

물론, 이것 역시 훌륭하게 잘못 될 수 있습니다.


비트 배열 포인트를 자세히 설명하겠습니다 . 긴 주문 목록 (5k IBM 구매, 10k DELL 판매 등)으로 운영되는 고주파 거래 시스템이 있다고 가정 해 봅시다. 다음 작업으로 넘어갈 수 있도록 모든 주문이 채워 졌는지 신속하게 결정해야한다고 가정 해 봅시다. 전통적인 OO 프로그래밍에서는 다음과 같습니다.

class Order {
  bool _isFilled;
  ...
public:
  inline bool isFilled() const { return _isFilled; }
};

std::vector<Order> orders;
bool needToFillMore = std::any_of(orders.begin(), orders.end(), 
  [](const Order & o) { return !o.isFilled(); } );

이 코드의 알고리즘 복잡도는 선형 스캔이므로 O (N)이됩니다. 메모리 액세스 측면에서 성능 프로파일을 살펴 보겠습니다. std :: any_of () 내부 루프의 각 반복은 인라인 된 o.isFilled ()를 호출하므로 _isFilled, 1 바이트의 메모리 액세스가됩니다. (또는 4는 아키텍처, 컴파일러 및 컴파일러 설정에 따라 다름) 총 128 바이트라고하는 객체입니다. 따라서 128 바이트마다 1 바이트에 액세스하고 있습니다. 최악의 경우를 가정하여 1 바이트를 읽으면 CPU 데이터 캐시 미스가 발생합니다. 이로 인해 RAM에 대한 읽기 요청이 발생하여 RAM에서 전체 라인을 읽습니다 ( 자세한 내용은 여기 참조 ). 따라서 메모리 액세스 프로파일은 N에 비례합니다.

이것을 다음과 비교하십시오.

const size_t ELEMS = MAX_ORDERS / sizeof (int);
unsigned int ordersFilled[ELEMS];

bool needToFillMore = std::any_of(ordersFilled, &ordersFilled[ELEMS+1],
   [](int packedFilledOrders) { return !(packedOrders == 0xFFFFFFFF); }

최악의 경우를 가정하면, 이것의 메모리 액세스 프로파일은 ELEMS를 RAM 라인의 너비로 나눈 값입니다 (다양-이중 채널 또는 3 중 채널 등일 수 있음).

실제로 메모리 액세스 패턴에 대한 알고리즘을 최적화하고 있습니다. 많은 양의 RAM이 도움이되지 않습니다. 이것이 필요한 CPU 데이터 캐시 크기입니다.

도움이 되나요?


YouTube의 저 지연 프로그래밍 (HFT 용)에 대한 훌륭한 CPPCon 강연이 있습니다 : https://www.youtube.com/watch?v=NH1Tta7purM


"bool 속성을 가진 객체의 배열 대신 다중 비트 배열"이것이 무엇을 의미합니까?
user997112

1
예제와 링크를 자세히 설명했습니다.
JBR 윌킨슨

전체 바이트를 사용하여 순서가 채워 졌는지 여부를 나타내지 않고 한 단계 더 나아가면 단일 비트를 사용할 수 있습니다. 따라서 단일 캐시 라인 (64 바이트)에서 256 차수의 상태를 나타낼 수 있습니다. 따라서 누락이 적습니다.
quixver

또한-메모리의 선형 스캔을 수행하는 경우 하드웨어 프리 페처는 데이터를로드하는 데 큰 도움이됩니다. 메모리에 순차적으로 접근하거나 보폭 또는 간단한 것을 제공했습니다. 그러나 비 순차적 방식으로 메모리에 액세스하는 경우 CPU 프리 페 처가 혼동됩니다. 예를 들어 이진 검색. 이 시점에서 프로그래머는 힌트-_mm_prefetch를 사용하여 CPU를 도울 수 있습니다.
quixver

-2

프로덕션에 하나 또는 두 개의 고주파수 소프트웨어를 넣지 않았기 때문에 가장 중요한 것을 말할 것입니다.

  1. 네트워킹 엔지니어와 함께 하드웨어 구성 및 시스템 관리자는 거래 시스템에서 처리 한 주문 수의 좋은 결과를 정의하지 않지만 위에서 설명한 기본 사항을 모르면 크게 다운 그레이드 할 수 있습니다.
  2. 실제로 고주파 거래를하기 위해 시스템을 만드는 유일한 사람은 C ++로 코드를 작성하는 컴퓨터 과학자입니다

    사용 된 지식 중에는

    A. 비교 및 ​​스왑 작업.

    • CAS가 프로세서에서 사용되는 방식과 컴퓨터가 소위 No-Locking 구조 처리에서 사용되도록 지원하는 방식 또는 잠금이없는 처리. 나는 여기에 전체 책을 쓰지 않을 것입니다. 간단히 말해 GNU 컴파일러와 Microsoft 컴파일러는 CAS 명령어의 직접적인 사용을 지원합니다. 큐에서 요소를 추출하거나 큐에 새 요소를 넣는 동안 코드에 "No.Wair"가있을 수 있습니다.
  3. 유능한 과학자가 더 많이 사용할 것입니다. 그는 최근 자바에서 처음 등장한 새로운 "패턴"을 찾아야한다. DISRUPTOR 패턴을 호출했습니다. 유럽의 LMAX 교환에서 폴드는 최신 프로세서의 스레드 기반 활용이 daya 대기열이 최신 CPU 캐시의 크기와 일치하지 않으면 CPU에 의한 메모리 캐시 릴리스에서 처리 시간이 느려질 것이라고 고주파 커뮤니티에 설명했습니다.

    그래서 그 읽기를 위해 멀티 스레딩 프로세스가 충돌 해결없이 하드웨어 CPU 캐시를 올바르게 사용할 수있는 Java 코드를 공개했습니다. 그리고 좋은 컴퓨터 과학자는 패턴이 이미 c ++로 포팅되었거나 자신을 포팅한다는 것을 알았습니다.

    이것은 관리자 구성을 능숙하게 능숙하게하는 방법입니다. 이것은 오늘날 고주파의 핵심입니다.

  4. 컴퓨터 과학 담당자는 QA 사람들을 돕기 위해 C ++ 코드를 많이 작성해야했습니다. 그러나 또한
    • 입증 된 달성 속도에 직면 한 거래자에게 검증
    • 다른 오래된 기술을 비난하고 자신의 코드로 노출시켜 좋은 결과를 얻지 못했음을 보여줍니다.
    • 다시 오래된 기술을 사용하는 대신 입증 된 pupe / 선택 커널 속도를 기반으로하는 자체 멀티 스레딩 통신 C ++ 코드를 작성하십시오. 나는 당신에게 예를 줄 것입니다-현대 tcp 라이브러리는 ICE입니다. 그리고 그것을 한 사람들은 밝습니다. 그러나 그들의 우선 순위는 많은 언어와의 호환성 분야에있었습니다. 그래서. C ++에서 더 잘할 수 있습니다. 따라서 ASYNCHRONOUS 선택 호출을 기반으로 최고의 성능을 발휘하십시오. HF가 아닌 여러 소비자에게 여러 생산자를 가지 마십시오.
      도착한 메시지의 커널 알림에만 파이프가 사용된다는 사실에 놀랄 것입니다. 거기에 64 비트 메시지 번호를 넣을 수 있지만 내용은 잠금이없는 CAS 대기열로 이동합니다. 비동기 커널 select()호출에 의해 트리거됩니다 .
    • 게다가. 메시지의 파이핑 / 큐잉을 수행하는 스레드에 C ++ 스레드 선호도를 지정하는 방법에 대해 학습합니다. 이 스레드는 핵심 선호도를 가져야합니다. 다른 사람이 같은 CPU 코어 번호를 사용해서는 안됩니다.
    • 등등.

보시다시피 고주파는 개발 분야입니다. C ++ 프로그래머 만이 성공할 수는 없습니다.

그리고 내가 성공한다고 말하면 당신과 헤지 펀드가 일할 ​​사람과 채용자가 말한 수를 넘어 매년 보상으로 투어 노력을 인정한다는 뜻입니다.

간단한 생성자 / 소멸자 FAQ의 시대는 영원히 사라졌습니다. 그리고 c ++ ... 새로운 컴파일러로 마이그레이션되어 메모리 관리를 덜어주고 클래스에 대한 깊이없는 상속을 강요합니다. 시간 낭비. 코드 재사용 패러다임이 변경되었습니다. 다형성으로 만든 클래스 수에 관한 것이 아닙니다. 재사용 할 수있는 코드의 성능을 곧바로 확인했습니다.

따라서 학습 곡선으로 들어가거나 선택하지 않는 것이 좋습니다. 정지 신호에 부딪치지 않습니다.


6
철자와 형식에 약간의 노력을 기울일 수 있습니다. 현재의 형태로,이 포스트는 거의 이해할 수 없습니다.
코드 InChaos

1
10 년 전의 상황을 설명했습니다. 하드웨어 기반 솔루션은 C ++의 최적화 수준에 관계없이 오늘날 순수 C ++보다 우수한 성능을 발휘합니다.
Sjoerd

하드웨어 기반 솔루션이 무엇인지 알고 싶은 사람들을 위해-대부분 코드가 실제로 고속 메모리에 레코딩되고 소위 ROM 메모리를 다시 태우지 않아도 변경되지 않는 FPGA 솔루션입니다. 읽기 전용
alex p

@alexp 당신은 당신이 무엇을 말하는지 명확하게 모른다. FPGA는 "고속 메모리에 레코딩 된 코드"와 다른 것입니다.
Sjoerd
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.