Skip to content

Commit

Permalink
new FieldBitSet/FieldBitCount functions
Browse files Browse the repository at this point in the history
- optimized for TFieldBits parameter, especially on 64-bit CPUs
  • Loading branch information
Arnaud Bouchez committed Oct 17, 2022
1 parent 509b815 commit 26d7416
Show file tree
Hide file tree
Showing 10 changed files with 60 additions and 26 deletions.
1 change: 1 addition & 0 deletions src/core/mormot.core.base.asmx64.inc
Original file line number Diff line number Diff line change
Expand Up @@ -2381,6 +2381,7 @@ asm .noframe
{$endif WIN64ABI}
{$endif FPC}
end;

function crc32cby4sse42(crc, value: cardinal): cardinal;
{$ifdef FPC}nostackframe; assembler; asm {$else} asm .noframe {$endif FPC}
mov eax, crc
Expand Down
2 changes: 1 addition & 1 deletion src/core/mormot.core.base.pas
Original file line number Diff line number Diff line change
Expand Up @@ -2228,7 +2228,7 @@ function GetBitsCountPas(value: PtrInt): PtrInt;
/// compute how many bits are set in a given pointer-sized integer
// - the PopCnt() intrinsic under FPC doesn't have any fallback on older CPUs,
// and default implementation is 5 times slower than our GetBitsCountPas() on x64
// - this redirected function will use fast SSE4.2 popcnt opcode, if available
// - this redirected function will use fast SSE4.2 "popcnt" opcode, if available
var GetBitsCountPtrInt: function(value: PtrInt): PtrInt = GetBitsCountPas;

/// compute how many bytes are needed to store a given number of bits
Expand Down
37 changes: 35 additions & 2 deletions src/db/mormot.db.core.pas
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,16 @@ function IsEqual(const A, B: TFieldBits): boolean; overload;
function IsAllFields(const Fields: TFieldBits): boolean;
{$ifdef HASINLINE}inline;{$endif}

/// faster alternative to "byte(Index) in Fields" expression
// - warning: no Index range check is done
// - similar to GetBitPtr(), but optimized for default MAX_SQLFIELDS=64
function FieldBitSet(const Fields: TFieldBits; Index: PtrUInt): boolean;
{$ifdef HASINLINE}inline;{$endif}

/// faster alternative to "GetBitsCount(Fields, MaxFIelds)" expression
function FieldBitCount(const Fields: TFieldBits; MaxFields: integer = MAX_SQLFIELDS): PtrInt;
{$ifdef HASINLINE}inline;{$endif}

/// fast initialize a TFieldBits with 0
// - is optimized for 64, 128, 192 and 256 max bits count (i.e. MAX_SQLFIELDS)
// - will work also with any other value
Expand Down Expand Up @@ -1480,6 +1490,29 @@ function IsAllFields(const Fields: TFieldBits): boolean;
{$endif MAX_SQLFIELDS_64}
end;

function FieldBitSet(const Fields: TFieldBits; Index: PtrUInt): boolean;
begin
{$if defined(MAX_SQLFIELDS_64) and defined(CPU64)}
result := PInt64(@Fields)^ and (Int64(1) shl Index) <> 0;
{$else}
result := PIntegerArray(@Fields)[Index shr 5] and (1 shl (Index and 31)) <> 0;
{$ifend MAX_SQLFIELDS_64 and CPU64}
end;

function FieldBitCount(const Fields: TFieldBits; MaxFields: integer): PtrInt;
begin
{$ifdef MAX_SQLFIELDS_64}
{$ifdef CPU64}
result := GetBitsCountPtrInt(PtrInt(Fields));
{$else}
result := GetBitsCountPtrInt(PIntegerArray(@Fields)[0]) +
GetBitsCountPtrInt(PIntegerArray(@Fields)[1]);
{$endif CPU64}
{$else}
result := GetBitsCount(Fields, MaxFields);
{$endif MAX_SQLFIELDS_64}
end;

