Skip to content

Commit 876ad1d

Browse files
committed
Merge pull request #220 in LCL/wolframclientforpython from feature/binary_deserialize_concatenation to master
* commit '634e3b4f9a0d544b9c15a900ace8b505c460b780': minor tweaks using 2 funcs adding binary_deserialize benchmark using concatenate_bytes
2 parents 00de96c + 634e3b4 commit 876ad1d

2 files changed

Lines changed: 56 additions & 34 deletions

File tree

wolframclient/cli/commands/benchmark.py

Lines changed: 39 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import tempfile
77

88
from wolframclient.cli.utils import SimpleCommand
9+
from wolframclient.deserializers import binary_deserialize
910
from wolframclient.language import wl
1011
from wolframclient.serializers import export
1112
from wolframclient.utils.debug import timed
@@ -39,13 +40,9 @@ def complexity_handler(self, complexity):
3940
"functions": repeat(wl.Function(1, 2, 3), complexity),
4041
}
4142

42-
@timed
43-
def export(self, *args, **opts):
44-
return export(*args, **opts)
43+
def formatted_time(self, function, *args, **opts):
4544

46-
def formatted_time(self, *args, **opts):
47-
48-
time = sum(first(self.export(*args, **opts)) for i in range(self.repetitions))
45+
time = sum(first(timed(function)(*args, **opts)) for i in range(self.repetitions))
4946

5047
return "%.5f" % (time / self.repetitions)
5148

@@ -55,27 +52,50 @@ def table_line(self, *iterable):
5552
def table_divider(self, length):
5653
self.print(*("-" * self.col_size for i in range(length)))
5754

55+
def stream_generators(self, path):
56+
yield "Memory", lambda complexity, export_format, path=path: None
57+
yield "File", lambda complexity, export_format, path=path: os.path.join(
58+
path, "benchmark-test-%s.%s" % (force_text(complexity).zfill(7), export_format)
59+
)
60+
5861
def report(self):
5962

6063
path = tempfile.gettempdir()
6164

6265
benchmarks = [(c, self.complexity_handler(c)) for c in self.complexity]
6366

64-
self.print("dumping results in", path)
67+
self.table_line("dumping results in %s" % path)
68+
self.table_line()
6569

6670
# running export to do all lazy loadings
6771
export(1)
6872

69-
for title, stream_generator in (
70-
("Memory", lambda complexity: None),
71-
(
72-
"File",
73-
lambda complexity: os.path.join(
74-
path,
75-
"benchmark-test-%s.%s" % (force_text(complexity).zfill(7), export_format),
76-
),
77-
),
78-
):
73+
self.table_line("* Binary deserialize")
74+
self.table_line()
75+
76+
self.table_line(
77+
"Memory", *(force_text(c).ljust(self.col_size) for c in self.complexity)
78+
)
79+
self.table_divider(len(self.complexity) + 1)
80+
81+
for label, opts in (("wxf", dict()), ("wxf zip", dict(compress=True))):
82+
83+
self.table_line(
84+
label,
85+
*(
86+
self.formatted_time(
87+
binary_deserialize, export(expr, target_format="wxf", **opts)
88+
)
89+
for complexity, expr in benchmarks
90+
)
91+
)
92+
93+
self.table_line()
94+
95+
self.table_line("* Export")
96+
self.table_line()
97+
98+
for title, stream_generator in self.stream_generators(path):
7999

80100
self.table_line(
81101
title, *(force_text(c).ljust(self.col_size) for c in self.complexity)
@@ -91,8 +111,9 @@ def report(self):
91111
label,
92112
*(
93113
self.formatted_time(
114+
export,
94115
expr,
95-
stream=stream_generator(complexity),
116+
stream=stream_generator(complexity, export_format),
96117
target_format=export_format,
97118
**opts
98119
)
@@ -102,8 +123,6 @@ def report(self):
102123

103124
self.table_line()
104125

105-
self.table_line()
106-
107126
def handle(self, profile, **opts):
108127
if profile:
109128
cProfile.runctx("report()", {"report": self.report}, {})

wolframclient/serializers/wxfencoder/streaming.py

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
from __future__ import absolute_import, print_function, unicode_literals
22

3-
from wolframclient.utils import six
43
from wolframclient.utils.api import zlib
5-
from wolframclient.utils.encoding import force_bytes
4+
from wolframclient.utils.decorators import decorate
5+
from wolframclient.utils.encoding import concatenate_bytes, force_bytes
66

77

88
class ZipCompressedWriter(object):
@@ -32,6 +32,7 @@ class ExactSizeReader(object):
3232
def __init__(self, reader):
3333
self._reader = reader
3434

35+
3536
def read(self, size=-1):
3637
"""Read from an underlying readable object.
3738
@@ -45,17 +46,19 @@ def read(self, size=-1):
4546
# Also a fast path when the requested amount of bytes is returned in one go.
4647
if size <= 0 or len(data) == size:
4748
return data
49+
50+
return self._read_rest(data, size)
51+
52+
@decorate(concatenate_bytes)
53+
def _read_rest(self, data, size=-1):
4854
# need an intermediary buffer
4955
out_len = len(data)
50-
data = six.BytesIO(data)
5156
while out_len < size:
5257
chunk = self._reader.read(size - out_len)
53-
if chunk == b"":
58+
if not chunk:
5459
raise EOFError("Not enough data to read.")
55-
data.write(chunk)
56-
out_len = out_len + len(chunk)
57-
return data.getvalue()
58-
60+
yield chunk
61+
out_len += len(chunk)
5962

6063
class ZipCompressedReader(object):
6164
"""A buffer implementation reading zip compressed data from a source buffer and returning uncompressed data.
@@ -70,6 +73,7 @@ def __init__(self, reader):
7073
self._compressor = zlib.decompressobj()
7174
self._reader = reader
7275

76+
@decorate(concatenate_bytes)
7377
def read(self, size=-1):
7478
"""Read from a compressed stream of bytes and return the inflated byte sequence.
7579
@@ -81,30 +85,29 @@ def read(self, size=-1):
8185
size = -1
8286
else:
8387
chunk_size = ZipCompressedReader.CHUNK_SIZE
84-
out_data = six.BytesIO()
88+
8589
out_len = 0
8690
while True:
8791
# first step find try to find some data to uncompress.
8892
# sometimes some bytes are left over. We have to send them first to zlib.
89-
if self._compressor.unconsumed_tail != b"":
93+
if self._compressor.unconsumed_tail:
9094
data_in = self._compressor.unconsumed_tail
9195
else:
9296
# read more data from input reader. Read in chunk since we can't guess how
9397
# big the inflated result is.
9498
data_in = self._reader.read(chunk_size)
9599
# no more data is available.
96-
if data_in == b"":
100+
if not data_in:
97101
break
98102
# second step, decompress the new chunk
99103
if size > 0:
100104
chunk = self._compressor.decompress(data_in, size - out_len)
101105
else:
102106
chunk = self._compressor.decompress(data_in)
103107
# increment output len.
104-
out_len = out_len + len(chunk)
108+
out_len += len(chunk)
105109
# write to buffer
106-
out_data.write(chunk)
110+
yield chunk
107111
# check requested size against output length.
108112
if size > 0 and out_len == size:
109113
break
110-
return out_data.getvalue()

0 commit comments

Comments
 (0)