Bug 1523996 - part 5 - bulk-read/write adjacent POD fields in IPDL-defined structs; r=Alex_Gaynor
authorNathan Froyd <froydnj@gmail.com>
Wed, 06 Mar 2019 13:59:57 +0000
changeset 520543 608ca009efdfd35fc49127b47bb13a57df6f7273
parent 520542 90d17458ca5425d50a0d58ad59ec227a3458890e
child 520544 351c542d6e4576a5e9e9b0035dc4780783054e58
push id10862
push userffxbld-merge
push dateMon, 11 Mar 2019 13:01:11 +0000
treeherdermozilla-beta@a2e7f5c935da [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersAlex_Gaynor
bugs1523996
milestone67.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1523996 - part 5 - bulk-read/write adjacent POD fields in IPDL-defined structs; r=Alex_Gaynor Now that fields are packed nicely, we can take advantage of the contiguous layout of POD fields and read/write all the POD fields of a given size in a single read/write call. For many structs, this should have little or no effect, but for large structs such as LoadInfoArgs, this reduces the number of function calls by ~50%. Differential Revision: https://phabricator.services.mozilla.com/D22001
ipc/ipdl/ipdl/lower.py
--- a/ipc/ipdl/ipdl/lower.py
+++ b/ipc/ipdl/ipdl/lower.py
@@ -1897,16 +1897,57 @@ class _ParamTraits():
 
         block.addstmts([
             StmtExpr(cls.write(var, msgvar, actor, ipdltype)),
         ])
         block.addstmts(cls.writeSentinel(msgvar, sentinelKey))
         return block
 
     @classmethod
+    def bulkSentinelKey(cls, fields):
+        return ' | '.join(f.basename for f in fields)
+
+    @classmethod
+    def checkedBulkWrite(cls, size, fields):
+        block = Block()
+        first = fields[0]
+
+        block.addstmts([
+            StmtExpr(ExprCall(ExprSelect(cls.msgvar, '->', 'WriteBytes'),
+                              args=[ExprAddrOf(ExprCall(first.getMethod(thisexpr=cls.var,
+                                                                        sel='.'))),
+                                    ExprLiteral.Int(size * len(fields))]))
+        ])
+        block.addstmts(cls.writeSentinel(cls.msgvar, cls.bulkSentinelKey(fields)))
+
+        return block
+
+    @classmethod
+    def checkedBulkRead(cls, size, fields):
+        block = Block()
+        first = fields[0]
+
+        readbytes = ExprCall(ExprSelect(cls.msgvar, '->', 'ReadBytesInto'),
+                             args=[cls.itervar,
+                                   ExprAddrOf(ExprCall(first.getMethod(thisexpr=cls.var,
+                                                                       sel='->'))),
+                                   ExprLiteral.Int(size * len(fields))])
+        ifbad = StmtIf(ExprNot(readbytes))
+        errmsg = 'Error bulk reading fields from %s' % first.ipdltype.name()
+        ifbad.addifstmts([cls.fatalError(errmsg),
+                          StmtReturn.FALSE])
+        block.addstmt(ifbad)
+        block.addstmts(cls.readSentinel(cls.msgvar,
+                                        cls.itervar,
+                                        cls.bulkSentinelKey(fields),
+                                        errfnSentinel()(errmsg)))
+
+        return block
+
+    @classmethod
     def checkedRead(cls, ipdltype, var,
                     msgvar, itervar, errfn,
                     paramtype, sentinelKey,
                     errfnSentinel, actor):
         block = Block()
 
         # Read the data
         ifbad = StmtIf(ExprNot(ExprCall(ExprVar('ReadIPDLParam'),
@@ -2073,37 +2114,49 @@ class _ParamTraits():
         cxxtype = Type(structtype.fullname())
 
         def get(sel, f):
             return ExprCall(f.getMethod(thisexpr=cls.var, sel=sel))
 
         write = []
         read = []
 
-        # The iteration order here doesn't particularly matter, but we choose
-        # member order for ideally better cache performance.
-        for f in sd.fields_member_order():
-            writefield = cls.checkedWrite(f.ipdltype,
-                                          get('.', f),
-                                          cls.msgvar,
-                                          sentinelKey=f.basename,
-                                          actor=cls.actor)
-            readfield = cls._checkedRead(f.ipdltype,
-                                         ExprAddrOf(get('->', f)), f.basename,
-                                         '\'' + f.getMethod().name + '\' ' +
-                                         '(' + f.ipdltype.name() + ') member of ' +
-                                         '\'' + structtype.name() + '\'')
-
-            # Wrap the read/write in a side check if the field is special.
-            if f.special:
-                writefield = cls.ifsideis(f.side, writefield)
-                readfield = cls.ifsideis(f.side, readfield)
-
-            write.append(writefield)
-            read.append(readfield)
+        for (size, fields) in itertools.groupby(sd.fields_member_order(),
+                                                lambda f: pod_size(f.ipdltype)):
+            fields = list(fields)
+
+            if size == pod_size_sentinel:
+                for f in fields:
+                    writefield = cls.checkedWrite(f.ipdltype,
+                                                  get('.', f),
+                                                  cls.msgvar,
+                                                  sentinelKey=f.basename,
+                                                  actor=cls.actor)
+                    readfield = cls._checkedRead(f.ipdltype,
+                                                 ExprAddrOf(get('->', f)), f.basename,
+                                                 '\'' + f.getMethod().name + '\' ' +
+                                                 '(' + f.ipdltype.name() + ') member of ' +
+                                                 '\'' + structtype.name() + '\'')
+
+                    # Wrap the read/write in a side check if the field is special.
+                    if f.special:
+                        writefield = cls.ifsideis(f.side, writefield)
+                        readfield = cls.ifsideis(f.side, readfield)
+
+                    write.append(writefield)
+                    read.append(readfield)
+            else:
+                for f in fields:
+                    assert not f.special
+
+                writefield = cls.checkedBulkWrite(size, fields)
+                readfield = cls.checkedBulkRead(size, fields)
+
+                write.append(writefield)
+                read.append(readfield)
 
         read.append(StmtReturn.TRUE)
 
         return cls.generateDecl(cxxtype, write, read)
 
     @classmethod
     def unionPickling(cls, uniontype):
         # NOTE: Not using _cxxBareType here as we don't have a side