Repository navigation
Expand file tree
/
Copy pathbench_local_data_to_arrow.py
More file actions
162 lines (125 loc) · 5.75 KB
/
Copy pathbench_local_data_to_arrow.py
File metadata and controls
162 lines (125 loc) · 5.75 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
"""
Microbenchmarks for ``LocalDataToArrowConversion.convert``, the hot path of
Spark Connect ``createDataFrame`` and Arrow-optimized Python UDF output.
``string`` / ``binary`` columns are converted element by element: the per-element
converter is fast when the element is already the target type (``str`` /
``bytes``) and slower when it needs coercion. Each benchmark sweeps the fraction
of non-target ("other") values from 0% (all fast path) to 100% (all slow path),
for a scalar column, a one-level ``array`` column, and a two-level
``array<array<...>>`` column (which drives the inlined array fast path hardest).
"""
def _build(convert, is_string, leaf, other_val, other_frac, n_rows):
import random
from pyspark.sql.types import ArrayType, BinaryType, StringType, StructField, StructType
leaf_type = StringType() if is_string else BinaryType()
def target(i):
return f"s{i}" if is_string else b"s%d" % i
rnd = random.Random(0)
def elem(i):
return other_val(i) if rnd.random() * 100 < other_frac else target(i)
if leaf == "array2":
schema = StructType([StructField("c", ArrayType(ArrayType(leaf_type)), True)])
data = [([[elem(i), elem(i + 1)], [elem(i + 2), elem(i + 3)]],) for i in range(n_rows)]
elif leaf == "array":
schema = StructType([StructField("c", ArrayType(leaf_type), True)])
data = [([elem(i), elem(i + 1), elem(i + 2)],) for i in range(n_rows)]
else: # scalar
schema = StructType([StructField("c", leaf_type, True)])
data = [(elem(i),) for i in range(n_rows)]
return data, schema
class LocalDataToArrowStringBenchmark:
"""
Benchmark ``convert`` on a ``string`` column, sweeping the kind and fraction
of non-target values.
- ``leaf``: ``scalar``, one-level ``array``, or two-level ``array2``
(``array<array<string>>``).
- ``other``: the non-target values -- ``none`` (null), ``int`` or ``bool``
(a single non-str type, isolating one coercion branch), or ``mix`` (a
rotation of int / float / bool / Decimal / date / datetime).
- ``other_frac``: fraction of elements that are non-target, ``0`` to ``100``.
"""
params = [
[1000000],
["scalar", "array", "array2"],
["none", "int", "bool", "mix"],
[0, 30, 70, 100],
]
param_names = ["n_rows", "leaf", "other", "other_frac"]
def setup(self, n_rows, leaf, other, other_frac):
import datetime
import decimal
from pyspark.sql.conversion import LocalDataToArrowConversion
self.convert = LocalDataToArrowConversion.convert
if other == "none":
def other_val(i):
return None
elif other == "int":
def other_val(i):
return i # int coerced to string via str()
elif other == "bool":
def other_val(i):
return i % 2 == 0 # bool coerced to "true" / "false"
else: # mix: a rotation of non-str types, each coerced to string
_pool = [
123,
4.5,
True,
False,
decimal.Decimal("1.50"),
datetime.date(2020, 1, 1),
datetime.datetime(2020, 1, 1, 3, 4, 5),
]
def other_val(i):
return _pool[i % len(_pool)]
self.data, self.schema = _build(self.convert, True, leaf, other_val, other_frac, n_rows)
def time_convert(self, n_rows, leaf, other, other_frac):
self.convert(self.data, self.schema, False)
def peakmem_convert(self, n_rows, leaf, other, other_frac):
self.convert(self.data, self.schema, False)
class LocalDataToArrowBinaryBenchmark:
"""
Benchmark ``convert`` on a ``binary`` column, sweeping the fraction of
non-target values.
- ``leaf``: ``scalar``, one-level ``array``, or two-level ``array2``
(``array<array<binary>>``).
- ``other``: ``none`` (null) or ``bytearray`` (the only non-bytes input
binary accepts, copied to immutable bytes).
- ``other_frac``: fraction of elements that are non-target, ``0`` to ``100``.
"""
params = [
[1000000],
["scalar", "array", "array2"],
["none", "bytearray"],
[0, 30, 70, 100],
]
param_names = ["n_rows", "leaf", "other", "other_frac"]
def setup(self, n_rows, leaf, other, other_frac):
from pyspark.sql.conversion import LocalDataToArrowConversion
self.convert = LocalDataToArrowConversion.convert
if other == "none":
def other_val(i):
return None
else: # bytearray coerced to immutable bytes
def other_val(i):
return bytearray(b"s%d" % i)
self.data, self.schema = _build(self.convert, False, leaf, other_val, other_frac, n_rows)
def time_convert(self, n_rows, leaf, other, other_frac):
self.convert(self.data, self.schema, False)
def peakmem_convert(self, n_rows, leaf, other, other_frac):
self.convert(self.data, self.schema, False)