Low Latency Unix / Linux


11

งานการเขียนโปรแกรม latency / ความถี่สูงต่ำส่วนใหญ่ (ขึ้นอยู่กับรายละเอียดของงาน) ดูเหมือนจะนำไปใช้กับแพลตฟอร์มยูนิกซ์ ในสเป็คจำนวนมากพวกเขาร้องขอเป็นพิเศษสำหรับผู้ที่มีประสบการณ์ประเภท "Linux latency ต่ำ"

สมมติว่าสิ่งนี้ไม่ได้หมายถึงระบบปฏิบัติการ linux แบบเรียลไทม์ผู้คนสามารถให้ความช่วยเหลือฉันเกี่ยวกับสิ่งนี้ได้หรือไม่ ฉันรู้ว่าคุณสามารถตั้งค่าความสัมพันธ์ของ CPU กับเธรดได้ แต่ฉันสมมติว่าพวกเขาขอมากไปกว่านั้น

การปรับแต่งเคอร์เนล? (แม้ว่าฉันจะได้ยินผู้ผลิตเช่น solarflare ผลิตการ์ดเครือข่ายผ่านเคอร์เนลอยู่แล้ว)?

สิ่งที่เกี่ยวกับ DMA หรือหน่วยความจำที่ใช้ร่วมกันระหว่างกระบวนการ หากผู้คนสามารถให้ความคิดสั้น ๆ กับฉันฉันสามารถไปและทำวิจัยบน google

(คำถามนี้อาจต้องมีคนคุ้นเคยกับการซื้อขายความถี่สูง)


2
การปรับแต่งเคอร์เนลเป็นวิธีการที่จะทำให้ระบบปฏิบัติการที่ไม่ใช่เรียลไทม์เป็นเวลาจริงที่สุด การปักหมุดด้ายยังมีผลบังคับใช้ คุณสามารถอ่านเพิ่มเติมเกี่ยวกับสิ่งนั้นได้ในบทความนี้: coralblocks.com/index.php/2014/04/…
rdalmeida

สิ่งที่เกี่ยวข้อง: stackoverflow.com/q/15702601/632951
Pacerier

คำตอบ:


26

ฉันทำงานที่สนับสนุนกลุ่ม HFT ในการตั้งค่า IB และ Hedge Fund แล้ว ฉันจะตอบจากมุมมองดูแลระบบ แต่บางส่วนนี้ใช้กับการเขียนโปรแกรมในสภาพแวดล้อมเช่นนี้

