2009-08-05 7 views
24

Ich habe einen Mini-HTTP-Server in C++ mit boost :: asio entwickelt, und jetzt lade ich ihn mit mehreren Clients, und ich konnte die Sättigung nicht annähernd erreichen die CPU. Ich teste gerade auf einer Amazon EC2-Instanz und bekomme ungefähr 50% einer CPU, 20% einer anderen, und die restlichen zwei sind im Leerlauf (laut htop).C++ Socket Server - CPU kann nicht gesättigt werden

Details:

  • Der Server Brände ein Thread pro Kern
  • Anforderungen empfangen werden, analysiert, verarbeitet und Antworten
  • die Anfragen schrieben werden, sind für die Daten, die aus dem gelesen wird, Speicher (schreibgeschützt für diesen Test)
  • Ich 'lade' den Server mit zwei Maschinen, die jeweils eine Java-Anwendung ausführen, 25 Threads laufen lassen, Anfragen senden
  • Ich sehe etwa 230 Anfragen/Sek thro ughput (dies ist Anwendung Anfragen, die aus vielen HTTP-Anfragen bestehen)

Also, was soll ich betrachte dieses Ergebnis zu verbessern? Da die CPU größtenteils leer ist, möchte ich diese zusätzliche Kapazität nutzen, um einen höheren Durchsatz zu erzielen, sagen wir 800 Anfragen pro Sekunde oder was auch immer.

Ideen die ich je hatte:

  • Die Anforderungen sind sehr klein und oft in wenigen ms erfüllt, konnte ich den Client ändern/compose größere Anforderungen zu senden (vielleicht mit batching)
  • I könnte den HTTP-Server modifizieren, um das Select-Designmuster zu verwenden, ist das hier angebracht?
  • Ich konnte einige Profilierung tun, um zu versuchen, zu verstehen, was der Engpass ist/sind
+0

Angenommen, Sie haben einen 1Gbps-Port auf dem Server? Was sind Ihre Anfrage- und Antwortgrößen (auf dem Draht)? – nik

+2

Wie hoch ist die Bandbreitennutzung am Server-Netzwerkanschluss (der von mir angenommene Wert 1Gbit/s)? – nik

+1

Der Test läuft auf EC2, was meiner Meinung nach Gigabit verwendet. Bmon berichtet über 3MiB (Megabits, glaube ich) TX-Rate und 2,5Mib RX-Rate. Viele Anfrage/Antwort-Größen sind klein (so wenig wie 100 Bytes), aber einige Antworten sind bis 1mb, Anfragen wahrscheinlich bis zu .25mb –

Antwort

40

boost :: asio ist nicht als fadenfreundlich wie man hoffen würde - dort um den epoll Code in boost eine große Sperre /asio/detail/epoll_reactor.hpp, was bedeutet, dass nur ein Thread gleichzeitig den epoll syscall des Kernels aufrufen kann. Und für sehr kleine Anfragen macht dies den Unterschied (das heißt, Sie werden nur ungefähr Singlethread-Leistung sehen).

Beachten Sie, dass dies eine Einschränkung darstellt, wie boost :: asio die Linux-Kernel-Funktionen verwendet, nicht unbedingt der Linux-Kernel selbst. Der epoll-Syscall unterstützt mehrere Threads, wenn flankengetriggerte Ereignisse verwendet werden, aber es kann ziemlich schwierig sein, es richtig zu machen (ohne übermäßiges Sperren).

BTW, ich habe einige Arbeit in diesem Bereich (kombiniert eine Full-Multithreaded Edge-Triggered Epoll Event-Schleife mit benutzerdefinierten Threads/Fasern) und machte einige Code unter der nginetd Projekt.

+0

Danke für die Info cmeerw, das ist interessantes Zeug. –

+1

(+1) cmeer Ich habe einen unbeantworteten Beitrag über die Leistung von boost :: asio im Allgemeinen auf Windows und Linux. Wenn Sie große Abschnitte von Asio gelesen haben, kommen Sie bitte und beantworten Sie meinen Beitrag: P –

+3

