Skip to content

Commit 0551b7a

Browse files
committed
Update - New algo - store float as int64.
1 parent b65aea8 commit 0551b7a

File tree

3 files changed

+77
-86
lines changed

3 files changed

+77
-86
lines changed

entries/ikelaiah/src/OneBRC.lpr

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,9 @@
2222
{$mode objfpc}{$H+}{$J-}{$modeSwitch advancedRecords}
2323
{$codepage utf8}
2424

25-
2625
uses
2726
{$IFDEF UNIX}
28-
cthreads,
27+
cmem, cthreads,
2928
{$ENDIF}
3029
Classes,
3130
SysUtils,

entries/ikelaiah/src/stopwatch.pas

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,10 @@ procedure DisplayTimer;
5353
seconds := elapsedMilliseconds div 1000;
5454
milliseconds := elapsedMilliseconds mod 1000;
5555

56-
WriteLn;
5756
WriteLn('------------------------------');
5857
WriteLn('Elapsed time: ', hours, ' hours ', minutes, ' minutes ',
5958
seconds, ' seconds ', milliseconds, ' milliseconds');
60-
//WriteLn('Elapsed time: ', (endTime - startTime), ' ms');
59+
WriteLn('------------------------------');
6160
end;
6261

6362

Lines changed: 75 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
unit WeatherStation;
22

33
{$mode objfpc}{$H+}{$J-}{$modeSwitch advancedRecords}
4-
54
interface
65

76
uses
87
{$IFDEF UNIX}
9-
cthreads,
8+
cmem, cthreads,
109
{$ENDIF}
1110
Classes,
1211
SysUtils,
@@ -21,15 +20,16 @@ interface
2120
// Create a record of temperature stats
2221
TStat = record
2322
var
24-
min: int64;
25-
max: int64;
23+
min: int64; // Borrowed the concept from go's approach to improve performance, save floats as int64
24+
max: int64; // This saved ~2 mins processing time.
2625
sum: int64;
27-
Count: int64;
26+
cnt: int64;
2827
private
2928
function RoundEx(x: double): double; // Borrowed from the baseline program
3029
function PascalRound(x: double): double; // Borrowed from the baseline program
3130
public
32-
constructor Create(newMin: int64; newMax: int64; newSum: int64; newCount: int64);
31+
constructor Create(const newMin: int64; const newMax: int64;
32+
const newSum: int64; const newCount: int64);
3333
function ToString: string;
3434

3535
end;
@@ -39,8 +39,7 @@ TStat = record
3939
TWeatherDictionary = specialize TDictionary<string, TStat>;
4040

4141
// The main algorithm to process the temp measurements from various weather station
42-
procedure ProcessTempMeasurements(filename: string);
43-
42+
procedure ProcessTempMeasurements(const filename: string);
4443

4544
implementation
4645

@@ -70,29 +69,24 @@ function TStat.PascalRound(x: double): double;
7069
Result := t;
7170
end;
7271

73-
constructor TStat.Create(newMin: int64; newMax: int64; newSum: int64; newCount: int64);
72+
constructor TStat.Create(const newMin: int64; const newMax: int64;
73+
const newSum: int64; const newCount: int64);
7474
begin
7575
self.min := newMin;
7676
self.max := newMax;
7777
self.sum := newSum;
78-
self.Count := newCount;
78+
self.cnt := newCount;
7979
end;
8080

8181
function TStat.ToString: string;
8282
var
8383
minR, meanR, maxR: double; // Store the rounded values prior saving to TStringList.
8484
begin
85-
{$IFDEF DEBUG}
86-
Result := Format('Min: %.1f; Mean: %.1f; Maxp: %.1f; Sum: %.1f; Count %d',
87-
[self.min, (self.sum / self.Count), self.max,
88-
self.sum, self.Count]);
89-
{$ENDIF DEBUG}
90-
// Result := Format('%.1f/%.1f/%.1f', [self.min, (self.sum / self.count), self.max]);
9185
minR := RoundEx(self.min / 10);
9286
maxR := RoundEx(self.max / 10);
93-
meanR := RoundEx(self.sum / self.Count / 10);
94-
Result := FormatFloat('0.0', minR) + '/' + FormatFloat('0.0', meanR) + '/' + FormatFloat('0.0', maxR);
95-
87+
meanR := RoundEx(self.sum / self.cnt / 10);
88+
Result := FormatFloat('0.0', minR) + '/' + FormatFloat('0.0', meanR) +
89+
'/' + FormatFloat('0.0', maxR);
9690
end;
9791

