1 Milliard Linier

· 695 ord · Læsetid 4 minutter

Yup, du læste rigtigt! Det her er en historie om at læse og behandle store datamængder i et sprog som alle mener er dødt.

Jeg blev inspireret til at lave et eksperiment, efter at ha' læst en udfordring til Java udviklere.

Hvor hurtigt kan dit program læse og behandle en fil på 1.000.000.000 linier? - Altså du skal skrive et program der kan læse og beregne på ret store datamængder.

“Men, hvor meget kan 1.000.000.000 linier fylde?” Tænker du sikkert - well … ca. 17GB viser det sig - og det er ikke eneste overraskelse jeg støder på.

Filen er en csv fil, med bynavne og temperaturer ala det her:

Miami;0.12
Houston;12.2
Dallas;-12.98
Philadelphia;-1.2
Atlanta;59.21
Houston;-3.2
Washington;
Miami;12.12

Programmet skal læse filen og generere en linie pr. by med minimum, gennemsnit og maksimum temperatur udfra indholdet i filen. Der vil max være 10.000 forskellige byer.

Generering af testdata sæt 🔗

Jeg lavede et super simpelt script der kan generere testdata i sæt, så jeg kan se forskellen på hvad størrelser gør ved tid.

Genereringen tager som følger:

─> time php data-generator.php 100000 > data-100-000.csv
php data-generator.php 100000 > data-100-000.csv  0,16s user 0,08s system 98% cpu 0,238 total

─> time php data-generator.php 1000000 > data-1-000-000.csv
php data-generator.php 1000000 > data-1-000-000.csv  1,52s user 0,55s system 99% cpu 2,076 total

─> time php data-generator.php 10000000 > data-10-000-000.csv
php data-generator.php 10000000 > data-10-000-000.csv  16,33s user 5,98s system 99% cpu 22,358 total

─> time php data-generator.php 100000000 > data-100-000-000.csv
php data-generator.php 100000000 > data-100-000-000.csv  244,82s user 88,48s system 99% cpu 5:34,04 total

─> time php data-generator.php 1000000000 > data-1-000-000-000.csv
php data-generator.php 1000000000 > data-1-000-000-000.csv  2110,31s user 781,45s system 99% cpu 48:16,22 total

Som det kan ses af tallene, så tog det lige over 35 minutter at generere det største dataset - hviklet egentlig overraskede mig. Jeg tror måske det skyldes at php er singeltrådet, og derfor kun kan makse den ene af min laptops kerner ud. Måske jeg skal prøve at lave et multi-trådet eksempel, for at se om jeg kan få den til at skrive hurtigere…

Mht. størrelserne på filerne, så kom de ud sådan her:

-rw-rw-r--+ 1 un un  17G Jan  5 22:35 data-1-000-000-000.csv
-rw-rw-r--+ 1 un un 1,7G Jan  5 21:45 data-100-000-000.csv
-rw-rw-r--+ 1 un un 165M Jan  5 21:39 data-10-000-000.csv
-rw-rw-r--+ 1 un un  17M Jan  5 21:39 data-1-000-000.csv
-rw-rw-r--+ 1 un un 1,7M Jan  5 21:38 data-100-000.csv

Processering af filerne 🔗

Det script jeg lavede til at parse filerne, var igen ret simpelt - der skal egentlig ikke så meget til. Det spændende er om jeg nu efterfølgende kan finde en måde at gøre det hurtigere på - for her igen, er min test begrænset til at makse en kerne ud - så kan det ikke gå hurtigere.

─> time php data-parser.php 100-000 > result-100-000.csv
php data-parser.php 100-000  0,17s user 0,01s system 99% cpu 0,178 total

─> time php data-parser.php 1-000-000 > result-1-000-000.csv
php data-parser.php 1-000-000  1,58s user 0,01s system 99% cpu 1,589 total

─> time php data-parser.php 10-000-000 > result-10-000-000.csv
php data-parser.php 10-000-000  15,66s user 0,09s system 99% cpu 15,755 total

─> time php data-parser.php 100-000-000 > result-100-000-000.csv
php data-parser.php 100-000-000  168,54s user 0,67s system 99% cpu 2:49,24 total

─> time php data-parser.php 1-000-000-000 > result-1-000-000-000.csv
php data-parser.php 1-000-000-000  1529,97s user 3,15s system 99% cpu 25:33,34 total

Her var overraskelse 2 - det var hurtigere at læse og behandle data, end det var at generere dem - selvom processen i at generere data er meget simplere end at parse dem efterfølgende.

Jeg har planer om at udfordre mine kollegaer til at skrive parsere, og så teste dem på én af vores servere - og på den måde lave en lille konkurrence. Men det er så også grunden til at der er meget lidt kode i det her indlæg. Det er jo sjovere, hvis de selv skal tænke :)

Og dog - lidt kode skal der vel være, så

function randomFloat($min = -99_999, $max = 99_999): float
{
    return number_format(random($min, $max) / 1_000, 2);
}

I PHP 8.3 kommer der en ny Random generator som kan det pis, men før den - hvad er så den bedste måde at lave random floats på? Som ovenstående, eller …?