Ich war wirklich besorgt über diese globale Sperre. Es ist kein so großes Problem wie es scheint. Der Flaschenhals kann nur in Szenarien mit hohem Durchsatz auftreten. Wenn asio jedoch im epoll-Modus (linux) läuft, versucht es präventiv zu schreiben oder zu lesen, wenn der async_ * -Anruf ausgegeben wird. In einem High-Input-Szenario ist der Socket normalerweise bereit zum Lesen, wobei "async_read" epoll vollständig überspringen kann. Sie können nicht nach einer besseren Netzwerkleistung fragen. –

2

Von Ihren Kommentaren zur Netzwerknutzung,
Sie scheinen nicht viel Netzwerkbewegung zu haben.

3 + 2.5 MiB/sec ist rund um die 50Mbps Ball-Park (im Vergleich zu Ihrem 1 Gbps-Port).

Ich würde sagen, Sie eine der folgenden zwei Probleme haben,

  1. Unzureichende Arbeitsbelastung (niedrige Anforderungsrate von Ihren Kunden)
    • im Server-Blocking (gestört Antworterzeugungs
    • )

Mit Blick auf cmeerw ‚s Notizen und Ihre CPU-Auslastung Zahlen
(Leerlauf bei 50% + 20% + 0% + 0%)
Es scheint sehr wahrscheinlich eine Einschränkung in Ihrer Serverimplementierung.
I Sekunde cmeerw 's Antwort (+1).

+1

Er führt Tests am EC2 Cloud Computing Cluster von Amazon durch. Es ist schwer, die möglicherweise schlechte Leistung von EC2 auszuschließen. – unixman83

3

230 Anfragen/Sek. Scheint sehr niedrig für so einfache Async-Anfragen. Daher ist die Verwendung mehrerer Threads wahrscheinlich eine vorzeitige Optimierung - lassen Sie sie richtig funktionieren und stimmen Sie in einem einzigen Thread ab, um zu sehen, ob Sie sie noch brauchen. Wenn Sie nicht benötigte Sperren loswerden, können Sie die Dinge auf den neuesten Stand bringen.

This article hat einige Details und Diskussionen über I/O-Strategien für Web-Server-Stil-Performance circa 2003. Wer hat etwas Neues?

+0

Denken Sie daran, dass die 230 Anfragen/Sek. 'Anwendungsanforderungen' sind, die aus vielen tatsächlichen HTTP-Anfragen bestehen. –

+0

Es gibt nicht viel Lockerung, um loszuwerden, keine in meinem Code, aber wie cmeerw darauf hinweist boost :: asio macht einige interne Verriegelung. Der HTTP-Server funktioniert rein CPU-gebunden, so dass die zusätzlichen Kerne keine teure Verschwendung wären –

+2

Wenn das Ziel nur darin besteht, die CPU zu sättigen, mache die Arbeit in einem Thread und lasse die anderen drei PI oder sowas berechnen. Wenn mehrere Threads auf Benutzerebene vorhanden sind, wird es für das Betriebssystem und die E/A-Hardware nicht einfacher oder schneller, Netzwerkpakete zu lesen und zu schreiben. Threads und Cores sind für Rechenarbeit, wenn Sie keine tun, können sie Ihnen nichts bringen und riskieren Konflikte mit dem, was das System sonst noch macht. – soru

12

Da Sie EC2 verwenden, sind alle Wetten deaktiviert.

Versuchen Sie es mit echter Hardware, und dann können Sie sehen, was passiert. Der Versuch, Leistungstests in VMs durchzuführen, ist grundsätzlich unmöglich.

Ich habe noch nicht herausgefunden, wofür EC2 nützlich ist, wenn jemand es herausfindet, lass es mich wissen.

+0

Dieses System wird in EC2 eingesetzt, so dass das Testen der Leistung des Systems auf echter Hardware nicht hilfreich wäre, denke ich nicht. –

+6

Marks Punkt ist gültig: Verwenden Sie für das Profiling eine echte Maschine oder zumindest eine kontrolliertere Umgebung. Stellen Sie sicher, dass Sie in einem VM-Image arbeiten und dass Ihre CPU im Leerlauf liegt, weil ein anderer Mieter auf der Box für eine Weile die gesamte CPU hat. Und das erschwert das Profiling. – janm

