Jeg var forleden dag til et foredrag* om Disruptor framework’et. Selvom jeg ikke til daglig udvikler high-performance software med krav til meget lav latency – og et throughput på over 10 millioner transaktioner pr sekund, så synes jeg det var meget spændende at høre om, specielt fordi man alligevel godt kan genbruge nogle af principperne bag Disruptor i visse ”almindelige situationer”.
I en high-perfomance softwareapplikation er følgende interessant at undersøge:
- Hvordan får man udnyttet den underliggende hardware bedst muligt?
- Hvordan undgår vi at tråde i applikationen blokerer for hinanden eller venter for at en bestemt og tidskrævende operation er udført?
- Hvordan man kan løse et concurrency problem på en anderledes måde end man er vant til?
- Hvordan kan man undgå at Garbage Collectoren sløver applikationen for meget?
I Disruptor framework’et har man løst de problemer, der ofte opstår når fælles data skal tilgås af flere tråde. Løsningen er at sende data fra den ene tråd til den anden uden at bruge låse. I Disruptor er nogle af de afgørende elementer for at opnå utrolig høj performance nævnt i det følgende:
- Message passing mellem tråde (i stedet for at bruge låse)
- Ring buffer (i stedet for en kø)
- Ingen låse overhovedet
- Pre-allokering af objekter
- ”Mechanical Sympathy” (som jeg før har skrevet et blogindlæg om)
For at undgå concurrency problemer, som opstår pga. delt hukommelse mellem tråde, så er løsningen at man ikke har delt hukommelse, som mange tråde kan tilgå samtidig. I stedet bruges principperne bag message passing imellem tråde. I Disruptor foregår message passing mellem tråde utroligt hurtigt. Hvis man bruger en almindelig lås såsom en semafor, mutex osv, så har den brug for at kalde ned til OS’et (og dermed højst sandsynlig sletter alt hvad du har i din cache), hvilket medfører at dit program kører langsommere (end hvis der ingen låse var).
I en ring buffer har man en publisher, der sætter data ind i ring bufferen, og en consumer, der tager data ud af ringbufferen. Kort fortalt, så er ring bufferen mere egnet til high-performance programmering end en kø (såsom en LinkedList), idet consumeren kan tage mange elementer ad gangen (batches), hvis produceren har nået at indsætte flere elementer siden consumeren sidst har taget data fra bufferen. Ring bufferen har en maksimal størrelse, hvilket gør at man undgår at fylde hukommelsen op med data indtil programmet smider en fejl.
I ring bufferen, som Disruptor implementerer, genbruges alle objekter, dvs. der laves ikke et new() kald for alle objekter, som udgør ring bufferen, og dermed får Garbage Collectoren mindre at lave og sløver ikke din applikation. Dette kaldes pre-allokering af objekter.
Man kan sagtens lave high-performance applikationer i Java. Det er ikke rigtigt at man kun kan skrive super hurtige og reagerende applikationer i C eller C++. Disruptor er et godt eksempel herpå.
Disruptor framework’et er open source og findes både til Java og .Net
Disruptor er dog ikke en gylden løsning, der sørger for at ordne alle problemer med concurrency i et snuptag. Der er stadig en del ting, som man selv skal sørge for er i orden, når man bruger Disruptor. Men selve API’et for Disruptor er ret nemt og ligetil at gå i gang med at bruge.
Selvom man ikke arbejder med high-performance systemer med hårde krav til millioner transaktioner pr. sekund med meget lav latency, så er det alligevel interessant at se på de teknikker, der kan bruges til at opnå disse krav. Det er ikke ny teknologi som sådan, men gammel viden som nu bliver taget op igen og anvendt for at kunne udnytte det voksende antal CPU’er/kerner, der findes i moderne hardware. Det kunne jo være at man kunne bruge idéerne bag ring buffers, batches eller pre-allokering af objekter for at løse et programmeringsproblem på en anderledes og effektiv måde.
*) Foredraget var et GOTO Night arrangement holdt af Rasmus Andersen fra Comiit
Tak for en god opsummering! Blev ret nysgerrig og skulle lige til at ærgre mig over ikke at have deltaget, men fandt så ud af, at der afholdes et lignende event i København d. 10 sept.
Link til andre interesserede: https://secure.trifork.com/cph-2014/freeevent/index.jsp?eventOID=6343