⚠ Archived content — this site is no longer maintained.   Current WebKit documentation is at docs.webkit.org.

Changeset 236996 in webkit


Ignore:
Timestamp:
Oct 9, 2018, 8:15:39 PM (8 years ago)
Author:
rniwa@webkit.org
Message:

ManifestGenerator shouldn't need more than 1GB of memory or run for 30 seconds
https://bugs.webkit.org/show_bug.cgi?id=190393

Reviewed by Antti Koivisto and unofficially reviewed by Dewei Zhu.

This patch reduces the runtime of /api/manifest from 13s to 7s and reduces the memory requirement from
1GB to 400MB for the internal dashboard in my local testing.

The biggest perf win comes from avoid running a complex query over test_configurations to compute
the latest modified date across different test configuration types ("current" vs. "baseline").
Instead, we now fetch the entire table row by row and compute the latest modified date in memory.

Also call intval in many more places to avoid generating double quotes, which is a pretty significant
proportion of the JSON file size at this point.

Finally, use references in more places to avoid deep copying of arrays.

  • public/include/manifest-generator.php:

(ManifestGenerator::generate): Skip the generation of "dashboard" since it's only used by v1 UI.
(ManifestGenerator::tests): Don't copy each row.
(ManifestGenerator::metrics): Ditto.
(ManifestGenerator::platforms): Implement the aforementioned optimization. Instead of grouping
test configurations for a given metric and platform together, fetch them all and do in-memory
processing in PHP. Avoid more copying as well.
(ManifestGenerator::repositories): Avoid more copying.
(ManifestGenerator::bug_trackers): Ditto.
(ManifestGenerator::fetch_triggerables): Ditto.