+2

(+1) Hass schlecht informiert Stimmen –

0

ASIO eignet sich für kleine bis mittlere Aufgaben, aber es ist nicht sehr gut, die Leistung des zugrunde liegenden Systems zu nutzen. Weder sind rohe Socket-Aufrufe oder sogar IOCP unter Windows, aber wenn Sie erfahren sind, werden Sie immer besser als ASIO sein. So oder so, es gibt eine Menge Overhead mit all diesen Methoden, nur mehr mit ASIO.

Für was es wert ist. Die Verwendung von Raw-Socket-Aufrufen für mein benutzerdefiniertes HTTP kann 800K dynamische Anforderungen pro Sekunde mit einem I7 mit 4 Kernen bereitstellen. Es dient von RAM, wo Sie für diese Leistungsebene sein müssen. Bei diesem Leistungsniveau verbrauchen der Netzwerktreiber und das Betriebssystem ungefähr 40% der CPU. Mit ASIO kann ich 50 bis 100K Anfragen pro Sekunde bekommen, die Leistung ist sehr variabel und meist in meiner App gebunden. Der Beitrag von @cmeerw erklärt meistens warum.

Eine Möglichkeit, die Leistung zu verbessern, ist die Implementierung eines UDP-Proxys. Wenn Sie HTTP-Anfragen abfangen und diese dann über UDP an Ihren Backend-UDP-HTTP-Server weiterleiten, können Sie eine Menge TCP-Overhead in den Betriebssystemstapeln umgehen. Sie können auch Frontends haben, die sich auf UDP selbst durchschneiden, was Ihnen nicht zu schwer fallen sollte. Ein Vorteil eines HTTP-UDP-Proxys ist, dass Sie jedes gute Frontend ohne Modifikation verwenden können, und Sie können sie nach Belieben ohne Auswirkungen austauschen. Sie brauchen nur ein paar mehr Server, um es zu implementieren. Diese Änderung in meinem Beispiel hat die CPU-Auslastung des Betriebssystems auf 10% gesenkt, was meine Anfragen pro Sekunde auf etwas mehr als eine Million in diesem einzelnen Backend erhöht hat. Und FWIW Sie sollten immer ein Frontend-Backend-Setup für jede performante Site einrichten, da die Frontends Daten zwischenspeichern können, ohne das wichtigere dynamische Back-End zu verlangsamen.

Die Zukunft scheint zu sein, Ihren eigenen Treiber zu schreiben, der seinen eigenen Netzwerkstapel implementiert, damit Sie den Anforderungen so nah wie möglich kommen und dort Ihr eigenes Protokoll implementieren können.Das ist wahrscheinlich nicht das, was die meisten Programmierer hören wollen, da es komplizierter ist. In meinem Fall wäre ich in der Lage, 40% mehr CPU zu verwenden und zu über 1 Million dynamischen Anfragen pro Sekunde zu bewegen. Mit der UDP-Proxy-Methode können Sie die optimale Leistung erreichen, ohne dass Sie dies tun müssen. Allerdings werden Sie mehr Server benötigen. Wenn Sie jedoch viele Anfragen pro Sekunde ausführen, benötigen Sie normalerweise mehrere Netzwerkkarten und mehrere Frontends, um die benötigte Bandbreite zu bewältigen ein paar leichte UDP-Proxies in dort ist nicht so ein großer Deal.

Hoffe, dass einige davon für Sie nützlich sein können.

+1

Vorsicht, um ein Beispiel oder Arbeitsprojekt zu zeigen? Ohne es ist das genauso hilfreich wie irrelevantes Gespräch. Ich versuche nicht, Sie zu erniedrigen, aber hier ist ein konkreter Code erforderlich. –

0

Wie viele Instanzen von io_service haben Sie? Boost asio hat eine example, die einen io_service pro CPU erstellt und sie in der Art von RoundRobin verwendet.

Sie können weiterhin vier Threads erstellen und einen pro CPU zuweisen, aber jeder Thread kann einen eigenen io_service abfragen.