decentriq_util 0.1.0.dev2__tar.gz → 0.1.0.dev3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {decentriq_util-0.1.0.dev2 → decentriq_util-0.1.0.dev3}/PKG-INFO +2 -1
- {decentriq_util-0.1.0.dev2 → decentriq_util-0.1.0.dev3}/decentriq_util/spark/__init__.py +53 -19
- {decentriq_util-0.1.0.dev2 → decentriq_util-0.1.0.dev3}/pyproject.toml +2 -1
- {decentriq_util-0.1.0.dev2 → decentriq_util-0.1.0.dev3}/decentriq_util/__init__.py +0 -0
- {decentriq_util-0.1.0.dev2 → decentriq_util-0.1.0.dev3}/decentriq_util/error.py +0 -0
- {decentriq_util-0.1.0.dev2 → decentriq_util-0.1.0.dev3}/decentriq_util/proto/__init__.py +0 -0
- {decentriq_util-0.1.0.dev2 → decentriq_util-0.1.0.dev3}/decentriq_util/python/__init__.py +0 -0
- {decentriq_util-0.1.0.dev2 → decentriq_util-0.1.0.dev3}/decentriq_util/sql/__init__.py +0 -0
- {decentriq_util-0.1.0.dev2 → decentriq_util-0.1.0.dev3}/decentriq_util/sql/proto/__init__.py +0 -0
- {decentriq_util-0.1.0.dev2 → decentriq_util-0.1.0.dev3}/decentriq_util/sql/proto/compute_sql_pb2.py +0 -0
- {decentriq_util-0.1.0.dev2 → decentriq_util-0.1.0.dev3}/decentriq_util/sql/proto/compute_sql_pb2.pyi +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: decentriq_util
|
|
3
|
-
Version: 0.1.0.
|
|
3
|
+
Version: 0.1.0.dev3
|
|
4
4
|
Summary: Utility functions for Python code run in Decentriq workers
|
|
5
5
|
Author: hafenr
|
|
6
6
|
Author-email: robin.hafen@gmail.com
|
|
@@ -13,4 +13,5 @@ Classifier: Programming Language :: Python :: 3.12
|
|
|
13
13
|
Requires-Dist: pandas (>=1.4.4)
|
|
14
14
|
Requires-Dist: protobuf (>=4.21,<5.0)
|
|
15
15
|
Requires-Dist: psutil (>=5.9.6,<6.0.0)
|
|
16
|
+
Requires-Dist: pyarrow (>=14.0.0,<15.0.0)
|
|
16
17
|
Requires-Dist: pyspark (>=3.4.0,<4.0.0)
|
|
@@ -7,6 +7,7 @@ import os
|
|
|
7
7
|
import shutil
|
|
8
8
|
import psutil
|
|
9
9
|
|
|
10
|
+
import pyarrow.parquet as pq
|
|
10
11
|
from pyspark.sql import SparkSession
|
|
11
12
|
|
|
12
13
|
|
|
@@ -36,6 +37,33 @@ def _get_cgroup_memory() -> int:
|
|
|
36
37
|
return 4 * 1024 * 1024 * 1024
|
|
37
38
|
|
|
38
39
|
|
|
40
|
+
def _get_uncompressed_parquet_size(path: str):
|
|
41
|
+
def is_parquet(filename: str):
|
|
42
|
+
return filename.endswith(".parquet")
|
|
43
|
+
def get_uncompressed_size(file_path: str):
|
|
44
|
+
parquet_file = pq.ParquetFile(file_path)
|
|
45
|
+
metadata = parquet_file.metadata
|
|
46
|
+
uncompressed_size = 0
|
|
47
|
+
for i in range(metadata.num_row_groups):
|
|
48
|
+
row_group = metadata.row_group(i)
|
|
49
|
+
for j in range(row_group.num_columns):
|
|
50
|
+
column = row_group.column(j)
|
|
51
|
+
uncompressed_size += column.total_uncompressed_size
|
|
52
|
+
return uncompressed_size
|
|
53
|
+
if os.path.isfile(path) and is_parquet(path):
|
|
54
|
+
uncompressed_size = get_uncompressed_size(path)
|
|
55
|
+
return uncompressed_size
|
|
56
|
+
# Path might be a subdir as obtained by `partitionBy`
|
|
57
|
+
elif os.path.isdir(path):
|
|
58
|
+
total_uncompressed_size = 0
|
|
59
|
+
for file in os.listdir(path):
|
|
60
|
+
file_path = os.path.join(path, file)
|
|
61
|
+
total_uncompressed_size += _get_uncompressed_parquet_size(file_path)
|
|
62
|
+
return total_uncompressed_size
|
|
63
|
+
else:
|
|
64
|
+
return 0
|
|
65
|
+
|
|
66
|
+
|
|
39
67
|
def _get_num_partitions(
|
|
40
68
|
paths: list[str],
|
|
41
69
|
available_memory: int,
|
|
@@ -44,36 +72,43 @@ def _get_num_partitions(
|
|
|
44
72
|
"""Determine the number of partitions s.t. each partition is as big as possible
|
|
45
73
|
while still making sure all cores are occupied."""
|
|
46
74
|
def get_file_or_dir_size(path):
|
|
47
|
-
if
|
|
48
|
-
|
|
49
|
-
elif os.path.isdir(path):
|
|
50
|
-
total_size = 0
|
|
51
|
-
for dirpath, dirnames, filenames in os.walk(path):
|
|
52
|
-
for f in filenames:
|
|
53
|
-
fp = os.path.join(dirpath, f)
|
|
54
|
-
total_size += os.path.getsize(fp)
|
|
55
|
-
size = total_size
|
|
75
|
+
if path.endswith(".parquet"):
|
|
76
|
+
return _get_uncompressed_parquet_size(path)
|
|
56
77
|
else:
|
|
57
|
-
|
|
78
|
+
if os.path.isfile(path):
|
|
79
|
+
size = os.path.getsize(path)
|
|
80
|
+
elif os.path.isdir(path):
|
|
81
|
+
total_size = 0
|
|
82
|
+
for dirpath, dirnames, filenames in os.walk(path):
|
|
83
|
+
for f in filenames:
|
|
84
|
+
fp = os.path.join(dirpath, f)
|
|
85
|
+
total_size += os.path.getsize(fp)
|
|
86
|
+
size = total_size
|
|
87
|
+
else:
|
|
88
|
+
raise ValueError(f"The path {path} is neither a file nor a directory.")
|
|
58
89
|
return size
|
|
59
90
|
|
|
60
91
|
file_size = max([get_file_or_dir_size(path) for path in paths])
|
|
92
|
+
if file_size == 0:
|
|
93
|
+
return 200 # default number of partitions in spark
|
|
61
94
|
|
|
62
95
|
# Don't make the partitions smaller than 36MiB
|
|
63
96
|
megabyte_in_bytes = 1024 * 1024
|
|
64
97
|
min_partition_size = 32 * megabyte_in_bytes
|
|
65
98
|
|
|
66
99
|
# Try to make partitions as big as possible s.t. each CPU is saturated,
|
|
67
|
-
# while accounting for
|
|
68
|
-
|
|
69
|
-
|
|
100
|
+
# while accounting for 50% overhead in spark.
|
|
101
|
+
task_per_cpu = 3 # try to get at least this many tasks/partitions per CPU
|
|
102
|
+
max_target_partition_size = max(
|
|
103
|
+
int(0.5 * available_memory / (task_per_cpu * cpu_count)) // megabyte_in_bytes * megabyte_in_bytes,
|
|
70
104
|
min_partition_size
|
|
71
105
|
)
|
|
72
106
|
|
|
107
|
+
target_partition_size = max_target_partition_size
|
|
73
108
|
while (
|
|
74
109
|
# If the file is small enough that it cannot be split across all cores,
|
|
75
110
|
# divide the partition size further.
|
|
76
|
-
int(file_size / target_partition_size) < cpu_count
|
|
111
|
+
int(file_size / target_partition_size) < (task_per_cpu * cpu_count)
|
|
77
112
|
):
|
|
78
113
|
# Don't go smaller than the minimum parition size
|
|
79
114
|
if target_partition_size / 2 < min_partition_size:
|
|
@@ -81,6 +116,7 @@ def _get_num_partitions(
|
|
|
81
116
|
target_partition_size = target_partition_size / 2
|
|
82
117
|
|
|
83
118
|
print(f"File size: {file_size}")
|
|
119
|
+
print(f"Max target partition size: {max_target_partition_size}")
|
|
84
120
|
print(f"Target partition size: {target_partition_size}")
|
|
85
121
|
|
|
86
122
|
num_partitions = int(math.ceil(file_size / target_partition_size))
|
|
@@ -90,7 +126,7 @@ def _get_num_partitions(
|
|
|
90
126
|
|
|
91
127
|
def _determine_optimal_spark_settings(
|
|
92
128
|
files: list[str],
|
|
93
|
-
heap_size_extra_room: Optional[int] = 512 * 1024 * 1024
|
|
129
|
+
heap_size_extra_room: Optional[int] = 2 * 512 * 1024 * 1024
|
|
94
130
|
) -> list[Tuple[str, str]]:
|
|
95
131
|
def get_spark_memory() -> int:
|
|
96
132
|
"""Determine the amount of memory available to spark"""
|
|
@@ -100,16 +136,14 @@ def _determine_optimal_spark_settings(
|
|
|
100
136
|
|
|
101
137
|
cpu_count = multiprocessing.cpu_count()
|
|
102
138
|
spark_memory = get_spark_memory()
|
|
139
|
+
spark_memory_4096 = (spark_memory // 4096) * 4096
|
|
103
140
|
num_partitions = _get_num_partitions(files, spark_memory, cpu_count)
|
|
104
141
|
|
|
105
|
-
print(f"CPU count: {cpu_count}")
|
|
106
|
-
print(f"Spark memory: {spark_memory}")
|
|
107
|
-
print(f"Num partitions: {num_partitions}")
|
|
108
|
-
|
|
109
142
|
settings = [
|
|
110
143
|
("spark.sql.shuffle.partitions", str(num_partitions)),
|
|
111
144
|
("spark.default.parallelism", str(num_partitions)),
|
|
112
145
|
("spark.driver.cores", str(cpu_count)),
|
|
146
|
+
("spark.driver.memory", str(spark_memory_4096)),
|
|
113
147
|
]
|
|
114
148
|
print("Spark settings:\n" + f"\n".join([str(x) for x in settings]))
|
|
115
149
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "decentriq_util"
|
|
3
|
-
version = "0.1.0-
|
|
3
|
+
version = "0.1.0-dev3"
|
|
4
4
|
description = "Utility functions for Python code run in Decentriq workers"
|
|
5
5
|
authors = ["hafenr <robin.hafen@gmail.com>"]
|
|
6
6
|
|
|
@@ -10,6 +10,7 @@ protobuf = "^4.21"
|
|
|
10
10
|
pandas = ">=1.4.4"
|
|
11
11
|
pyspark = "^3.4.0"
|
|
12
12
|
psutil = "^5.9.6"
|
|
13
|
+
pyarrow = "^14.0.0"
|
|
13
14
|
|
|
14
15
|
[tool.poetry.dev-dependencies]
|
|
15
16
|
pytest = "^7.2.0"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{decentriq_util-0.1.0.dev2 → decentriq_util-0.1.0.dev3}/decentriq_util/sql/proto/__init__.py
RENAMED
|
File without changes
|
{decentriq_util-0.1.0.dev2 → decentriq_util-0.1.0.dev3}/decentriq_util/sql/proto/compute_sql_pb2.py
RENAMED
|
File without changes
|
{decentriq_util-0.1.0.dev2 → decentriq_util-0.1.0.dev3}/decentriq_util/sql/proto/compute_sql_pb2.pyi
RENAMED
|
File without changes
|