มีสองสิ่งที่นายจ้างมักจะมองหาเมื่อพวกเขาอ้างถึงการสนับสนุน "ความล่าช้าต่ำ" บางส่วนของคำถามเหล่านี้เป็นคำถาม "ความเร็วเร็ว" (คุณทราบหรือไม่ว่าจะซื้อการ์ดประเภท 10g และมีช่องเสียบอะไรบ้าง) แต่มีคำถามมากมายเกี่ยวกับวิธีการที่สภาพแวดล้อมการซื้อขายความถี่สูงแตกต่างจากแบบดั้งเดิม สภาพแวดล้อม Unix ตัวอย่างบางส่วน:

  • Unix นั้นได้รับการปรับแต่งแบบดั้งเดิมเพื่อรองรับการรันกระบวนการจำนวนมากโดยไม่ต้องอดอาหารใด ๆ สำหรับทรัพยากร แต่ในสภาพแวดล้อม HFT คุณมีแนวโน้มที่จะต้องการเรียกใช้แอปพลิเคชั่นเดียวด้วยค่าใช้จ่ายขั้นต่ำ เป็นตัวอย่างเล็ก ๆ ที่คลาสสิกการเปิดใช้งานไฮเปอร์เธรดบนซีพียู Intel ช่วยให้กระบวนการทำงานได้มากขึ้นในคราวเดียว แต่มีผลกระทบต่อประสิทธิภาพอย่างมากต่อความเร็วในการประมวลผลแต่ละกระบวนการ ในฐานะโปรแกรมเมอร์คุณจะต้องพิจารณาต้นทุนของนามธรรมเช่นเธรดและ RPC และหาวิธีแก้ปัญหาแบบเสาหินที่มากขึ้น - ในขณะที่ทำความสะอาดน้อยกว่า - จะหลีกเลี่ยงค่าใช้จ่าย

  • โดยทั่วไปแล้ว TCP / IP จะได้รับการปรับแต่งเพื่อป้องกันการเชื่อมต่อที่ลดลงและใช้แบนด์วิดท์ให้มีประสิทธิภาพ หากเป้าหมายของคุณคือทำให้เวลาในการตอบสนองต่ำที่สุดเป็นไปได้จากลิงก์ที่รวดเร็วมาก - แทนที่จะได้รับแบนด์วิดท์สูงสุดที่เป็นไปได้จากลิงก์ที่ จำกัด มากขึ้น - คุณจะต้องปรับการปรับแต่งสแต็กเครือข่าย จากด้านการเขียนโปรแกรมคุณจะต้องการดูตัวเลือกซ็อกเก็ตที่มีอยู่และหาว่าค่าใดที่มีค่าเริ่มต้นที่ปรับจูนแบนด์วิธและความน่าเชื่อถือมากกว่าการลดเวลาแฝง

  • เช่นเดียวกับระบบเครือข่ายเช่นเดียวกับระบบจัดเก็บข้อมูล - คุณต้องการทราบวิธีการแจ้งปัญหาประสิทธิภาพการจัดเก็บจากปัญหาแอปพลิเคชันและเรียนรู้ว่ารูปแบบการใช้งาน I / O แบบใดมีแนวโน้มที่จะรบกวนการทำงานของโปรแกรมน้อยที่สุด ตัวอย่างเรียนรู้ว่าความซับซ้อนของการใช้ asynchronous IO สามารถชำระให้คุณได้อย่างไรและข้อเสียคืออะไร)

  • ในที่สุดและเจ็บปวดมากขึ้น: เราผู้ดูแลระบบ Unix ต้องการข้อมูลมากที่สุดเกี่ยวกับสถานะของสภาพแวดล้อมที่เราตรวจสอบมากที่สุดดังนั้นเราจึงต้องการเรียกใช้เครื่องมือเช่นตัวแทน SNMP เครื่องมือตรวจสอบที่ใช้งานเช่น Nagios และเครื่องมือรวบรวมข้อมูลเช่น sar (1) ในสภาพแวดล้อมที่บริบทสวิตช์จำเป็นต้องลดลงอย่างที่สุดและใช้ดิสก์และเครือข่าย IO ควบคุมอย่างแน่นหนาแม้ว่าเราจะต้องพบการแลกเปลี่ยนที่ถูกต้องระหว่างค่าใช้จ่ายในการตรวจสอบและประสิทธิภาพของโลหะเปลือยของกล่องตรวจสอบ คุณใช้เทคนิคใดที่ทำให้การเขียนโปรแกรมง่ายขึ้น แต่คุณต้องเสียค่าใช้จ่าย

ในที่สุดก็มีสิ่งอื่น ๆ ที่เพิ่งมาพร้อมกับเวลา ลูกเล่นและรายละเอียดที่คุณเรียนรู้จากประสบการณ์ แต่สิ่งเหล่านี้มีความเฉพาะเจาะจงมากกว่า (เมื่อฉันใช้ epoll ทำไมเซิร์ฟเวอร์ HP ทั้งสองรุ่นที่มีคอนโทรลเลอร์ PCIe ที่เหมือนกันในทางทฤษฎีมีประสิทธิภาพแตกต่างกันอย่างไร) เชื่อมโยงกับร้านค้าเฉพาะของคุณมากขึ้นและมีแนวโน้มจะเปลี่ยนจากปีหนึ่งเป็นอีกปีหนึ่ง .


1
ขอบคุณแม้ว่าฉันมีความสนใจในคำตอบการเขียนโปรแกรมนี้มีประโยชน์มากและให้ข้อมูล
user997112

5
@ user997112 นี่คือคำตอบการเขียนโปรแกรม ถ้ามันไม่ได้ดูเหมือนเป็นเช่นนี้ให้อ่านมันจนมันไม่ :)
ทิมโพสต์

15

