Skip to content

Commit a3d18d0

Browse files
committed
Optimize Offset/Pad/Prep: use cached head and slicing, reduce casting
1 parent b8eb562 commit a3d18d0

File tree

1 file changed

+52
-44
lines changed

1 file changed

+52
-44
lines changed

python/flatbuffers/builder.py

Lines changed: 52 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ def __init__(self, initialSize=1024):
166166
def Clear(self):
167167
## @cond FLATBUFFERS_INTERNAL
168168
self.current_vtable = None
169-
self.head = UOffsetTFlags.py_type(len(self.Bytes))
169+
self.head = len(self.Bytes)
170170
self.minalign = 1
171171
self.objectEnd = None
172172
self.vtables = {}
@@ -192,7 +192,7 @@ def Output(self):
192192
if not self.finished:
193193
raise BuilderNotFinishedError()
194194

195-
return self.Bytes[self.Head() :]
195+
return self.Bytes[self.head :]
196196

197197
## @cond FLATBUFFERS_INTERNAL
198198
def StartObject(self, numfields):
@@ -321,7 +321,7 @@ def EndObject(self):
321321
self.nested = False
322322
return self.WriteVtable()
323323

324-
def growByteBuffer(self):
324+
def GrowByteBuffer(self):
325325
"""Doubles the size of the byteslice, and copies the old data towards
326326
327327
the end of the new buffer (since we build the buffer backwards).
@@ -352,12 +352,15 @@ def Head(self):
352352
## @cond FLATBUFFERS_INTERNAL
353353
def Offset(self):
354354
"""Offset relative to the end of the buffer."""
355-
return UOffsetTFlags.py_type(len(self.Bytes) - self.Head())
355+
return len(self.Bytes) - self.head
356356

357357
def Pad(self, n):
358358
"""Pad places zeros at the current offset."""
359-
for i in range_func(n):
360-
self.Place(0, N.Uint8Flags)
359+
if n <= 0:
360+
return
361+
new_head = self.head - n
362+
self.Bytes[new_head : self.head] = b"\x00" * n
363+
self.head = new_head
361364

362365
def Prep(self, size, additionalBytes):
363366
"""Prep prepares to write an element of `size` after `additional_bytes`
@@ -374,15 +377,19 @@ def Prep(self, size, additionalBytes):
374377

375378
# Find the amount of alignment needed such that `size` is properly
376379
# aligned after `additionalBytes`:
377-
alignSize = (~(len(self.Bytes) - self.Head() + additionalBytes)) + 1
380+
head = self.head
381+
buf_len = len(self.Bytes)
382+
alignSize = (~(buf_len - head + additionalBytes)) + 1
378383
alignSize &= size - 1
379384

380385
# Reallocate the buffer if needed:
381-
while self.Head() < alignSize + size + additionalBytes:
382-
oldBufSize = len(self.Bytes)
383-
self.growByteBuffer()
384-
updated_head = self.head + len(self.Bytes) - oldBufSize
385-
self.head = UOffsetTFlags.py_type(updated_head)
386+
needed = alignSize + size + additionalBytes
387+
while head < needed:
388+
oldBufSize = buf_len
389+
self.GrowByteBuffer()
390+
buf_len = len(self.Bytes)
391+
head += buf_len - oldBufSize
392+
self.head = head
386393
self.Pad(alignSize)
387394

388395
def PrependSOffsetTRelative(self, off):
@@ -482,16 +489,15 @@ def CreateString(self, s, encoding="utf-8", errors="strict"):
482489
else:
483490
raise TypeError("non-string passed to CreateString")
484491

485-
self.Prep(N.UOffsetTFlags.bytewidth, (len(x) + 1) * N.Uint8Flags.bytewidth)
492+
payload_len = len(x)
493+
self.Prep(N.UOffsetTFlags.bytewidth, (payload_len + 1) * N.Uint8Flags.bytewidth)
486494
self.Place(0, N.Uint8Flags)
487495

488-
l = UOffsetTFlags.py_type(len(s))
489-
## @cond FLATBUFFERS_INTERNAL
490-
self.head = UOffsetTFlags.py_type(self.Head() - l)
491-
## @endcond
492-
self.Bytes[self.Head() : self.Head() + l] = x
496+
new_head = self.head - payload_len
497+
self.head = new_head
498+
self.Bytes[new_head : new_head + payload_len] = x
493499

494-
self.vectorNumElems = len(x)
500+
self.vectorNumElems = payload_len
495501
return self.EndVector()
496502

497503
def CreateByteVector(self, x):
@@ -505,15 +511,13 @@ def CreateByteVector(self, x):
505511
if not isinstance(x, compat.binary_types):
506512
raise TypeError("non-byte vector passed to CreateByteVector")
507513

