Author: Antonio Cuni <[email protected]>
Branch: faster-rstruct
Changeset: r80805:7b525c75f558
Date: 2015-11-20 16:21 +0100
http://bitbucket.org/pypy/pypy/changeset/7b525c75f558/

Log:    use the str_storage_getitem fastpath to unpack ints and floats by
        reading them directly from within the buffer, is it's properly
        aligned. Add tests which disallow the fast path so that we can
        easily test also the slow paths

diff --git a/rpython/rlib/rstruct/nativefmttable.py 
b/rpython/rlib/rstruct/nativefmttable.py
--- a/rpython/rlib/rstruct/nativefmttable.py
+++ b/rpython/rlib/rstruct/nativefmttable.py
@@ -8,7 +8,8 @@
 from rpython.rlib.objectmodel import specialize
 from rpython.rlib.rarithmetic import r_singlefloat, widen
 from rpython.rlib.rstruct import standardfmttable as std
-from rpython.rlib.rstruct.standardfmttable import native_is_bigendian
+from rpython.rlib.rstruct.standardfmttable import (native_is_bigendian, 
unpack_fastpath,
+                                                   CannotUnpack)
 from rpython.rlib.rstruct.error import StructError
 from rpython.rlib.unroll import unrolling_iterable
 from rpython.rlib.strstorage import str_storage_getitem
@@ -44,16 +45,13 @@
 
 @specialize.argtype(0)
 def unpack_double(fmtiter):
-    if fmtiter.is_aligned(sizeof_double):
-        # fast path
-        input = fmtiter.get_buffer()
-        pos = fmtiter.get_pos()
-        doubleval = str_storage_getitem(rffi.DOUBLE, input, pos)
-        fmtiter.advance(sizeof_double)
-    else:
+    try:
+        doubleval = unpack_fastpath(rffi.DOUBLE, fmtiter)
+    except CannotUnpack:
         # slow path, take the slice
         input = fmtiter.read(sizeof_double)
         doubleval = str_storage_getitem(rffi.DOUBLE, input, 0)
+    #
     fmtiter.appendobj(doubleval)
 
 def pack_float(fmtiter):
@@ -72,8 +70,11 @@
 
 @specialize.argtype(0)
 def unpack_float(fmtiter):
-    input = fmtiter.read(sizeof_float)
-    floatval = str_storage_getitem(rffi.FLOAT, input, 0)
+    try:
+        floatval = unpack_fastpath(rffi.FLOAT, fmtiter)
+    except CannotUnpack:
+        input = fmtiter.read(sizeof_float)
+        floatval = str_storage_getitem(rffi.FLOAT, input, 0)
     doubleval = float(floatval) # convert from r_singlefloat to rpython's float
     fmtiter.appendobj(doubleval)
 
diff --git a/rpython/rlib/rstruct/runpack.py b/rpython/rlib/rstruct/runpack.py
--- a/rpython/rlib/rstruct/runpack.py
+++ b/rpython/rlib/rstruct/runpack.py
@@ -39,16 +39,13 @@
         def appendobj(self, value):
             self.value = value
 
-        def is_aligned(self, size):
-            return self.mr.inputpos % size == 0
-
-        def get_buffer(self):
-            return self.mr.input
-
         def get_pos(self):
             return self.mr.inputpos
 
-        def advance(self, size):
+        def get_buffer_as_string_maybe(self):
+            return self.mr.input
+
+        def skip(self, size):
             self.read(size) # XXX, could avoid taking the slice
     ReaderForPos.__name__ = 'ReaderForPos%d' % pos
     return ReaderForPos
diff --git a/rpython/rlib/rstruct/standardfmttable.py 
b/rpython/rlib/rstruct/standardfmttable.py
--- a/rpython/rlib/rstruct/standardfmttable.py
+++ b/rpython/rlib/rstruct/standardfmttable.py
@@ -130,6 +130,21 @@
 
 # ____________________________________________________________
 
