13af6ab5fSopenharmony_ci#!/usr/bin/env python3
23af6ab5fSopenharmony_ci# -*- coding: utf-8 -*-
33af6ab5fSopenharmony_ci
43af6ab5fSopenharmony_ci"""
53af6ab5fSopenharmony_ciCopyright (c) 2023 Huawei Device Co., Ltd.
63af6ab5fSopenharmony_ciLicensed under the Apache License, Version 2.0 (the "License");
73af6ab5fSopenharmony_ciyou may not use this file except in compliance with the License.
83af6ab5fSopenharmony_ciYou may obtain a copy of the License at
93af6ab5fSopenharmony_ci
103af6ab5fSopenharmony_ci    http://www.apache.org/licenses/LICENSE-2.0
113af6ab5fSopenharmony_ci
123af6ab5fSopenharmony_ciUnless required by applicable law or agreed to in writing, software
133af6ab5fSopenharmony_cidistributed under the License is distributed on an "AS IS" BASIS,
143af6ab5fSopenharmony_ciWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
153af6ab5fSopenharmony_ciSee the License for the specific language governing permissions and
163af6ab5fSopenharmony_cilimitations under the License.
173af6ab5fSopenharmony_ci"""
183af6ab5fSopenharmony_ci
193af6ab5fSopenharmony_ciimport argparse
203af6ab5fSopenharmony_ciimport math
213af6ab5fSopenharmony_ciimport os
223af6ab5fSopenharmony_ciimport time
233af6ab5fSopenharmony_cifrom config import COMP, DEFAULT_RUNS, PARSE_ONLY, OPT_LEVEL, RUNS_NUM, DEFAULT_PARAMS, CUR_FILE_DIR, \
243af6ab5fSopenharmony_ci    DEFAULT_TESTCASES_DIR, SELECTED_PARAMETERS, JS_FILE_SUFFIX, TS_FILE_SUFFIX, REPORT_DIR, TEST_RESULT_FILE, \
253af6ab5fSopenharmony_ci    DEFAULT_OUTPUT_DIR, ABC_FILE_SUFFIX, DEFAULT_HERMES_PARAMS, DEFAULT_HERMES_PATH, HERMES_FILE_SUFFIX, \
263af6ab5fSopenharmony_ci    HERMES_CODE_PATH, ES2ABC, HERMES
273af6ab5fSopenharmony_cifrom utils import traverse_dir, write_result, write_html, run_cmd_cwd, clear_folder_shutil, pull_cases, remove_dir, \
283af6ab5fSopenharmony_ci    pull_build_hermes
293af6ab5fSopenharmony_ci
303af6ab5fSopenharmony_ci
313af6ab5fSopenharmony_cidef parse_args():
323af6ab5fSopenharmony_ci    parser = argparse.ArgumentParser()
333af6ab5fSopenharmony_ci    parser.add_argument('--es2abc-tool',
343af6ab5fSopenharmony_ci                        required=True,
353af6ab5fSopenharmony_ci                        help="es2abc tool path")
363af6ab5fSopenharmony_ci    parser.add_argument('--runs', required=False, type=int,
373af6ab5fSopenharmony_ci                        default=DEFAULT_RUNS,
383af6ab5fSopenharmony_ci                        help='Number of runs')
393af6ab5fSopenharmony_ci    parser.add_argument('--opt-level', required=False, choices=['0', '1', '2'],
403af6ab5fSopenharmony_ci                        help="Specifies the compiler optimization level")
413af6ab5fSopenharmony_ci    parser.add_argument('--parse-only', required=False,
423af6ab5fSopenharmony_ci                        default=False, action='store_true',
433af6ab5fSopenharmony_ci                        help="During the test, only the input file is parsed")
443af6ab5fSopenharmony_ci    parser.add_argument('--engine-comp', required=False,
453af6ab5fSopenharmony_ci                        default=False, action='store_true',
463af6ab5fSopenharmony_ci                        help="Compared to the other engine")
473af6ab5fSopenharmony_ci    arguments = parser.parse_args()
483af6ab5fSopenharmony_ci    return arguments
493af6ab5fSopenharmony_ci
503af6ab5fSopenharmony_ci
513af6ab5fSopenharmony_ciclass Es2AbcBenchMarks:
523af6ab5fSopenharmony_ci    def __init__(self, args):
533af6ab5fSopenharmony_ci        self.args = args
543af6ab5fSopenharmony_ci        self.results = []
553af6ab5fSopenharmony_ci        self.cmds = {}
563af6ab5fSopenharmony_ci        self.select_params = []
573af6ab5fSopenharmony_ci
583af6ab5fSopenharmony_ci    def parse_args(self):
593af6ab5fSopenharmony_ci        cmds = {ES2ABC: [self.args.es2abc_tool]}
603af6ab5fSopenharmony_ci        if self.args.parse_only and not self.args.engine_comp:
613af6ab5fSopenharmony_ci            cmds[ES2ABC].append('--parse-only')
623af6ab5fSopenharmony_ci            self.select_params.append(f'{PARSE_ONLY}:True')
633af6ab5fSopenharmony_ci        else:
643af6ab5fSopenharmony_ci            self.select_params.append(f'{PARSE_ONLY}:False')
653af6ab5fSopenharmony_ci        if self.args.opt_level and not self.args.engine_comp:
663af6ab5fSopenharmony_ci            cmds[ES2ABC].append(f'--opt-level {self.args.opt_level}')
673af6ab5fSopenharmony_ci            self.select_params.append(f'{OPT_LEVEL}:{self.args.opt_level}')
683af6ab5fSopenharmony_ci        else:
693af6ab5fSopenharmony_ci            self.select_params.append(f'{OPT_LEVEL}:0')
703af6ab5fSopenharmony_ci        self.select_params.append(f'{RUNS_NUM}:{self.args.runs}')
713af6ab5fSopenharmony_ci        cmds[ES2ABC] += DEFAULT_PARAMS
723af6ab5fSopenharmony_ci        if self.args.engine_comp:
733af6ab5fSopenharmony_ci            cmds[HERMES] = [DEFAULT_HERMES_PATH] + DEFAULT_HERMES_PARAMS
743af6ab5fSopenharmony_ci            self.select_params.append(f'{COMP}:True')
753af6ab5fSopenharmony_ci        else:
763af6ab5fSopenharmony_ci            self.select_params.append(f'{COMP}:False')
773af6ab5fSopenharmony_ci        self.cmds = cmds
783af6ab5fSopenharmony_ci
793af6ab5fSopenharmony_ci    def run(self):
803af6ab5fSopenharmony_ci        test_case_path = os.path.join(CUR_FILE_DIR, DEFAULT_TESTCASES_DIR)
813af6ab5fSopenharmony_ci        test_cases_files = traverse_dir(test_case_path)
823af6ab5fSopenharmony_ci        self.parse_args()
833af6ab5fSopenharmony_ci        html_data = {}
843af6ab5fSopenharmony_ci        selected_params = f'{SELECTED_PARAMETERS}: {"; ".join(self.select_params)}'
853af6ab5fSopenharmony_ci        write_result(selected_params)
863af6ab5fSopenharmony_ci        for dir_name, file_paths in test_cases_files.items():
873af6ab5fSopenharmony_ci            case_dir_name = os.path.basename(dir_name)
883af6ab5fSopenharmony_ci            case_info_html_data = {}
893af6ab5fSopenharmony_ci            case_info_path = os.path.join(REPORT_DIR, f'{case_dir_name}.html')
903af6ab5fSopenharmony_ci            print(f"---------------------------{case_dir_name}--------------------------------")
913af6ab5fSopenharmony_ci            for engine_type in self.cmds:
923af6ab5fSopenharmony_ci                print(f">>>engine {engine_type}")
933af6ab5fSopenharmony_ci                case_execution_time_sum, means = self.run_benchmark(file_paths, engine_type, case_dir_name,
943af6ab5fSopenharmony_ci                                                                    case_info_html_data)
953af6ab5fSopenharmony_ci                print(f">>>Done. \n")
963af6ab5fSopenharmony_ci                case_execution_time_sum_avg = str(case_execution_time_sum)
973af6ab5fSopenharmony_ci                score_sum = self.args.runs / self.gen_score(means)
983af6ab5fSopenharmony_ci                test_result = [f'{case_dir_name}', case_execution_time_sum_avg, f'{score_sum * 100}'.split('.')[0],
993af6ab5fSopenharmony_ci                               case_info_path, self.args.runs]
1003af6ab5fSopenharmony_ci                if engine_type in html_data:
1013af6ab5fSopenharmony_ci                    html_data[engine_type].append(test_result)
1023af6ab5fSopenharmony_ci                else:
1033af6ab5fSopenharmony_ci                    html_data[engine_type] = [test_result]
1043af6ab5fSopenharmony_ci            write_html(case_info_html_data, case_info_path, selected_params, info=True)
1053af6ab5fSopenharmony_ci            print("-------------------------------------------------------------------\n")
1063af6ab5fSopenharmony_ci        write_html(html_data, os.path.join(REPORT_DIR, TEST_RESULT_FILE), selected_params, info=False)
1073af6ab5fSopenharmony_ci
1083af6ab5fSopenharmony_ci    def run_benchmark(self, file_paths, engine_type, case_dir_name, case_info_html_data):
1093af6ab5fSopenharmony_ci        case_execution_time_sum = 0
1103af6ab5fSopenharmony_ci        means = []
1113af6ab5fSopenharmony_ci        for file_path in file_paths:
1123af6ab5fSopenharmony_ci            file_name = os.path.basename(file_path)
1133af6ab5fSopenharmony_ci            if not (file_name.endswith(JS_FILE_SUFFIX) or file_name.endswith(TS_FILE_SUFFIX)):
1143af6ab5fSopenharmony_ci                continue
1153af6ab5fSopenharmony_ci            print(f'Running {file_name.replace(JS_FILE_SUFFIX, "").replace(TS_FILE_SUFFIX, "")} ')
1163af6ab5fSopenharmony_ci            case_execution_time, case_execution_times = self.run_single_benchmark(file_path, engine_type)
1173af6ab5fSopenharmony_ci            case_execution_time_ms = str(case_execution_time * 1000).split(".")[0]
1183af6ab5fSopenharmony_ci            case_execution_time_sum += int(case_execution_time_ms)
1193af6ab5fSopenharmony_ci            mean = self.gen_score(case_execution_times)
1203af6ab5fSopenharmony_ci            means.append(mean)
1213af6ab5fSopenharmony_ci            score = self.args.runs / mean
1223af6ab5fSopenharmony_ci            score = f'{score * 100}'.split('.')[0]
1233af6ab5fSopenharmony_ci            log_str = (f'engine {engine_type} case: {os.path.join(case_dir_name, file_name)} number of runs: '
1243af6ab5fSopenharmony_ci                       f'{self.args.runs} avg time: {case_execution_time_ms}ms  Score:{score}\n')
1253af6ab5fSopenharmony_ci            single_case_result = [f'{os.path.join(case_dir_name, file_name)}', case_execution_time_ms, score,
1263af6ab5fSopenharmony_ci                                  self.args.runs]
1273af6ab5fSopenharmony_ci            if engine_type in case_info_html_data:
1283af6ab5fSopenharmony_ci                case_info_html_data[engine_type].append(single_case_result)
1293af6ab5fSopenharmony_ci            else:
1303af6ab5fSopenharmony_ci                case_info_html_data[engine_type] = [single_case_result]
1313af6ab5fSopenharmony_ci            write_result(log_str)
1323af6ab5fSopenharmony_ci        return case_execution_time_sum, means
1333af6ab5fSopenharmony_ci
1343af6ab5fSopenharmony_ci    def run_single_benchmark(self, file_path, engine_type):
1353af6ab5fSopenharmony_ci        elapsed_sum = 0
1363af6ab5fSopenharmony_ci        case_execution_times = []
1373af6ab5fSopenharmony_ci        new_cmds = [i for i in self.cmds[engine_type]]
1383af6ab5fSopenharmony_ci        output_file = os.path.join(DEFAULT_OUTPUT_DIR, os.path.basename(file_path).
1393af6ab5fSopenharmony_ci                                   replace(JS_FILE_SUFFIX, ABC_FILE_SUFFIX).
1403af6ab5fSopenharmony_ci                                   replace(TS_FILE_SUFFIX, ABC_FILE_SUFFIX))
1413af6ab5fSopenharmony_ci        if engine_type == ES2ABC:
1423af6ab5fSopenharmony_ci            new_cmds += [file_path, "--output", output_file]
1433af6ab5fSopenharmony_ci        else:
1443af6ab5fSopenharmony_ci            new_cmds += [output_file.replace(ABC_FILE_SUFFIX, HERMES_FILE_SUFFIX), file_path]
1453af6ab5fSopenharmony_ci        for _ in range(self.args.runs):
1463af6ab5fSopenharmony_ci            start = time.time()
1473af6ab5fSopenharmony_ci            run_cmd_cwd(new_cmds)
1483af6ab5fSopenharmony_ci            elapsed_sum += time.time() - start
1493af6ab5fSopenharmony_ci            case_execution_times.append(elapsed_sum)
1503af6ab5fSopenharmony_ci        return elapsed_sum / self.args.runs, case_execution_times
1513af6ab5fSopenharmony_ci
1523af6ab5fSopenharmony_ci    @staticmethod
1533af6ab5fSopenharmony_ci    def gen_score(numbers):
1543af6ab5fSopenharmony_ci        log = 0
1553af6ab5fSopenharmony_ci        for num in numbers:
1563af6ab5fSopenharmony_ci            log += math.log(num)
1573af6ab5fSopenharmony_ci        mean = math.exp(log / len(numbers))
1583af6ab5fSopenharmony_ci        return mean
1593af6ab5fSopenharmony_ci
1603af6ab5fSopenharmony_ci
1613af6ab5fSopenharmony_cidef prepare_and_run(args):
1623af6ab5fSopenharmony_ci    clear_folder_shutil(DEFAULT_OUTPUT_DIR)
1633af6ab5fSopenharmony_ci    clear_folder_shutil(REPORT_DIR)
1643af6ab5fSopenharmony_ci    clear_folder_shutil(DEFAULT_TESTCASES_DIR)
1653af6ab5fSopenharmony_ci    pull_cases_success = pull_cases()
1663af6ab5fSopenharmony_ci    if pull_cases_success:
1673af6ab5fSopenharmony_ci        abc_runner = Es2AbcBenchMarks(args)
1683af6ab5fSopenharmony_ci        abc_runner.run()
1693af6ab5fSopenharmony_ci        remove_dir(DEFAULT_OUTPUT_DIR)
1703af6ab5fSopenharmony_ci    print("> > > Done.")
1713af6ab5fSopenharmony_ci
1723af6ab5fSopenharmony_ci
1733af6ab5fSopenharmony_cidef main():
1743af6ab5fSopenharmony_ci    args = parse_args()
1753af6ab5fSopenharmony_ci    print("> > > benchmark running")
1763af6ab5fSopenharmony_ci    if args.engine_comp:
1773af6ab5fSopenharmony_ci        clear_folder_shutil(HERMES_CODE_PATH)
1783af6ab5fSopenharmony_ci        print("Building Hermes...")
1793af6ab5fSopenharmony_ci        build_result = pull_build_hermes()
1803af6ab5fSopenharmony_ci        if not build_result:
1813af6ab5fSopenharmony_ci            raise Exception('build hermes failed.')
1823af6ab5fSopenharmony_ci    prepare_and_run(args)
1833af6ab5fSopenharmony_ci
1843af6ab5fSopenharmony_ci
1853af6ab5fSopenharmony_ciif __name__ == "__main__":
1863af6ab5fSopenharmony_ci    main()
187