Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: support benchmark on older versions of bigframes #1050

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions noxfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -887,9 +887,19 @@ def benchmark(session: nox.Session):
"all benchmarks are run."
),
)
parser.add_argument(
"-v",
"--bigframe-version",
type=str,
default=None,
help="Specify the version of bigframes to test against.",
)

args = parser.parse_args(session.posargs)

if args.bigframe_version:
session.install(f"bigframes=={args.bigframe_version}")

benchmark_script_list: List[pathlib.Path] = []
if args.benchmark_filter:
for filter_item in args.benchmark_filter:
Expand Down Expand Up @@ -922,6 +932,7 @@ def benchmark(session: nox.Session):
f"--publish-benchmarks={base_path}",
f"--iterations={args.iterations}",
f"--output-csv={args.output_csv}",
f"--backtrace={args.bigframe_version is not None}",
)


Expand Down
86 changes: 73 additions & 13 deletions scripts/run_and_publish_benchmark.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,13 @@
import subprocess
import sys
import tempfile
import traceback
from typing import Dict, List, Tuple, Union

import numpy as np
import pandas as pd
import pandas_gbq
import requests

LOGGING_NAME_ENV_VAR = "BIGFRAMES_PERFORMANCE_LOG_NAME"
CURRENT_DIRECTORY = pathlib.Path(__file__).parent.absolute()
Expand Down Expand Up @@ -73,8 +75,10 @@ def run_benchmark_subprocess(args, log_env_name_var, file_path=None, region=None
if file.suffix != ".backup":
print(f"Benchmark failed, deleting: {file}")
file.unlink()

error_file = directory / f"{pathlib.Path(file_path).name}.error"
error_file.touch()
with error_file.open("w") as f:
f.write(traceback.format_exc())


def collect_benchmark_result(
Expand All @@ -83,7 +87,7 @@ def collect_benchmark_result(
"""Generate a DataFrame report on HTTP queries, bytes processed, slot time and execution time from log files."""
path = pathlib.Path(benchmark_path)
try:
results_dict: Dict[str, List[Union[int, float, None]]] = {}
results_dict: Dict[str, List[Union[int, float, str, None]]] = {}
bytes_files = sorted(path.rglob("*.bytesprocessed"))
millis_files = sorted(path.rglob("*.slotmillis"))
bq_seconds_files = sorted(path.rglob("*.bq_exec_time_seconds"))
Expand All @@ -100,6 +104,18 @@ def collect_benchmark_result(
"Mismatch in the number of report files for bytes, millis, and seconds."
)

for error_file in error_files:
filename = error_file.relative_to(path).with_suffix("")
with open(error_file, "r") as file:
results_dict[str(filename)] = [
None,
None,
None,
None,
None,
file.read().strip(),
]

for idx in range(len(bytes_files)):
bytes_file = bytes_files[idx]
millis_file = millis_files[idx]
Expand Down Expand Up @@ -142,6 +158,7 @@ def collect_benchmark_result(
total_slot_millis,
local_seconds,
bq_seconds,
None,
]
finally:
for files_to_remove in (
Expand All @@ -160,6 +177,7 @@ def collect_benchmark_result(
"Slot_Millis",
"Local_Execution_Time_Sec",
"BigQuery_Execution_Time_Sec",
"Error",
]

benchmark_metrics = pd.DataFrame.from_dict(
Expand Down Expand Up @@ -245,25 +263,55 @@ def geometric_mean_excluding_zeros(data):
return round(np.exp(log_data.mean()), 1)


def get_repository_status():
def get_pypi_release_time(package_name, version):
"""
Fetch the release time of a specific version of a package from PyPI.
"""
url = f"https://pypi.org/pypi/{package_name}/{version}/json"
response = requests.get(url)

if response.status_code == 200:
data = response.json()
release_time = data["urls"][0]["upload_time_iso_8601"]
return release_time
else:
raise ValueError(
f"Failed to retrieve package info for {package_name} version {version}"
)


def get_repository_status(backtrace: bool = False):
current_directory = os.getcwd()
subprocess.run(
["git", "config", "--global", "--add", "safe.directory", current_directory],
check=True,
)

git_hash = subprocess.check_output(
["git", "rev-parse", "--short", "HEAD"], text=True
).strip()
bigframes_version = subprocess.check_output(
["python", "-c", "import bigframes; print(bigframes.__version__)"], text=True
).strip()
with tempfile.TemporaryDirectory() as tmpdirname:
bigframes_version = subprocess.check_output(
["python", "-c", "import bigframes; print(bigframes.__version__)"],
text=True,
cwd=tmpdirname,
).strip()

if backtrace:
git_hash = "benchmark_backtrace"
# We use the PyPI release time for backtrace benchmarks to align with the version's
# release date. This ensures that combining backtrace data with regular benchmark
# results won't affect time-based analysis.
benchmark_start_time = get_pypi_release_time("bigframes", bigframes_version)
else:
git_hash = subprocess.check_output(
["git", "rev-parse", "--short", "HEAD"], text=True
).strip()
benchmark_start_time = datetime.datetime.now().isoformat()

release_version = (
f"{bigframes_version}dev{datetime.datetime.now().strftime('%Y%m%d')}+{git_hash}"
)

return {
"benchmark_start_time": datetime.datetime.now().isoformat(),
"benchmark_start_time": benchmark_start_time,
"git_hash": git_hash,
"bigframes_version": bigframes_version,
"release_version": release_version,
Expand Down Expand Up @@ -302,14 +350,16 @@ def find_config(start_path):
return None


def publish_to_bigquery(dataframe, notebook, project_name="bigframes-metrics"):
def publish_to_bigquery(
dataframe, notebook, project_name="bigframes-metrics", backtrace=False
):
bigquery_table = (
f"{project_name}.benchmark_report.notebook_benchmark"
if notebook
else f"{project_name}.benchmark_report.benchmark"
)

repo_status = get_repository_status()
repo_status = get_repository_status(backtrace)
for idx, col in enumerate(repo_status.keys()):
dataframe.insert(idx, col, repo_status[col])

Expand Down Expand Up @@ -420,6 +470,14 @@ def parse_arguments():
help="Determines whether to output results to a CSV file. If no location is provided, a temporary location is automatically generated.",
)

parser.add_argument(
"--backtrace",
type=str,
choices=["True", "False"],
default="False",
help="Specify whether to perform backtrace benchmarking. Use 'True' to enable or 'False' to disable it.",
)

return parser.parse_args()


Expand Down Expand Up @@ -450,7 +508,9 @@ def main():
# The 'BENCHMARK_AND_PUBLISH' environment variable should be set to 'true' only
# in specific Kokoro sessions.
if os.getenv("BENCHMARK_AND_PUBLISH", "false") == "true":
publish_to_bigquery(benchmark_metrics, args.notebook)
publish_to_bigquery(
benchmark_metrics, args.notebook, backtrace=(args.backtrace == "True")
)
# If the 'GCLOUD_BENCH_PUBLISH_PROJECT' environment variable is set, publish the
# benchmark metrics to a specified BigQuery table in the provided project. This is
# intended for local testing where the default behavior is not to publish results.
Expand Down
4 changes: 3 additions & 1 deletion tests/benchmark/db_benchmark/groupby/q1.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
import pathlib

import benchmark.utils as utils
import bigframes_vendored.db_benchmark.groupby_queries as vendored_dbbenchmark_groupby_queries

if __name__ == "__main__":
(
Expand All @@ -26,6 +25,9 @@
suffix,
) = utils.get_configuration(include_table_id=True)
current_path = pathlib.Path(__file__).absolute()
vendored_dbbenchmark_groupby_queries = utils.import_local_module(
"bigframes_vendored.db_benchmark.groupby_queries"
)

utils.get_execution_time(
vendored_dbbenchmark_groupby_queries.q1,
Expand Down
4 changes: 3 additions & 1 deletion tests/benchmark/db_benchmark/groupby/q10.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
import pathlib

import benchmark.utils as utils
import bigframes_vendored.db_benchmark.groupby_queries as vendored_dbbenchmark_groupby_queries

if __name__ == "__main__":
(
Expand All @@ -26,6 +25,9 @@
suffix,
) = utils.get_configuration(include_table_id=True)
current_path = pathlib.Path(__file__).absolute()
vendored_dbbenchmark_groupby_queries = utils.import_local_module(
"bigframes_vendored.db_benchmark.groupby_queries"
)

utils.get_execution_time(
vendored_dbbenchmark_groupby_queries.q10,
Expand Down
4 changes: 3 additions & 1 deletion tests/benchmark/db_benchmark/groupby/q2.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
import pathlib

import benchmark.utils as utils
import bigframes_vendored.db_benchmark.groupby_queries as vendored_dbbenchmark_groupby_queries

if __name__ == "__main__":
(
Expand All @@ -26,6 +25,9 @@
suffix,
) = utils.get_configuration(include_table_id=True)
current_path = pathlib.Path(__file__).absolute()
vendored_dbbenchmark_groupby_queries = utils.import_local_module(
"bigframes_vendored.db_benchmark.groupby_queries"
)

utils.get_execution_time(
vendored_dbbenchmark_groupby_queries.q2,
Expand Down
4 changes: 3 additions & 1 deletion tests/benchmark/db_benchmark/groupby/q3.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
import pathlib

import benchmark.utils as utils
import bigframes_vendored.db_benchmark.groupby_queries as vendored_dbbenchmark_groupby_queries

if __name__ == "__main__":
(
Expand All @@ -26,6 +25,9 @@
suffix,
) = utils.get_configuration(include_table_id=True)
current_path = pathlib.Path(__file__).absolute()
vendored_dbbenchmark_groupby_queries = utils.import_local_module(
"bigframes_vendored.db_benchmark.groupby_queries"
)

utils.get_execution_time(
vendored_dbbenchmark_groupby_queries.q3,
Expand Down
4 changes: 3 additions & 1 deletion tests/benchmark/db_benchmark/groupby/q4.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
import pathlib

import benchmark.utils as utils
import bigframes_vendored.db_benchmark.groupby_queries as vendored_dbbenchmark_groupby_queries

if __name__ == "__main__":
(
Expand All @@ -26,6 +25,9 @@
suffix,
) = utils.get_configuration(include_table_id=True)
current_path = pathlib.Path(__file__).absolute()
vendored_dbbenchmark_groupby_queries = utils.import_local_module(
"bigframes_vendored.db_benchmark.groupby_queries"
)

utils.get_execution_time(
vendored_dbbenchmark_groupby_queries.q4,
Expand Down
4 changes: 3 additions & 1 deletion tests/benchmark/db_benchmark/groupby/q5.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
import pathlib

import benchmark.utils as utils
import bigframes_vendored.db_benchmark.groupby_queries as vendored_dbbenchmark_groupby_queries

if __name__ == "__main__":
(
Expand All @@ -26,6 +25,9 @@
suffix,
) = utils.get_configuration(include_table_id=True)
current_path = pathlib.Path(__file__).absolute()
vendored_dbbenchmark_groupby_queries = utils.import_local_module(
"bigframes_vendored.db_benchmark.groupby_queries"
)

utils.get_execution_time(
vendored_dbbenchmark_groupby_queries.q5,
Expand Down
4 changes: 3 additions & 1 deletion tests/benchmark/db_benchmark/groupby/q6.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
import pathlib

import benchmark.utils as utils
import bigframes_vendored.db_benchmark.groupby_queries as vendored_dbbenchmark_groupby_queries

if __name__ == "__main__":
(
Expand All @@ -26,6 +25,9 @@
suffix,
) = utils.get_configuration(include_table_id=True)
current_path = pathlib.Path(__file__).absolute()
vendored_dbbenchmark_groupby_queries = utils.import_local_module(
"bigframes_vendored.db_benchmark.groupby_queries"
)

utils.get_execution_time(
vendored_dbbenchmark_groupby_queries.q6,
Expand Down
4 changes: 3 additions & 1 deletion tests/benchmark/db_benchmark/groupby/q7.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
import pathlib

import benchmark.utils as utils
import bigframes_vendored.db_benchmark.groupby_queries as vendored_dbbenchmark_groupby_queries

if __name__ == "__main__":
(
Expand All @@ -26,6 +25,9 @@
suffix,
) = utils.get_configuration(include_table_id=True)
current_path = pathlib.Path(__file__).absolute()
vendored_dbbenchmark_groupby_queries = utils.import_local_module(
"bigframes_vendored.db_benchmark.groupby_queries"
)

utils.get_execution_time(
vendored_dbbenchmark_groupby_queries.q7,
Expand Down
4 changes: 3 additions & 1 deletion tests/benchmark/db_benchmark/groupby/q8.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
import pathlib

import benchmark.utils as utils
import bigframes_vendored.db_benchmark.groupby_queries as vendored_dbbenchmark_groupby_queries

if __name__ == "__main__":
(
Expand All @@ -26,6 +25,9 @@
suffix,
) = utils.get_configuration(include_table_id=True)
current_path = pathlib.Path(__file__).absolute()
vendored_dbbenchmark_groupby_queries = utils.import_local_module(
"bigframes_vendored.db_benchmark.groupby_queries"
)

utils.get_execution_time(
vendored_dbbenchmark_groupby_queries.q8,
Expand Down
5 changes: 3 additions & 2 deletions tests/benchmark/db_benchmark/join/q1.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
import pathlib

import benchmark.utils as utils
import bigframes_vendored.db_benchmark.join_queries as vendored_dbbenchmark_join_queries

if __name__ == "__main__":
(
Expand All @@ -25,8 +24,10 @@
session,
suffix,
) = utils.get_configuration(include_table_id=True)

current_path = pathlib.Path(__file__).absolute()
vendored_dbbenchmark_join_queries = utils.import_local_module(
"bigframes_vendored.db_benchmark.join_queries"
)

utils.get_execution_time(
vendored_dbbenchmark_join_queries.q1,
Expand Down
Loading