นอกจากคำตอบของการปรับแต่งฮาร์ดแวร์ / การตั้งค่าที่ยอดเยี่ยมจาก @jimwise "linux latency ต่ำ" กำลังแสดงถึง:

  • C ++ สำหรับเหตุผลของการกำหนด (ไม่ล่าช้าอย่างน่าประหลาดใจในขณะที่ GC เตะเข้า), เข้าถึงสิ่งอำนวยความสะดวกระดับต่ำ (I / O, สัญญาณ), พลังภาษา (ใช้ TMP และ STL อย่างเต็มที่, ความปลอดภัยประเภท)
  • ชอบความเร็วหน่วยความจำเกิน:> 512 Gb of 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 () ซึ่ง inlined ดังนั้นจึงกลายเป็นการเข้าถึงหน่วยความจำของ _isFilled, 1 ไบต์ (หรือ 4 ขึ้นอยู่กับสถาปัตยกรรมการตั้งค่าคอมไพเลอร์และคอมไพเลอร์ของคุณ) ในวัตถุที่รวม 128 ไบต์ ดังนั้นเราจึงเข้าถึง 1 ไบต์ในทุก ๆ 128 ไบต์ เมื่อเราอ่าน 1 ไบต์สันนิษฐานว่าเป็นกรณีที่เลวร้ายที่สุดเราจะได้รับแคชข้อมูลของ CPU นี่จะทำให้คำร้องขอการอ่านเป็น RAM ซึ่งอ่านทั้งบรรทัดจาก RAM ( ดูที่นี่สำหรับข้อมูลเพิ่มเติม ) เพื่ออ่าน 8 บิต ดังนั้นโปรไฟล์การเข้าถึงหน่วยความจำจึงเป็นสัดส่วนกับ 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 (แตกต่างกัน - อาจเป็นแบบดูอัลแชนแนลหรือทริปเปิลแชนเนล ฯลฯ )

ดังนั้นด้วยเหตุนี้เราจึงเพิ่มประสิทธิภาพอัลกอริธึมสำหรับรูปแบบการเข้าถึงหน่วยความจำ RAM จำนวนใดจะช่วยได้ - เป็นขนาดแคชข้อมูลของ CPU ที่ทำให้เกิดความต้องการนี้

สิ่งนี้ช่วยได้ไหม?


มี CPPC ที่ยอดเยี่ยมพูดคุยเกี่ยวกับการเขียนโปรแกรมความหน่วงต่ำ (สำหรับ HFT) บน YouTube: https://www.youtube.com/watch?v=NH1Tta7purM


"อาร์เรย์หลายบิตแทนอาร์เรย์ของวัตถุกับคุณสมบัติบูล" คุณหมายถึงอะไร
user997112

1
ฉันทำอย่างละเอียดกับตัวอย่างและลิงค์
JBRWilkinson

ก้าวไปอีกขั้น - แทนที่จะใช้ทั้งไบต์เพื่อระบุว่าคำสั่งซื้อนั้นเติมหรือไม่ - คุณสามารถใช้เพียงบิตเดียว ดังนั้นใน cacheline เดียว (64 ไบต์) - คุณสามารถแสดงสถานะของคำสั่ง 256 ดังนั้น - คิดถึงน้อยกว่า
ตลก

นอกจากนี้ - ถ้าคุณกำลังสแกนหน่วยความจำเชิงเส้น - prefetcher ฮาร์ดแวร์จะทำงานได้อย่างยอดเยี่ยมในการโหลดข้อมูลของคุณ ให้คุณเข้าถึงหน่วยความจำตามลำดับหรือก้าวหรือสิ่งที่ง่าย แต่ถ้าคุณเข้าถึงหน่วยความจำในลักษณะที่ไม่ต่อเนื่องใด ๆ - CPU prefetcher จะสับสน เช่นการค้นหาแบบไบนารี่ ณ จุดนั้นโปรแกรมเมอร์สามารถช่วย cpu ด้วยคำแนะนำ - _mm_prefetch
quixver

-2