procedure FillZero(var Fields: TFieldBits);
begin
{$ifdef MAX_SQLFIELDS_128}
Expand Down Expand Up @@ -1511,10 +1544,10 @@ procedure FieldBitsToIndex(const Fields: TFieldBits;
begin
if MaxLength > MAX_SQLFIELDS then
raise ESynDBException.CreateUtf8('FieldBitsToIndex(MaxLength=%)', [MaxLength]);
SetLength(Index, GetBitsCount(Fields, MaxLength));
SetLength(Index, FieldBitCount(Fields, MaxLength));
p := pointer(Index);
for i := 0 to MaxLength - 1 do
if byte(i) in Fields then
if FieldBitSet(Fields, i) then
begin
p^ := i;
inc(p);
Expand Down
2 changes: 1 addition & 1 deletion src/mormot.commit.inc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
'2.0.4207'
'2.0.4208'
6 changes: 3 additions & 3 deletions src/orm/mormot.orm.base.pas
Original file line number Diff line number Diff line change
Expand Up @@ -11160,7 +11160,7 @@ function TOrmPropertiesAbstract.CsvTextFromFieldBits(const Bits: TFieldBits): Ra
begin
l := 0;
for f := 0 to Fields.Count - 1 do
if byte(f) in Bits then
if FieldBitSet(Bits, f) then
inc(l, length(Fields.List[f].Name) + 1);
if l = 0 then
begin
Expand All @@ -11170,7 +11170,7 @@ function TOrmPropertiesAbstract.CsvTextFromFieldBits(const Bits: TFieldBits): Ra
FastSetString(result, nil, l - 1); // allocate once for all
p := pointer(result);
for f := 0 to Fields.Count - 1 do
if byte(f) in Bits then
if FieldBitSet(Bits, f) then
begin
l := length(Fields.List[f].Name);
MoveFast(pointer(Fields.List[f].Name)^, p^, l);
Expand All @@ -11193,7 +11193,7 @@ procedure TOrmPropertiesAbstract.CsvFromFieldBits(const Prefix: array of const;
try
W.Add(Prefix, twNone);
for f := 0 to Fields.Count - 1 do
if byte(f) in Bits then
if FieldBitSet(Bits, f) then
begin
W.AddString(Fields.List[f].Name);
if BitsSuffix <> '' then
Expand Down
24 changes: 12 additions & 12 deletions src/orm/mormot.orm.core.pas
Original file line number Diff line number Diff line change
Expand Up @@ -6546,14 +6546,14 @@ procedure TOrm.FillFrom(aRecord: TOrm; const aRecordFieldBits: TFieldBits);
if POrmClass(aRecord)^ = POrmClass(self)^ then
fID := aRecord.fID; // same class -> ID values will match
for f := 0 to D.Count - 1 do
if byte(f) in aRecordFieldBits then
if FieldBitSet(aRecordFieldBits, f) then
D.List[f].CopyValue(aRecord, self);
exit;
end;
// two diverse tables -> don't copy ID, and per-name field lookup
S := aRecord.OrmProps.Fields;
for i := 0 to S.Count - 1 do
if byte(i) in aRecordFieldBits then
if FieldBitSet(aRecordFieldBits, i) then
begin
SP := S.List[i];
if D.List[i].Name = SP.Name then
Expand Down Expand Up @@ -6883,7 +6883,7 @@ procedure TOrm.GetBinaryValues(W: TBufferWriter;
begin
with Orm.Fields do
for f := 0 to Count - 1 do
if byte(f) in aFields then
if FieldBitSet(aFields, f) then
List[f].GetBinary(self, W);
end;

Expand Down Expand Up @@ -7006,7 +7006,7 @@ procedure TOrm.AppendAsJsonObject(W: TJsonWriter; Fields: TFieldBits;
nfo := pointer(P.Fields.List);
for i := 0 to P.Fields.Count - 1 do
begin
if byte(i) in Fields then
if FieldBitSet(Fields, i) then
begin
W.Add('"');
W.AddNoJsonEscape(pointer(nfo^.Name), length(nfo^.Name));
Expand Down Expand Up @@ -7266,7 +7266,7 @@ ClassToText(c, cname); // TOrmFtsTest = class(TOrmFts3Porter)
if SQL <> '' then
begin
result := result + Name + SQL;
if byte(i) in IsUniqueFieldsBits then
if FieldBitSet(IsUniqueFieldsBits, i) then
insert(' UNIQUE', result, length(result) - 1);
end;
end;
Expand Down Expand Up @@ -7424,7 +7424,7 @@ procedure TOrm.ClearProperties(const aFieldsCsv: RawUtf8);
else if not FieldBitsFromCsv(aFieldsCsv, bits) then
exit;
for f := 0 to Fields.Count - 1 do
if (byte(f) in bits) and
if FieldBitSet(bits, f) and
(Fields.List[f].OrmFieldType in COPIABLE_FIELDS) then
Fields.List[f].SetValue(self, nil, 0, false); // clear field value
end;
Expand Down Expand Up @@ -8165,7 +8165,7 @@ procedure TOrm.GetAsDocVariant(withID: boolean; const withFields: TFieldBits;
doc.Values[i] := fID;
end;
for f := 0 to Fields.Count - 1 do
if byte(f) in withFields then
if FieldBitSet(withFields, f) then
begin
i := doc.InternalAdd(Fields.List[f].Name);
Fields.List[f].GetVariant(self, doc.Values[i]);
Expand Down Expand Up @@ -10171,10 +10171,10 @@ constructor TOrmModelProperties.Create(aModel: TOrmModel; aTable: TOrmClass;
// pre-computation of SQL statements
SQL.UpdateSetAll := SQL.UpdateSetAll + Name + '=?,';
SQL.InsertSet := SQL.InsertSet + Name + ',';
if byte(f) in SimpleFieldsBits[ooUpdate] then
if FieldBitSet(SimpleFieldsBits[ooUpdate], f) then
SQL.UpdateSetSimple := SQL.UpdateSetSimple + Name + '=?,';
// filter + validation of unique fields, i.e. if marked as "stored false"
if byte(f) in IsUniqueFieldsBits then
if FieldBitSet(IsUniqueFieldsBits, f) then
begin
// must trim() text value before storage, and validate for unicity
if OrmFieldType in [oftUtf8Text, oftAnsiText] then
Expand Down Expand Up @@ -10441,7 +10441,7 @@ procedure TOrmMapping.ComputeSql;
if OrmFieldType in COPIABLE_FIELDS then // oftMany fields do not exist
case content of
cTableSimpleFields:
if byte(f) in SimpleFieldsBits[ooSelect] then
if FieldBitSet(SimpleFieldsBits[ooSelect], f) then
begin
if withTableName then
W.AddStrings([TableName, '.']);
Expand All @@ -10452,7 +10452,7 @@ procedure TOrmMapping.ComputeSql;
W.AddComma;
end;
cUpdateSimple:
if byte(f) in SimpleFieldsBits[ooSelect] then
if FieldBitSet(SimpleFieldsBits[ooSelect], f) then
W.AddStrings([ExtFieldNames[f], '=?,']);
cUpdateSetAll:
W.AddStrings([ExtFieldNames[f], '=?,']);
Expand Down Expand Up @@ -11185,7 +11185,7 @@ function TRestBatch.Add(Value: TOrm; SendData, ForceID: boolean;
(fields * props.FieldBits[oftBlob] <> []) then
for f := 1 to length(props.BlobFields) do
begin
if (blob^.PropertyIndex in fields) and
if FieldBitSet(fields, blob^.PropertyIndex) and
blob^.IsValueVoid(Value) then
exclude(fields, blob^.PropertyIndex);
inc(blob);
Expand Down
4 changes: 2 additions & 2 deletions src/orm/mormot.orm.mongodb.pas
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,7 @@ function TRestStorageMongoDB.BsonProjectionSet(var Projection: variant;
name := name + SubFields[0];
W.BsonWrite(name, result);
for i := 0 to fStoredClassRecordProps.Fields.Count - 1 do
if i in Fields then
if FieldBitSet(Fields, i) then
begin
name := fStoredClassMapping^.ExtFieldNames[i];
if i + 1 < sf then
Expand All @@ -350,7 +350,7 @@ function TRestStorageMongoDB.BsonProjectionSet(var Projection: variant;
else
n := 0;
for i := 0 to fStoredClassRecordProps.Fields.Count - 1 do
if i in Fields then
if FieldBitSet(Fields, i) then
begin
BsonFieldNames^[n] := ExtFieldNames[i];
inc(n);
Expand Down
2 changes: 1 addition & 1 deletion src/orm/mormot.orm.sql.pas
Original file line number Diff line number Diff line change
Expand Up @@ -928,7 +928,7 @@ function TRestStorageExternal.AdaptSqlForEngineList(var SQL: RawUtf8): boolean;
for f := 0 to high(stmt.OrderByField) do
begin
W.AddString(fStoredClassMapping^.FieldNameByIndex(stmt.OrderByField[f] - 1));
if byte(f) in stmt.OrderByFieldDesc then
if FieldBitSet(stmt.OrderByFieldDesc, f) then
W.AddShorter(' desc');
W.AddComma;
end;
Expand Down
4 changes: 2 additions & 2 deletions src/orm/mormot.orm.sqlite3.pas
Original file line number Diff line number Diff line change
Expand Up @@ -2736,7 +2736,7 @@ function ProcessPutHexID(DB: TRestOrmServerDB; const Fields: TFieldBits;
if not IsEqual(b^.SimpleFields, Fields) then
begin
b^.SimpleFields := Fields;
b^.UpdateFieldsCount := GetBitsCount(Fields, Props.Fields.Count) + 1;
b^.UpdateFieldsCount := FieldBitCount(Fields, Props.Fields.Count) + 1;
Props.CsvFromFieldBits(['update ', Props.SqlTableName, ' set '],
Fields, '=?', [' where RowID=?'], b^.UpdateSql);
end;
Expand Down Expand Up @@ -2812,7 +2812,7 @@ function TRestOrmServerDB.InternalBatchDirectOne(Encoding: TRestBatchEncoding;
if fBatch^.SimpleFieldsCount = 0 then
begin
fBatch^.SimpleFields := Fields;
fBatch^.SimpleFieldsCount := GetBitsCount(Fields, SizeOf(Fields) shl 3) + 1;
fBatch^.SimpleFieldsCount := FieldBitCount(Fields) + 1;
end;
AddID(fBatch^.ID, fBatch^.IDCount, result);
ObjArrayAddCount(fBatch^.Simples, pointer(Sent), fBatch^.ValuesCount);
Expand Down
4 changes: 2 additions & 2 deletions src/orm/mormot.orm.storage.pas
Original file line number Diff line number Diff line change
Expand Up @@ -2265,14 +2265,14 @@ constructor TRestStorageInMemory.Create(aClass: TOrmClass;
end;
// initialize fUnique[] fUniquePerField[] lookup tables
fields := fStoredClassRecordProps.Fields;
n := GetBitsCount(fStoredClassRecordProps.IsUniqueFieldsBits, fields.Count);
n := FieldBitCount(fStoredClassRecordProps.IsUniqueFieldsBits);
if n > 0 then
begin
SetLength(fUnique, n);
SetLength(fUniquePerField, fields.Count);
n := 0;
for f := 0 to fields.Count - 1 do
if byte(f) in fStoredClassRecordProps.IsUniqueFieldsBits then
if FieldBitSet(fStoredClassRecordProps.IsUniqueFieldsBits, f) then
begin
fUnique[n] := TRestStorageInMemoryUnique.Create(self, fields.List[f]);
fUniquePerField[f] := fUnique[n];
Expand Down

0 comments on commit 26d7416

Please sign in to comment.