Location:
trunk/Websites/perf.webkit.org
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/Websites/perf.webkit.org/ChangeLog

    r236956 r236996  
     12018-10-09  Ryosuke Niwa  <rniwa@webkit.org>
     2
     3        ManifestGenerator shouldn't need more than 1GB of memory or run for 30 seconds
     4        https://bugs.webkit.org/show_bug.cgi?id=190393
     5
     6        Reviewed by Antti Koivisto and unofficially reviewed by Dewei Zhu.
     7
     8        This patch reduces the runtime of /api/manifest from 13s to 7s and reduces the memory requirement from
     9        1GB to 400MB for the internal dashboard in my local testing.
     10
     11        The biggest perf win comes from avoid running a complex query over test_configurations to compute
     12        the latest modified date across different test configuration types ("current" vs. "baseline").
     13        Instead, we now fetch the entire table row by row and compute the latest modified date in memory.
     14
     15        Also call intval in many more places to avoid generating double quotes, which is a pretty significant
     16        proportion of the JSON file size at this point.
     17
     18        Finally, use references in more places to avoid deep copying of arrays.
     19
     20        * public/include/manifest-generator.php:
     21        (ManifestGenerator::generate): Skip the generation of "dashboard" since it's only used by v1 UI.
     22        (ManifestGenerator::tests): Don't copy each row.
     23        (ManifestGenerator::metrics): Ditto.
     24        (ManifestGenerator::platforms): Implement the aforementioned optimization. Instead of grouping
     25        test configurations for a given metric and platform together, fetch them all and do in-memory
     26        processing in PHP. Avoid more copying as well.
     27        (ManifestGenerator::repositories): Avoid more copying.
     28        (ManifestGenerator::bug_trackers): Ditto.
     29        (ManifestGenerator::fetch_triggerables): Ditto.
     30
    1312018-10-08  Ryosuke Niwa  <rniwa@webkit.org>
    232
  • trunk/Websites/perf.webkit.org/public/include/manifest-generator.php

    r236454 r236996  
    3030        $metrics = (object)$this->metrics();
    3131        $platforms = (object)$this->platforms($platform_table, false);
    32         $dashboard = (object)$this->platforms($platform_table, true);
    3332        $repositories = (object)$this->repositories($repositories_table, $repositories_with_commit);
    3433
     
    3837            'metrics' => &$metrics,
    3938            'all' => &$platforms,
    40             'dashboard' => &$dashboard,
     39            'dashboard' => (object)array(), // Only used by v1 UI.
    4140            'repositories' => &$repositories,
    4241            'builders' => (object)$this->builders(),
     
    6564        if (!$tests_table)
    6665            return $tests;
    67         foreach ($tests_table as $test_row) {
     66        foreach ($tests_table as &$test_row) {
    6867            $tests[$test_row['test_id']] = array(
    6968                'name' => $test_row['test_name'],
    7069                'url' => $test_row['test_url'],
    71                 'parentId' => $test_row['test_parent'],
     70                'parentId' => $test_row['test_parent'] ? intval($test_row['test_parent']) : NULL,
    7271            );
    7372        }
     
    8079        if (!$metrics_table)
    8180            return $metrics;
    82         foreach ($metrics_table as $row) {
     81        foreach ($metrics_table as &$row) {
    8382            $metrics[$row['metric_id']] = array(
    8483                'name' => $row['metric_name'],
    85                 'test' => $row['metric_test'],
     84                'test' => intval($row['metric_test']),
    8685                'aggregator' => $row['aggregator_name']);
    8786        }
     
    8988    }
    9089
    91     private function platforms($platform_table, $is_dashboard) {
    92         $metrics = $this->db->query_and_fetch_all('SELECT config_metric AS metric_id, config_platform AS platform_id,
    93             extract(epoch from max(config_runs_last_modified) at time zone \'utc\') * 1000 AS last_modified, bool_or(config_is_in_dashboard) AS in_dashboard
    94             FROM test_configurations GROUP BY config_metric, config_platform ORDER BY config_platform');
     90    private function platforms(&$platform_table, $is_dashboard) {
     91        $config_query = $this->db->query('SELECT config_platform, config_metric,
     92            extract(epoch from config_runs_last_modified at time zone \'utc\') * 1000 AS last_modified
     93            FROM test_configurations');
    9594
    9695        $platform_metrics = array();
    9796
    98         if ($metrics) {
     97        if ($config_query) {
    9998            $current_platform_entry = null;
    100             foreach ($metrics as $metric_row) {
    101                 if ($is_dashboard && !Database::is_true($metric_row['in_dashboard']))
    102                     continue;
    103 
    104                 $platform_id = $metric_row['platform_id'];
    105                 if (!$current_platform_entry || $current_platform_entry['id'] != $platform_id) {
    106                     $current_platform_entry = &array_ensure_item_has_array($platform_metrics, $platform_id);
    107                     $current_platform_entry['id'] = $platform_id;
    108                     array_ensure_item_has_array($current_platform_entry, 'metrics');
    109                     array_ensure_item_has_array($current_platform_entry, 'last_modified');
     99            $last_modified_map = array();
     100            while (1) {
     101                $config_row = $this->db->fetch_next_row($config_query);
     102                if (!$config_row)
     103                    break;
     104
     105                $platform_id = $config_row['config_platform'];
     106                $metric_id = $config_row['config_metric'];
     107                $last_modified = intval($config_row['last_modified']);
     108
     109                $key = $platform_id . '-' . $metric_id;
     110                if (array_key_exists($key, $last_modified_map)) {
     111                    $last_modified_map[$key] = max($last_modified_map[$key], $last_modified);
     112                    continue;
    110113                }
    111 
    112                 array_push($current_platform_entry['metrics'], $metric_row['metric_id']);
    113                 array_push($current_platform_entry['last_modified'], intval($metric_row['last_modified']));
     114                $last_modified_map[$key] = $last_modified;
     115
     116                $current_platform_entry = &array_ensure_item_has_array($platform_metrics, $platform_id);
     117                array_ensure_item_has_array($current_platform_entry, 'metrics');
     118                array_push($current_platform_entry['metrics'], intval($metric_id));
    114119            }
    115120        }
     
    118123        $platforms = array();
    119124        if ($platform_table) {
    120             foreach ($platform_table as $platform_row) {
     125            foreach ($platform_table as &$platform_row) {
    121126                if (Database::is_true($platform_row['platform_hidden']))
    122127                    continue;
    123128                $id = $platform_row['platform_id'];
    124129                if (array_key_exists($id, $platform_metrics)) {
     130                    $metrics = &$platform_metrics[$id]['metrics'];
     131                    $last_modified = array();
     132                    foreach ($metrics as $metric_id)
     133                        array_push($last_modified, $last_modified_map[$id . '-' . $metric_id]);
    125134                    $platforms[$id] = array(
    126                         'name' => $platform_row['platform_name'],
    127                         'metrics' => $platform_metrics[$id]['metrics'],
    128                         'lastModified' => $platform_metrics[$id]['last_modified']);
     135                        'name' => &$platform_row['platform_name'],
     136                        'metrics' => &$metrics,
     137                        'lastModified' => &$last_modified
     138                    );
    129139                }
    130140            }
     
    137147        if (!$repositories_table)
    138148            return $repositories;
    139         foreach ($repositories_table as $row) {
     149        foreach ($repositories_table as &$row) {
    140150            $repositories[$row['repository_id']] = array(
    141151                'name' => $row['repository_name'],
     
    154164            return array();
    155165        $builders = array();
    156         foreach ($builders_table as $row)
     166        foreach ($builders_table as &$row)
    157167            $builders[$row['builder_id']] = array('name' => $row['builder_name'], 'buildUrl' => $row['builder_build_url']);
    158168
     
    173183        $bug_trackers_table = $this->db->fetch_table('bug_trackers');
    174184        if ($bug_trackers_table) {
    175             foreach ($bug_trackers_table as $row) {
     185            foreach ($bug_trackers_table as &$row) {
    176186                $bug_trackers[$row['tracker_id']] = array(
    177187                    'name' => $row['tracker_name'],
     
    217227                array_ensure_item_has_array($repository_set_by_group, $group_id);
    218228                array_push($repository_set_by_group[$group_id], array(
    219                     'repository' => $repository_row['trigrepo_repository'],
     229                    'repository' => intval($repository_row['trigrepo_repository']),
    220230                    'acceptsPatch' => Database::is_true($repository_row['trigrepo_accepts_patch'])));
    221231            }
     
    228238                $repository_list = array_get($repository_set_by_group, $group_id, array());
    229239                array_push($triggerable['repositoryGroups'], array(
    230                     'id' => $group_row['repositorygroup_id'],
     240                    'id' => intval($group_row['repositorygroup_id']),
    231241                    'name' => $group_row['repositorygroup_name'],
    232242                    'description' => $group_row['repositorygroup_description'],
     
    235245                    'repositories' => $repository_list));
    236246                // V2 UI compatibility.
    237                 foreach ($repository_list as $repository_data) {
     247                foreach ($repository_list as &$repository_data) {
    238248                    $repository_id = $repository_data['repository'];
    239249                    $set = &$triggerable_id_to_repository_set[$triggerable_id];
     
    254264                    continue;
    255265                $triggerable = &$id_to_triggerable[$triggerable_id];
    256                 array_push($triggerable['configurations'], array($row['trigconfig_test'], $row['trigconfig_platform']));
     266                array_push($triggerable['configurations'], array(intval($row['trigconfig_test']), intval($row['trigconfig_platform'])));
    257267            }
    258268        }
Note: See TracChangeset for help on using the changeset viewer.