+ALLOW_FASTPATH = True # set to False by TestNoFastPath
+
+class CannotUnpack(Exception):
+    pass
+
[email protected](0)
+def unpack_fastpath(TYPE, fmtiter):
+    size = rffi.sizeof(TYPE)
+    pos = fmtiter.get_pos()
+    strbuf = fmtiter.get_buffer_as_string_maybe()
+    if pos % size != 0 or strbuf is None or not ALLOW_FASTPATH:
+        raise CannotUnpack
+    fmtiter.skip(size)
+    return str_storage_getitem(TYPE, strbuf, pos)
+
 @specialize.argtype(0)
 def unpack_pad(fmtiter, count):
     fmtiter.read(count)
@@ -173,8 +188,6 @@
             return TYPE
     raise KeyError("Cannot find an int type size=%d, signed=%d" % (size, 
signed))
 
-UNPACK_ALLOW_RAW_STORAGE = True
-
 def make_int_unpacker(size, signed, _memo={}):
     try:
         return _memo[size, signed]
@@ -196,17 +209,28 @@
     TYPE = get_rffi_int_type(size, signed)
 
     @specialize.argtype(0)
+    def unpack_int_fastpath_maybe(fmtiter):
+        if fmtiter.bigendian != native_is_bigendian:
+            return False
+        try:
+            intvalue = unpack_fastpath(TYPE, fmtiter)
+        except CannotUnpack:
+            return False
+        if not signed and size < native_int_size:
+            intvalue = rarithmetic.intmask(intvalue)
+        intvalue = inttype(intvalue)
+        fmtiter.appendobj(intvalue)
+        return True
+
+    @specialize.argtype(0)
     def unpack_int(fmtiter):
+        if unpack_int_fastpath_maybe(fmtiter):
+            return
+        # slow path
         intvalue = inttype(0)
         s = fmtiter.read(size)
         idx = 0
-        if UNPACK_ALLOW_RAW_STORAGE and fmtiter.bigendian == 
native_is_bigendian:
-            # fast path, using the native raw_storage
-            intvalue = str_storage_getitem(TYPE, s, 0)
-            if not signed and size < native_int_size:
-                intvalue = rarithmetic.intmask(intvalue)
-            intvalue = inttype(intvalue)
-        elif fmtiter.bigendian:
+        if fmtiter.bigendian:
             for i in unroll_range_size:
                 x = ord(s[idx])
                 if signed and i == 0 and x >= 128:
diff --git a/rpython/rlib/rstruct/test/test_runpack.py 
b/rpython/rlib/rstruct/test/test_runpack.py
--- a/rpython/rlib/rstruct/test/test_runpack.py
+++ b/rpython/rlib/rstruct/test/test_runpack.py
@@ -48,6 +48,13 @@
             d, f = runpack("@df", d_data)
             return d, f
         #
+        # direct test
+        d, f = fn()
+        assert d == 12.34     # no precision lost
+        assert f != 12.34     # precision lost
+        assert abs(f - 12.34) < 1E-6
+        #
+        # translated test
         res = self.interpret(fn, [])
         d = res.item0
         f = res.item1  # convert from r_singlefloat
@@ -83,8 +90,11 @@
         assert unpack(">q", '\xbeMLKJIHH') == -0x41B2B3B4B5B6B7B8
         assert unpack(">Q", '\x81BCDEFGH') == 0x8142434445464748
 
-    def test_unpack_standard_no_raw_storage(self, monkeypatch):
-        monkeypatch.setattr(standardfmttable, 'UNPACK_ALLOW_RAW_STORAGE', 
False)
-        self.test_unpack_standard_little()
-        self.test_unpack_standard_big()
 
+class TestNoFastPath(TestRStruct):
+
+    def setup_method(self, meth):
+        standardfmttable.ALLOW_FASTPATH = False
+
+    def teardown_method(self, meth):
+        standardfmttable.ALLOW_FASTPATH = True
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to