9892
{
@@ -121,18 +115,14 @@ function CustomTStringListComparer(AList: TStringList;
121115
end;
122116

123117

124-
procedure AddCityTemperature(cityName: string; newTemp: int64; var weatherDictionary: TWeatherDictionary);
118+
procedure AddCityTemperature(const cityName: string; const newTemp: int64;
119+
var weatherDictionary: TWeatherDictionary);
125120
var
126121
stat: TStat;
127122
begin
128-
// If city name exists, modify temp as needed
123+
// If city name esxists, modify temp as needed
129124
if weatherDictionary.ContainsKey(cityName) then
130125
begin
131-
132-
{$IFDEF DEBUG}
133-
WriteLn('City found: ', cityName);
134-
{$ENDIF DEBUG}
135-
136126
// Get the temp record
137127
stat := weatherDictionary[cityName];
138128

@@ -146,7 +136,7 @@ procedure AddCityTemperature(cityName: string; newTemp: int64; var weatherDictio
146136
stat.sum := stat.sum + newTemp;
147137

148138
// Increase the counter
149-
stat.Count := stat.Count + 1;
139+
stat.cnt := stat.cnt + 1;
150140

151141
// Update the stat of this city
152142
weatherDictionary.AddOrSetValue(cityName, stat);
@@ -156,27 +146,20 @@ procedure AddCityTemperature(cityName: string; newTemp: int64; var weatherDictio
156146
if not weatherDictionary.ContainsKey(cityName) then
157147
begin
158148
weatherDictionary.Add(cityName, TStat.Create(newTemp, newTemp, newTemp, 1));
159-
{$IFDEF DEBUG}
160-
WriteLn('Added: ', cityName);
161-
{$ENDIF DEBUG}
162149
end;
163150
end;
164151

165-
procedure ProcessTempMeasurements(filename: string);
152+
procedure ProcessTempMeasurements(const filename: string);
166153
var
167154
wd: TWeatherDictionary;
168-
line, ws, recordedTemp: string;
155+
line, ws, strTemp: string;
169156
weatherStationList: TStringList;
170157
textFile: System.TextFile;
171-
isFirstKey: boolean = True;
172-
delimiterPos: integer;
158+
delimiterPos, valCode: integer;
159+
intTemp: int64;
160+
index: integer;
173161
begin
174162

175-
// Start a timer
176-
{$IFDEF DEBUG}
177-
Stopwatch.StartTimer;
178-
{$ENDIF}
179-
180163
// Create a city - weather dictionary
181164
wd := TWeatherDictionary.Create;
182165
weatherStationList := TStringList.Create;
@@ -190,32 +173,36 @@ procedure ProcessTempMeasurements(filename: string);
190173
// Open the file for reading
191174
Reset(textFile);
192175

176+
{$IFDEF DEBUG}
177+
// Start a timer
178+
Stopwatch.StartTimer;
179+
{$ENDIF}
180+
193181
// Keep reading lines until the end of the file is reached
194182
while not EOF(textFile) do
195183
begin
196184
// Read a line
197185
ReadLn(textFile, line);
198186

199-
// If the line start with #, then continue/skip.
200-
if (line[1] = '#') then continue;
201-
202187
// Get position of the delimiter
203188
delimiterPos := Pos(';', line);
204-
205-
// Else, add an entry into the dictionary.
206-
// Get the weather station name
207-
// Using Copy ans POS - as suggested by Gemini AI.
208-
// This made the program 3 mins fater when processing 1 billion rows.
209-
ws := Copy(line, 1, delimiterPos - 1);
210-
211-
// Get the temperature recorded, as string, remove '.' from string float
212-
// because we want to save it as int64.
213-
recordedTemp := Copy(line, delimiterPos + 1, Length(line));
214-
recordedTemp := StringReplace(recordedTemp, '.', '', [rfReplaceAll, rfIgnoreCase]);
215-
216-
// Add the weather station and the recorded temp (as int64) in the TDictionary
217-
AddCityTemperature(ws, StrToInt64(recordedTemp), wd);
218-
189+
if delimiterPos > 0 then
190+
begin
191+
// Get the weather station name
192+
// Using Copy and POS - as suggested by Gemini AI.
193+
// This part saves 3 mins faster when processing 1 billion rows.
194+
ws := Copy(line, 1, delimiterPos - 1);
195+
196+
// Get the temperature recorded, as string, remove '.' from string float
197+
// because we want to save it as int64.
198+
strTemp := Copy(line, delimiterPos + 1, Length(line));
199+
strTemp := StringReplace(strTemp, '.', '', [rfReplaceAll]);
200+
201+
// Add the weather station and the recorded temp (as int64) in the TDictionary
202+
Val(strTemp, intTemp, valCode);
203+
if valCode <> 0 then Continue;
204+
AddCityTemperature(ws, intTemp, wd);
205+
end;
219206
end; // end while loop reading line at a time
220207

221208
// Close the file
@@ -226,46 +213,52 @@ procedure ProcessTempMeasurements(filename: string);
226213
WriteLn('File handling error occurred. Details: ', E.Message);
227214
end; // End of file reading ////////////////////////////////////////////////
228215

216+
{$IFDEF DEBUG}
217+
Stopwatch.StopTimer;
218+
WriteLn('Finished reading and updating dictionary');
219+
Stopwatch.DisplayTimer;
220+
{$ENDIF}
221+
229222
// Format and sort weather station by name and temp stat ///////////////////
230-
ws:='';
223+
{$IFDEF DEBUG}
224+
Stopwatch.StartTimer;
225+
{$ENDIF}
226+
ws := '';
231227
for ws in wd.Keys do
232228
begin
233-
weatherStationList.Add(ws + '=' + wd[ws].ToString);
229+
weatherStationList.Add(ws + '=' + wd[ws].ToString + ', ');
234230
end;
235231
weatherStationList.CustomSort(@CustomTStringListComparer);
236232

237-
// Print TStringList - sorted by weather station and temp stat /////////////
238-
Write('{');
239-
for ws in weatherStationList do
240-
begin
241-
// If it's not the first key, print a comma
242-
if not isFirstKey then
243-
Write(', ');
244-
245-
// Print the weather station and the temp stat
246-
Write(ws);
247-
248-
// Set isFirstKey to False after printing the first key
249-
isFirstKey := False;
250-
end;
233+
{$IFDEF DEBUG}
234+
Stopwatch.StopTimer;
235+
WriteLn('Finished creating TStringList and sorted it');
236+
Stopwatch.DisplayTimer;
237+
{$ENDIF}
251238

252-
WriteLn('}');
239+
// Print TStringList - sorted by weather station and temp stat /////////////
240+
{$IFDEF DEBUG}
241+
Stopwatch.StartTimer;
242+
{$ENDIF}
243+
strTemp := '';
244+
// Print the weather station and the temp stat
245+
for index := 0 to weatherStationList.Count - 1 do
246+
strTemp := strTemp + weatherStationList[index];
247+
// Remove last comma and space; ', ', a neat trick from Gus.
248+
SetLength(strTemp, Length(strTemp) - 2);
249+
WriteLn('{', strTemp, '}');
253250

254251
{$IFDEF DEBUG}
255-
WriteLn('DEBUG mode on');
256-
{$ENDIF DEBUG}
252+
Stopwatch.StopTimer;
253+
WriteLn('Finished printing the sorted weather station and temperatures');
254+
Stopwatch.DisplayTimer;
255+
{$ENDIF}
257256

258257
finally
259258
weatherStationList.Free;
260259
wd.Free;
261260
end; // End of processing TDictionary and TStringList
262261

263-
// Stop a timer
264-
{$IFDEF DEBUG}
265-
Stopwatch.StopTimer;
266-
Stopwatch.DisplayTimer;
267-
{$ENDIF}
268-
269262
end;
270263

271264
end.

0 commit comments

Comments
 (0)