เนื่องจากฉันไม่ได้ใส่ซอฟต์แวร์ความถี่สูงหนึ่งหรือสองรายการในการผลิตฉันจะบอกว่าสิ่งที่สำคัญที่สุด:

  1. การกำหนดค่าฮาร์ดแวร์และผู้ดูแลระบบพร้อมกับวิศวกรระบบเครือข่ายไม่ได้กำหนดผลลัพธ์ที่ดีของจำนวนคำสั่งที่ดำเนินการโดยระบบการซื้อขาย แต่พวกเขาสามารถปรับลดรุ่นได้ครั้งใหญ่หากพวกเขาไม่ทราบพื้นฐานที่ระบุไว้ข้างต้น
  2. บุคคลเดียวที่ทำให้ระบบทำการซื้อขายด้วยความถี่สูงคือนักวิทยาศาสตร์คอมพิวเตอร์ที่รวบรวมรหัสไว้ใน c ++

    ในบรรดาความรู้ที่ใช้คือ

    A. เปรียบเทียบและสลับการทำงาน

    • วิธีใช้ CAS ในตัวประมวลผลและวิธีที่คอมพิวเตอร์สนับสนุนให้ใช้ในการประมวลผลโครงสร้างแบบไม่ล็อค หรือการประมวลผลล็อคฟรี ฉันจะไม่เขียนหนังสือทั้งเล่มที่นี่ โดยย่อคอมไพเลอร์ GNU และคอมไพเลอร์ Microsoft สนับสนุนการใช้คำสั่ง CAS โดยตรง จะช่วยให้รหัสของคุณมี "No.Wair" ในขณะที่การแยกองค์ประกอบจากคิวหรือใส่ใหม่ในคิว
  3. นักวิทยาศาสตร์ที่มีความสามารถจะใช้มากขึ้น เขาควรพบใน "รูปแบบใหม่" ล่าสุดที่ปรากฏใน Java ก่อน รูปแบบ DISRUPTOR เรียกว่า พับในการแลกเปลี่ยน LMAX ในยุโรปอธิบายต่อชุมชนความถี่สูงว่าการใช้เธรดในโปรเซสเซอร์ที่ทันสมัยจะทำให้เวลาในการประมวลผลบนหน่วยความจำแคชปล่อยโดย CPU หากคิว daya ไม่สอดคล้องกับขนาดของ cpu แคชที่ทันสมัย ​​= 64

    ดังนั้นสำหรับการอ่านครั้งนั้นพวกเขาจึงเปิดเผยโค้ด java ที่อนุญาตให้กระบวนการมัลติเธรดใช้แคช CPU ของฮาร์ดแวร์ได้อย่างถูกต้องโดยไม่มีการแก้ไขข้อขัดแย้ง และนักวิทยาศาสตร์คอมพิวเตอร์ที่ดีต้องหารูปแบบนั้นได้ถูกส่งไปยัง c ++ แล้วหรือทำการ porting ตัวเอง

    นี่เป็นวิธีที่มีประสิทธิภาพนอกเหนือจากการกำหนดค่าผู้ดูแลระบบ นี่คือหัวใจที่แท้จริงของความถี่สูงในวันนี้

  4. นักวิทยาศาสตร์คอมพิวเตอร์ได้รับการเขียนรหัส C ++ มากมายไม่เพียง แต่ช่วยคน QA แต่ถึงยังไง
    • ตรวจสอบในผู้ซื้อขายเผชิญกับความเร็วที่พิสูจน์แล้ว
    • ประณามใช้เทคโนโลยีเก่าที่แตกต่างกันและเปิดเผยด้วยรหัสของตัวเองเพื่อแสดงว่าพวกเขาล้มเหลวในการสร้างผลลัพธ์ที่ดี
    • เขียนรหัส c ++ การสื่อสารแบบหลายเธรดของตัวเองโดยอ้างอิงจากเคอร์เนล / เลือกความเร็วเคอร์เนลที่ได้รับการพิสูจน์แล้วแทนที่จะใช้เทคโนโลยีเก่าอีกครั้ง ฉันจะยกตัวอย่าง - ห้องสมุด tcp ที่ทันสมัยคือ ICE และคนที่ทำมันก็สดใส แต่ลำดับความสำคัญของพวกเขาอยู่ในเขตของความเข้ากันได้กับหลายภาษา ดังนั้น. คุณสามารถทำได้ดีกว่าใน c ++ ดังนั้นค้นหา exaples ประสิทธิภาพสูงสุดตาม ASYNCHRONOUS select- call และอย่าไปเพื่อผู้บริโภคหลายรายผู้ผลิตหลายราย - ไม่ใช่เพื่อ HF
      และคุณจะประหลาดใจเมื่อพบว่ามีการใช้ไพพ์เพื่อการแจ้งเตือนเคอร์เนลของข้อความที่มาถึงเท่านั้น คุณสามารถใส่หมายเลขข้อความ 64- บิตได้ - แต่สำหรับเนื้อหาที่คุณเข้าสู่คิว CAS ที่ไม่มีการล็อค ถูกกระตุ้นโดยการselect()เรียกเคอร์เนลแบบอะซิงโครนัส
    • ยิ่งไปกว่านั้น เรียนรู้เกี่ยวกับการกำหนดด้วย c ++ thread affinity ให้กับเธรดของคุณที่ทำการไพพ์ / จัดคิวข้อความของคุณ เธรดนั้นควรมีความเกี่ยวข้องหลัก ไม่มีใครควรใช้หมายเลขแกน CPU เดียวกัน
    • และอื่น ๆ

