1- unit OneBRC ;
1+ unit uonebrc ;
22
33{ $mode ObjFPC}{ $H+}
44
55interface
66
77uses
8- Classes, SysUtils,
8+ Classes, SysUtils, syncobjs,
99 mormot.core.os, mormot.core.base;
1010
1111function RoundExDouble (const ATemp: Double): Double; inline;
@@ -45,7 +45,8 @@ TMyDictionary = class
4545 property StationNames: TStationNames read FStationNames;
4646 property Values: TValues read FValues;
4747 function TryGetValue (const aKey: Cardinal; out aValue: PStationData): Boolean; inline;
48- procedure Add (const aKey: Cardinal; const aValue: PStationData; const aStationName: AnsiString); inline;
48+ procedure Add (const aKey: Cardinal; const aValue: PStationData); inline;
49+ procedure AddName (const aKey: Cardinal; const aStationName: AnsiString); inline;
4950 end ;
5051
5152 { TOneBRC }
@@ -57,11 +58,14 @@ TOneBRC = class
5758 FMemoryMap: TMemoryMap;
5859 FData: pAnsiChar;
5960 FDataSize: Int64;
61+ FCS: TCriticalSection;
6062
6163 FThreadCount: UInt16;
6264 FThreads: array of TThread;
6365 FStationsDicts: array of TMyDictionary;
6466
67+ FStationsNames: TMyDictionary;
68+
6569 procedure ExtractLineData (const aStart: Int64; const aEnd: Int64; out aLength: ShortInt; out aTemp: SmallInt); inline;
6670
6771 public
@@ -185,7 +189,7 @@ function TMyDictionary.TryGetValue(const aKey: Cardinal; out aValue: PStationDat
185189 aValue := FValues[vIdx];
186190end ;
187191
188- procedure TMyDictionary.Add (const aKey: Cardinal; const aValue: PStationData; const aStationName: AnsiString );
192+ procedure TMyDictionary.Add (const aKey: Cardinal; const aValue: PStationData);
189193var
190194 vIdx: Integer;
191195 vFound: Boolean;
@@ -194,6 +198,19 @@ procedure TMyDictionary.Add(const aKey: Cardinal; const aValue: PStationData; co
194198 if not vFound then begin
195199 FHashes[vIdx] := aKey;
196200 FValues[vIdx] := aValue;
201+ end
202+ else
203+ raise Exception.Create (' TMyDict: cannot add, duplicate key' );
204+ end ;
205+
206+ procedure TMyDictionary.AddName (const aKey: Cardinal; const aStationName: AnsiString);
207+ var
208+ vIdx: Integer;
209+ vFound: Boolean;
210+ begin
211+ InternalFind (aKey, vFound, vIdx);
212+ if not vFound then begin
213+ FHashes[vIdx] := aKey;
197214 FStationNames[vIdx] := aStationName;
198215 end
199216 else
@@ -251,11 +268,19 @@ constructor TOneBRC.Create (const aThreadCount: UInt16);
251268 for I := 0 to aThreadCount - 1 do begin
252269 FStationsDicts[I] := TMyDictionary.Create;
253270 end ;
271+
272+ FStationsNames := TMyDictionary.Create;
273+ FCS := TCriticalSection.Create;
254274end ;
255275
256276destructor TOneBRC.Destroy;
277+ var I: UInt16;
257278begin
258- // TODO: free data structures
279+ FCS.Free;
280+ FStationsNames.Free;
281+ for I := 0 to FThreadCount - 1 do begin
282+ FStationsDicts[I].Free;
283+ end ;
259284 inherited Destroy;
260285end ;
261286
@@ -344,18 +369,22 @@ procedure TOneBRC.ProcessData (aThreadNb: UInt16; aStartIdx: Int64; aEndIdx: Int
344369 Inc (vData^.Count);
345370 end
346371 else begin
347- // SetString done only once per station name (per thread), for later sorting
348- // for 1-thread, I had a separate list to store station names
349- // for N-threads, merging those lists became costly.
350- // store the name directly in the record, we'll generate a stringlist at the end
351- SetString(vStation, pAnsiChar(@FData[vLineStart]), vLenStationName);
352-
353372 // pre-allocated array of records instead of on-the-go allocation
354373 vData^.Min := vTemp;
355374 vData^.Max := vTemp;
356375 vData^.Sum := vTemp;
357376 vData^.Count := 1 ;
358- FStationsDicts[aThreadNb].Add (vHash, vData, vStation);
377+ FStationsDicts[aThreadNb].Add (vHash, vData);
378+
379+ // SetString done only once per station name, for later sorting
380+ if not FStationsNames.TryGetValue (vHash, vData) then begin
381+ FCS.Acquire;
382+ if not FStationsNames.TryGetValue (vHash, vData) then begin
383+ SetString(vStation, pAnsiChar(@FData[vLineStart]), vLenStationName);
384+ FStationsNames.AddName (vHash, vStation);
385+ end ;
386+ FCS.Release;
387+ end ;
359388 end ;
360389
361390 // we're at a #10: next line starts at the next index
@@ -400,7 +429,7 @@ procedure TOneBRC.Merge(aLeft: UInt16; aRight: UInt16);
400429 vDataL^.Min := vDataR^.Min;
401430 end
402431 else begin
403- FStationsDicts[aLeft].Add (iHash, vDataR, FStationsDicts[aRight].StationNames[I] );
432+ FStationsDicts[aLeft].Add (iHash, vDataR);
404433 end ;
405434 end ;
406435end ;
@@ -431,10 +460,9 @@ procedure TOneBRC.GenerateOutput;
431460 try
432461 vStations.BeginUpdate;
433462 for I := 0 to cDictSize - 1 do begin
434- vData := FStationsDicts[0 ].Values[I];
435- // count = 0 means empty slot: skip
436- if vData^.Count <> 0 then begin
437- vStations.Add(FStationsDicts[0 ].StationNames[I]);
463+ if FStationsNames.Keys[I] <> 0 then begin
464+ // count = 0 means empty slot: skip
465+ vStations.Add(FStationsNames.StationNames[I]);
438466 end ;
439467 end ;
440468 vStations.EndUpdate;
0 commit comments