508-
self.Prep(N.UOffsetTFlags.bytewidth, len(x) * N.Uint8Flags.bytewidth)
509-
510-
l = UOffsetTFlags.py_type(len(x))
511-
## @cond FLATBUFFERS_INTERNAL
512-
self.head = UOffsetTFlags.py_type(self.Head() - l)
513-
## @endcond
514-
self.Bytes[self.Head() : self.Head() + l] = x
514+
data_len = len(x)
515+
self.Prep(N.UOffsetTFlags.bytewidth, data_len * N.Uint8Flags.bytewidth)
516+
new_head = self.head - data_len
517+
self.head = new_head
518+
self.Bytes[new_head : new_head + data_len] = x
515519

516-
self.vectorNumElems = len(x)
520+
self.vectorNumElems = data_len
517521
return self.EndVector()
518522

519523
def CreateNumpyVector(self, x):
@@ -540,14 +544,14 @@ def CreateNumpyVector(self, x):
540544
else:
541545
x_lend = x.byteswap(inplace=False)
542546

543-
# Calculate total length
544-
l = UOffsetTFlags.py_type(x_lend.itemsize * x_lend.size)
545-
## @cond FLATBUFFERS_INTERNAL
546-
self.head = UOffsetTFlags.py_type(self.Head() - l)
547-
## @endcond
548-
549547
# tobytes ensures c_contiguous ordering
550-
self.Bytes[self.Head() : self.Head() + l] = x_lend.tobytes(order="C")
548+
payload = x_lend.tobytes(order="C")
549+
550+
# Calculate total length
551+
payload_len = len(payload)
552+
new_head = self.head - payload_len
553+
self.head = new_head
554+
self.Bytes[new_head : new_head + payload_len] = payload
551555

552556
self.vectorNumElems = x.size
553557
return self.EndVector()
@@ -617,11 +621,11 @@ def __Finish(self, rootTable, sizePrefix, file_identifier=None):
617621

618622
self.PrependUOffsetTRelative(rootTable)
619623
if sizePrefix:
620-
size = len(self.Bytes) - self.Head()
624+
size = len(self.Bytes) - self.head
621625
N.enforce_number(size, N.Int32Flags)
622626
self.PrependInt32(size)
623627
self.finished = True
624-
return self.Head()
628+
return self.head
625629

626630
def Finish(self, rootTable, file_identifier=None):
627631
"""Finish finalizes a buffer, pointing to the given `rootTable`."""
@@ -815,35 +819,39 @@ def Place(self, x, flags):
815819
"""
816820

817821
N.enforce_number(x, flags)
818-
self.head = self.head - flags.bytewidth
819-
encode.Write(flags.packer_type, self.Bytes, self.Head(), x)
822+
new_head = self.head - flags.bytewidth
823+
self.head = new_head
824+
encode.Write(flags.packer_type, self.Bytes, new_head, x)
820825

821826
def PlaceVOffsetT(self, x):
822827
"""PlaceVOffsetT prepends a VOffsetT to the Builder, without checking
823828
824829
for space.
825830
"""
826831
N.enforce_number(x, N.VOffsetTFlags)
827-
self.head = self.head - N.VOffsetTFlags.bytewidth
828-
encode.Write(packer.voffset, self.Bytes, self.Head(), x)
832+
new_head = self.head - N.VOffsetTFlags.bytewidth
833+
self.head = new_head
834+
encode.Write(packer.voffset, self.Bytes, new_head, x)
829835

830836
def PlaceSOffsetT(self, x):
831837
"""PlaceSOffsetT prepends a SOffsetT to the Builder, without checking
832838
833839
for space.
834840
"""
835841
N.enforce_number(x, N.SOffsetTFlags)
836-
self.head = self.head - N.SOffsetTFlags.bytewidth
837-
encode.Write(packer.soffset, self.Bytes, self.Head(), x)
842+
new_head = self.head - N.SOffsetTFlags.bytewidth
843+
self.head = new_head
844+
encode.Write(packer.soffset, self.Bytes, new_head, x)
838845

839846
def PlaceUOffsetT(self, x):
840847
"""PlaceUOffsetT prepends a UOffsetT to the Builder, without checking
841848
842849
for space.
843850
"""
844851
N.enforce_number(x, N.UOffsetTFlags)
845-
self.head = self.head - N.UOffsetTFlags.bytewidth
846-
encode.Write(packer.uoffset, self.Bytes, self.Head(), x)
852+
new_head = self.head - N.UOffsetTFlags.bytewidth
853+
self.head = new_head
854+
encode.Write(packer.uoffset, self.Bytes, new_head, x)
847855

848856
## @endcond
849857

0 commit comments

Comments
 (0)