[Piglit] [PATCH v2 9/9] tests/oglconform.py: make interpret_result() faster in most cases

baker.dylan.c at gmail.com baker.dylan.c at gmail.com
Mon Oct 26 11:59:01 PDT 2015


From: Dylan Baker <baker.dylan.c at gmail.com>

This optimizes the common paths of OGLCTest.interpret_result(): pass and
the common skip case.

For most OGLCTest cases the result will be in the final 3 lines of
stdout. In this case it is substantially faster to split the last 3
lines, and then check for a string in that list than to use either an
RegexObject (even a pre-compiled one) or to use a 'str in str'
approach.

I've used the following script to benchmark the performance of each
approach, using the actual output of a test. This assumes a single
expression in the RegexObject (no use of 'or').

```python
from __future__ import absolute_import, division, print_function
import re
import timeit

str_ = 'Tests Passed : 1'

re_ = re.compile(str_)

def smart():
    sub = out.rsplit('n', 4)[1:]
    return str_ in sub

with open('out.txt') as f:
    out = f.read()

print('uncompiled re')
print(timeit.timeit(lambda: re.search(str_, out)))

print('')

print('compiled re')
print(timeit.timeit(lambda: re_.search(out)))

print('')

print('str in str')
print(timeit.timeit(lambda: str_ in out))

print('')

print('smart str')
print(timeit.timeit(smart))
```

This provides an output as follows:
```
uncompiled re
7.29590296745

compiled re
6.59288287163

str in str
9.50384187698

smart str
0.936215162277
```

The final aproach is 10x faster than the 'str in str' approach, and
6.5x faster than the compiled re. This seems much more significant than
it is because timeit.timeit runs 1,000,000 iterations of the function.
---
 tests/oglconform.py | 22 ++++++++++++++++------
 1 file changed, 16 insertions(+), 6 deletions(-)

diff --git a/tests/oglconform.py b/tests/oglconform.py
index cb1d5c1..7128b20 100644
--- a/tests/oglconform.py
+++ b/tests/oglconform.py
@@ -46,9 +46,10 @@ class OGLCTest(Test):
     will obtain a list of tests from oglconform and add them all.
 
     """
-    skip_re = re.compile(r'Total Not run: 1|no test in schedule is '
-                         r'compat|GLSL [13].[345]0 is not supported|wont be '
-                         r'scheduled due to lack of compatible fbconfig')
+    skip_re = re.compile(
+        r'no test in schedule is compat|'
+        r'GLSL [13].[345]0 is not supported|'
+        r'wont be scheduled due to lack of compatible fbconfig')
 
     def __init__(self, category, subtest):
         super(OGLCTest, self).__init__([category, subtest])
@@ -59,10 +60,19 @@ class OGLCTest(Test):
             super(OGLCTest, self).command
 
     def interpret_result(self):
-        if self.skip_re.search(self.result.out) is not None:
-            self.result.result = 'skip'
-        elif re.search('Total Passed : 1', self.result.out) is not None:
+        # Most of what we want to search for is in the last three lines of the
+        # the output
+        split = self.result.out.rsplit('\n', 4)[1:]
+        if 'Total Passed : 1' in split:
             self.result.result = 'pass'
+        elif 'Total Failed : 1' in split:
+            # This is a fast path to avoid the regular expression.
+            self.result.result = 'fail'
+        elif ('Total Not run: 1' in split or
+              self.skip_re.search(self.result.out) is not None):
+            # Lazy evaluation means that the re (which is slow) is only tried if
+            # the more obvious case is not true
+            self.result.result = 'skip'
         else:
             self.result.result = 'fail'
 
-- 
2.6.2



More information about the Piglit mailing list