3434 Min, Max: SmallInt; // 16-bit (-32767..+32768) temperatures * 10
3535 end ;
3636 PBrcStation = ^TBrcStation;
37- TBrcStations = array of TBrcStation;
37+ TBrcStationDynArray = array of TBrcStation;
38+
39+ TBrcStations = array [word] of TBrcStation;
40+ PBrcStations = ^TBrcStations;
3841
3942 TBrcList = record
4043 public
41- Station: TBrcStations;
4244 Count: integer;
4345 { $ifdef CUSTOMHASH}
46+ Station: PBrcStations; // perfectly aligned to 64 bytes from StationMem[]
4447 StationHash: array of word; // store 0 if void, or Station[] index + 1
48+ StationMem: TBrcStationDynArray;
4549 function Search (name : pointer; namelen: PtrInt): PBrcStation;
4650 { $else}
51+ Station: TBrcStationDynArray;
4752 Stations: TDynArrayHashed;
4853 function Search (name : PByteArray): PBrcStation;
4954 { $endif CUSTOMHASH}
50- procedure Init (max: integer);
55+ procedure Init (max: integer; align: boolean );
5156 end ;
5257
5358 TBrcMain = class
5459 protected
5560 fSafe: TLightLock;
5661 fEvent: TSynEvent;
57- fRunning: integer;
62+ fRunning, fMax : integer;
5863 fCurrentChunk: PByteArray;
5964 fCurrentRemain: PtrUInt;
6065 fList: TBrcList;
@@ -90,10 +95,14 @@ TBrcThread = class(TThread)
9095const
9196 HASHSIZE = 1 shl 18 ; // slightly oversized to avoid most collisions
9297
93- procedure TBrcList.Init (max: integer);
98+ procedure TBrcList.Init (max: integer; align: boolean );
9499begin
95100 assert(max <= high(StationHash[0 ]));
96- SetLength(Station, max);
101+ SetLength(StationMem, max); // RTL won't align by 64 bytes
102+ Station := pointer(StationMem);
103+ if align then
104+ while PtrUInt(Station) and 63 <> 0 do // manual alignment
105+ inc(PByte(Station));
97106 SetLength(StationHash, HASHSIZE);
98107end ;
99108
@@ -206,10 +215,11 @@ function StationComp(const A, B): integer;
206215 result := MemCmp(@sa.NameLen, @sb.NameLen, sa.NameLen + 1 );
207216end ;
208217
209- procedure TBrcList.Init (max: integer);
218+ procedure TBrcList.Init (max: integer; align: boolean );
210219begin
220+ // align is just ignored, because TDynArray requires natural alignment
211221 Stations.Init(
212- TypeInfo(TBrcStations ), Station, @StationHash, @StationComp, nil , @Count);
222+ TypeInfo(TBrcStationDynArray ), Station, @StationHash, @StationComp, nil , @Count);
213223 Stations.Capacity := max;
214224end ;
215225
@@ -220,7 +230,7 @@ function TBrcList.Search(name: PByteArray): PBrcStation;
220230begin
221231 assert(name ^[0 ] < SizeOf(TBrcStation.NameText));
222232 i := Stations.FindHashedForAdding(name ^, added);
223- result := @Station[i]; // in two steps (Station[] may be reallocated if added )
233+ result := @Station[i]; // in two steps (Station[] may be reallocated)
224234 if not added then
225235 exit;
226236 MoveFast(name ^, result^.NameLen, name ^[0 ] + 1 );
@@ -237,7 +247,7 @@ constructor TBrcThread.Create(owner: TBrcMain);
237247begin
238248 fOwner := owner;
239249 FreeOnTerminate := true;
240- fList.Init(length( fOwner.fList.Station) );
250+ fList.Init(fOwner.fMax, { align= } true );
241251 InterlockedIncrement(fOwner.fRunning);
242252 inherited Create({ suspended=} false);
243253end ;
@@ -328,7 +338,8 @@ constructor TBrcMain.Create(const fn: TFileName; threads, max: integer;
328338 fEvent := TSynEvent.Create;
329339 if not fMem.Map(fn) then
330340 raise ESynException.CreateUtf8(' Impossible to find %' , [fn]);
331- fList.Init(max);
341+ fMax := max;
342+ fList.Init(fMax, { align=} false); // not aligned for TDynArray.Sort to work
332343 fCurrentChunk := pointer(fMem.Buffer);
333344 fCurrentRemain := fMem.Size;
334345 core := 0 ;
@@ -391,30 +402,23 @@ procedure TBrcMain.Aggregate(const another: TBrcList);
391402 n: integer;
392403begin
393404 fSafe.Lock; // several TBrcThread may finish at the same time
394- { $ifdef CUSTOMHASH}
395- if fList.Count = 0 then
396- fList := another // we can reuse the existing hash table
397- else
398- { $endif CUSTOMHASH}
399- begin
400- n := another.Count;
401- s := pointer(another.Station);
402- repeat
403- { $ifdef CUSTOMHASH}
404- d := fList.Search(@s^.NameText, s^.NameLen);
405- { $else}
406- d := fList.Search(@s^.NameLen);
407- { $endif CUSTOMHASH}
408- inc(d^.Count, s^.Count);
409- inc(d^.Sum, s^.Sum);
410- if s^.Max > d^.Max then
411- d^.Max := s^.Max;
412- if s^.Min < d^.Min then
413- d^.Min := s^.Min;
414- inc(s);
415- dec(n);
416- until n = 0 ;
417- end ;
405+ n := another.Count;
406+ s := pointer(another.Station);
407+ repeat
408+ { $ifdef CUSTOMHASH}
409+ d := fList.Search(@s^.NameText, s^.NameLen);
410+ { $else}
411+ d := fList.Search(@s^.NameLen);
412+ { $endif CUSTOMHASH}
413+ inc(d^.Count, s^.Count);
414+ inc(d^.Sum, s^.Sum);
415+ if s^.Max > d^.Max then
416+ d^.Max := s^.Max;
417+ if s^.Min < d^.Min then
418+ d^.Min := s^.Min;
419+ inc(s);
420+ dec(n);
421+ until n = 0 ;
418422 fSafe.UnLock;
419423 if InterlockedDecrement(fRunning) = 0 then
420424 fEvent.SetEvent; // all threads finished: release main console thread
@@ -482,8 +486,8 @@ function TBrcMain.SortedText: RawUtf8;
482486 tmp: TTextWriterStackBuffer;
483487begin
484488 { $ifdef CUSTOMHASH}
485- DynArrayFakeLength(pointer( fList.Station) , fList.Count);
486- DynArray(TypeInfo(TBrcStations ), fList.Station).Sort(ByStationName);
489+ DynArrayFakeLength(fList.Station, fList.Count);
490+ DynArray(TypeInfo(TBrcStationDynArray ), fList.Station).Sort(ByStationName);
487491 { $else}
488492 fList.Stations.Sort(ByStationName);
489493 { $endif CUSTOMHASH}
@@ -530,7 +534,8 @@ function TBrcMain.SortedText: RawUtf8;
530534 assert(SizeOf(TBrcStation) = 64 ); // 64 bytes = CPU L1 cache line size
531535 // read command line parameters
532536 Executable.Command.ExeDescription := ' The mORMot One Billion Row Challenge' ;
533- fn := Executable.Command.ArgString(0 , ' the data source #filename' );
537+ if Executable.Command.Arg(0 , ' the data source #filename' ) then
538+ Utf8ToFileName(Executable.Command.Args[0 ], fn);
534539 verbose := Executable.Command.Option(
535540 [' v' , ' verbose' ], ' generate verbose output with timing' );
536541 affinity := Executable.Command.Option(
0 commit comments