อย่างที่คุณเห็น - ความถี่สูงเป็นเขตพัฒนา คุณไม่สามารถเป็นโปรแกรมเมอร์ C ++ เท่านั้นที่จะประสบความสำเร็จ

และเมื่อฉันบอกว่าประสบความสำเร็จฉันหมายถึงกองทุนเฮดจ์ฟันด์คุณจะทำงานให้ WILL รู้จักความพยายามของทัวร์ในการชดเชยรายปีเกินจำนวนผู้คนและนายหน้าพูดถึง

เวลาของคำถามที่พบบ่อยคอนสตรัคเตอร์ / destructor ง่ายหายไปตลอดกาลและ c ++ ... ตัวเองย้ายไปพร้อมกับคอมไพเลอร์ใหม่เพื่อบรรเทาคุณจากการจัดการหน่วยความจำและการบังคับใช้ความลึกขนาดใหญ่ในชั้นเรียน เสียเวลา. เปลี่ยนกระบวนทัศน์การนำโค้ดกลับมาใช้ใหม่ มันไม่ได้เกี่ยวกับจำนวนคลาสที่คุณสร้างใน polymorph มันเกี่ยวกับประสิทธิภาพของเวลาที่ได้รับการยืนยันโดยตรงของรหัสที่คุณสามารถนำมาใช้ซ้ำได้

ดังนั้นคุณเลือกที่จะเข้าสู่ช่วงการเรียนรู้ที่นั่นหรือไม่ มันจะไม่ตีสัญญาณหยุด


6
คุณอาจต้องใช้ความพยายามในการสะกดและจัดรูปแบบ ในรูปแบบปัจจุบันโพสต์นี้เข้าใจได้ยาก
CodesInChaos

1
คุณอธิบายสถานการณ์เมื่อ 10 ปีก่อน โซลูชันที่ใช้ฮาร์ดแวร์นั้นมีประสิทธิภาพเหนือกว่า C ++ อย่างแท้จริงทุกวันนี้ไม่ว่า C ++ ของคุณจะมีประสิทธิภาพสูงสุดเพียงใด
Sjoerd

สำหรับผู้ที่ต้องการรู้ว่าอะไรคือฮาร์ดแวร์ที่ใช้โซลูชั่น - ส่วนใหญ่เป็นโซลูชั่น FPGA ที่รหัสถูกเขียนลงในหน่วยความจำที่รวดเร็วและไม่เปลี่ยนโดยไม่ต้องรีบูตหน่วยความจำ ROM อ่านอย่างเดียว
alex p

@alexp คุณไม่รู้ว่าคุณกำลังพูดถึงอะไรอย่างชัดเจน FPGA เป็นสิ่งที่แตกต่างจาก "โค้ดที่ถูกเบิร์นไปยังหน่วยความจำที่รวดเร็ว"
Sjoerd
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.