Skip to content

Commit 2586a98

Browse files
authored
Merge pull request #2806 from AlexandreSinger/feature-ap-global-placement
[AP] Global Placer
2 parents 113655f + fe92b3e commit 2586a98

File tree

5 files changed

+285
-17
lines changed

5 files changed

+285
-17
lines changed

vpr/src/analytical_place/analytical_placement_flow.cpp

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include "atom_netlist.h"
1313
#include "full_legalizer.h"
1414
#include "gen_ap_netlist_from_atoms.h"
15+
#include "global_placer.h"
1516
#include "globals.h"
1617
#include "partial_legalizer.h"
1718
#include "partial_placement.h"
@@ -58,7 +59,6 @@ static void print_ap_netlist_stats(const APNetlist& netlist) {
5859
}
5960

6061
void run_analytical_placement_flow(t_vpr_setup& vpr_setup) {
61-
(void)vpr_setup;
6262
// Start an overall timer for the Analytical Placement flow.
6363
vtr::ScopedStartFinishTimer timer("Analytical Placement");
6464

@@ -79,16 +79,9 @@ void run_analytical_placement_flow(t_vpr_setup& vpr_setup) {
7979
print_ap_netlist_stats(ap_netlist);
8080

8181
// Run the Global Placer
82-
// For now, just runs the solver and partial legalizer 10 times arbitrarily.
83-
PartialPlacement p_placement(ap_netlist);
84-
std::unique_ptr<AnalyticalSolver> solver = make_analytical_solver(e_analytical_solver::QP_HYBRID,
85-
ap_netlist);
86-
std::unique_ptr<PartialLegalizer> legalizer = make_partial_legalizer(e_partial_legalizer::FLOW_BASED,
87-
ap_netlist);
88-
for (size_t i = 0; i < 10; i++) {
89-
solver->solve(i, p_placement);
90-
legalizer->legalize(p_placement);
91-
}
82+
std::unique_ptr<GlobalPlacer> global_placer = make_global_placer(e_global_placer::SimPL,
83+
ap_netlist);
84+
PartialPlacement p_placement = global_placer->place();
9285

9386
// Verify that the partial placement is valid before running the full
9487
// legalizer.

vpr/src/analytical_place/full_legalizer.cpp

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -394,8 +394,6 @@ void FullLegalizer::place_clusters(const ClusteredNetlist& clb_nlist,
394394

395395
// FIXME: Allocate and load moveable blocks?
396396
// - This may be needed to perform SA. Not needed right now.
397-
398-
// TODO: Check initial placement legality
399397
}
400398

401399
void FullLegalizer::legalize(const PartialPlacement& p_placement) {
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
/**
2+
* @file
3+
* @author Alex Singer
4+
* @date October 2024
5+
* @brief The definitions of the global placers used in the AP flow and their
6+
* base class.
7+
*/
8+
9+
#include "global_placer.h"
10+
#include <cstdio>
11+
#include <memory>
12+
#include "analytical_solver.h"
13+
#include "ap_netlist.h"
14+
#include "partial_legalizer.h"
15+
#include "partial_placement.h"
16+
#include "vpr_error.h"
17+
#include "vtr_log.h"
18+
#include "vtr_time.h"
19+
20+
std::unique_ptr<GlobalPlacer> make_global_placer(e_global_placer placer_type,
21+
const APNetlist& netlist) {
22+
// Based on the placer type passed in, build the global placer.
23+
switch (placer_type) {
24+
case e_global_placer::SimPL:
25+
return std::make_unique<SimPLGlobalPlacer>(netlist);
26+
default:
27+
VPR_FATAL_ERROR(VPR_ERROR_AP,
28+
"Unrecognized global placer type");
29+
30+
}
31+
}
32+
33+
SimPLGlobalPlacer::SimPLGlobalPlacer(const APNetlist& netlist) : GlobalPlacer(netlist) {
34+
// This can be a long method. Good to time this to see how long it takes to
35+
// construct the global placer.
36+
vtr::ScopedStartFinishTimer global_placer_building_timer("Constructing Global Placer");
37+
// Build the solver.
38+
solver_ = make_analytical_solver(e_analytical_solver::QP_HYBRID,
39+
netlist);
40+
// Build the partial legalizer
41+
partial_legalizer_ = make_partial_legalizer(e_partial_legalizer::FLOW_BASED,
42+
netlist);
43+
}
44+
45+
/**
46+
* @brief Helper method to print the header of the per-iteration status updates
47+
* of the global placer.
48+
*/
49+
static void print_SimPL_status_header() {
50+
VTR_LOG("---- ---------------- ---------------- ----------- -------------- ----------\n");
51+
VTR_LOG("Iter Lower Bound HPWL Upper Bound HPWL Solver Time Legalizer Time Total Time\n");
52+
VTR_LOG(" (sec) (sec) (sec)\n");
53+
VTR_LOG("---- ---------------- ---------------- ----------- -------------- ----------\n");
54+
}
55+
56+
/**
57+
* @brief Helper method to print the per-iteration status of the global placer.
58+
*/
59+
static void print_SimPL_status(size_t iteration,
60+
double lb_hpwl,
61+
double ub_hpwl,
62+
float solver_time,
63+
float legalizer_time,
64+
float total_time) {
65+
// Iteration
66+
VTR_LOG("%4zu", iteration);
67+
68+
// Lower Bound HPWL
69+
VTR_LOG(" %16.2f", lb_hpwl);
70+
71+
// Upper Bound HPWL
72+
VTR_LOG(" %16.2f", ub_hpwl);
73+
74+
// Solver runtime
75+
VTR_LOG(" %11.3f", solver_time);
76+
77+
// Legalizer runtime
78+
VTR_LOG(" %14.3f", legalizer_time);
79+
80+
// Total runtime
81+
VTR_LOG(" %10.3f", total_time);
82+
83+
VTR_LOG("\n");
84+
85+
fflush(stdout);
86+
}
87+
88+
PartialPlacement SimPLGlobalPlacer::place() {
89+
// Create a timer to time the entire global placement time.
90+
vtr::ScopedStartFinishTimer global_placer_time("AP Global Placer");
91+
// Create a timer to keep track of how long the solver and legalizer take.
92+
vtr::Timer runtime_timer;
93+
// Print the status header.
94+
if (log_verbosity_ >= 1)
95+
print_SimPL_status_header();
96+
// Initialialize the partial placement object.
97+
PartialPlacement p_placement(netlist_);
98+
// Run the global placer.
99+
for (size_t i = 0; i < max_num_iterations_; i++) {
100+
float iter_start_time = runtime_timer.elapsed_sec();
101+
102+
// Run the solver.
103+
float solver_start_time = runtime_timer.elapsed_sec();
104+
solver_->solve(i, p_placement);
105+
float solver_end_time = runtime_timer.elapsed_sec();
106+
double lb_hpwl = p_placement.get_hpwl(netlist_);
107+
108+
// Run the legalizer.
109+
float legalizer_start_time = runtime_timer.elapsed_sec();
110+
partial_legalizer_->legalize(p_placement);
111+
float legalizer_end_time = runtime_timer.elapsed_sec();
112+
double ub_hpwl = p_placement.get_hpwl(netlist_);
113+
114+
// Print some stats
115+
if (log_verbosity_ >= 1) {
116+
float iter_end_time = runtime_timer.elapsed_sec();
117+
print_SimPL_status(i, lb_hpwl, ub_hpwl,
118+
solver_end_time - solver_start_time,
119+
legalizer_end_time - legalizer_start_time,
120+
iter_end_time - iter_start_time);
121+
}
122+
123+
// Exit condition: If the upper-bound and lower-bound HPWLs are
124+
// sufficiently close together then stop.
125+
double hpwl_relative_gap = (ub_hpwl - lb_hpwl) / ub_hpwl;
126+
if (hpwl_relative_gap < target_hpwl_relative_gap_)
127+
break;
128+
}
129+
// Return the placement from the final iteration.
130+
// TODO: investigate saving the best solution found so far. It should be
131+
// cheap to save a copy of the PartialPlacement object.
132+
return p_placement;
133+
}
134+
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
/**
2+
* @file
3+
* @author Alex Singer
4+
* @date October 2024
5+
* @brief The declarations of the Global Placer base class which is used to
6+
* define the functionality of all global placers in the AP flow.
7+
*
8+
* A Global Placer creates a Partial Placement given only the netlist and the
9+
* architecture. It uses analytical techniques (i.e. efficient numerical
10+
* minimization of an objective function of a placement) to find a placement
11+
* that optimizes for objectives subject to some of the constraints of the FPGA
12+
* architecture.
13+
*/
14+
15+
#pragma once
16+
17+
#include <memory>
18+
19+
// Forward declarations
20+
class APNetlist;
21+
class AnalyticalSolver;
22+
class PartialPlacement;
23+
class PartialLegalizer;
24+
25+
/**
26+
* @brief Enumeration of all of the global placers currently implemented in VPR.
27+
*/
28+
enum class e_global_placer {
29+
SimPL // Global placer based on the SimPL paper.
30+
};
31+
32+
/**
33+
* @brief The Global Placer base class
34+
*
35+
* This declares the functionality that all Global Placers will use. This
36+
* provides a standard interface for the global placers so they can be used
37+
* interchangably. This makes it very easy to test and compare different global
38+
* placers.
39+
*/
40+
class GlobalPlacer {
41+
public:
42+
virtual ~GlobalPlacer() {}
43+
44+
/**
45+
* @brief Constructor of the base GlobalPlacer class
46+
*
47+
* @param netlist Netlist of the design at some abstraction level;
48+
* typically this would have some atoms and groups of
49+
* atoms (in a pack pattern).
50+
* @param log_verbosity The verbosity of log messages in the Global
51+
* Placer.
52+
*/
53+
GlobalPlacer(const APNetlist& netlist, int log_verbosity = 1)
54+
: netlist_(netlist),
55+
log_verbosity_(log_verbosity) {}
56+
57+
/**
58+
* @brief Perform global placement on the given netlist.
59+
*
60+
* The role of a global placer is to try and find a placement for the given
61+
* netlist which optimizes some objective function and is mostly legal.
62+
*/
63+
virtual PartialPlacement place() = 0;
64+
65+
protected:
66+
67+
/// @brief The APNetlist the global placer is placing.
68+
const APNetlist& netlist_;
69+
70+
/// @brief The setting of how verbose the log messages should be in the
71+
/// global placer. Anything larger than zero will display per
72+
/// iteration status messages.
73+
int log_verbosity_;
74+
};
75+
76+
/**
77+
* @brief A factory method which creates a Global Placer of the given type.
78+
*/
79+
std::unique_ptr<GlobalPlacer> make_global_placer(e_global_placer placer_type,
80+
const APNetlist& netlist);
81+
82+
/**
83+
* @brief A Global Placer based on the SimPL work for analytical ASIC placement.
84+
* https://doi.org/10.1145/2461256.2461279
85+
*
86+
* This placement technique uses a solver to generate a placement that optimizes
87+
* over some objective function and is likely very illegal (has many overlapping
88+
* blocks and blocks in the wrong places). This solution represents the "lower-
89+
* bound" on the solution quality.
90+
*
91+
* This technique passes this "lower-bound" solution into a legalizer, which
92+
* tries to find the closest legal solution to the lower-bound solution (by
93+
* spreading out blocks and placing them in legal positions). This often
94+
* destroys the quality of the lower-bound solution, and is considered an
95+
* "upper-bound" on the solution quality.
96+
*
97+
* Each iteration of this global placer, the upper-bound solution is fed into
98+
* the solver as a "hint" to what a legal solution looks like. This allows the
99+
* solver to produce another placement which will make decisions knowing where
100+
* the blocks will end-up in the legal solution. This worstens the quality of
101+
* the lower-bound solution; however, after passing this solution back into
102+
* the legalizer, this will likely improve the quality of the upper-bound
103+
* solution.
104+
*
105+
* Over several iterations the upper-bound and lower-bound solutions will
106+
* approach each other until a good quality, mostly-legal solution is found.
107+
*/
108+
class SimPLGlobalPlacer : public GlobalPlacer {
109+
private:
110+
111+
/// @brief The maximum number of iterations the global placer can perform.
112+
static constexpr size_t max_num_iterations_ = 100;
113+
114+
/// @brief The target relative gap between the HPWL of the upper-bound and
115+
/// lower-bound placements. The placer will stop if the difference
116+
/// between the two bounds, normalized to the upper-bound, is smaller
117+
/// than this number.
118+
static constexpr double target_hpwl_relative_gap_ = 0.10;
119+
120+
/// @brief The solver which generates the lower-bound placement.
121+
std::unique_ptr<AnalyticalSolver> solver_;
122+
123+
/// @brief The legalizer which generates the upper-bound placement.
124+
std::unique_ptr<PartialLegalizer> partial_legalizer_;
125+
126+
public:
127+
128+
/**
129+
* @brief Constructor for the SimPL Global Placer
130+
*
131+
* Constructs the solver and partial legalizer.
132+
*/
133+
SimPLGlobalPlacer(const APNetlist& netlist);
134+
135+
/**
136+
* @brief Run a SimPL-like global placement algorithm
137+
*
138+
* This iteratively runs the solver and legalizer until a good quality and
139+
* mostly-legal placement is found.
140+
*/
141+
PartialPlacement place() final;
142+
};
143+
Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
arch circuit script_params vtr_flow_elapsed_time vtr_max_mem_stage vtr_max_mem error odin_synth_time max_odin_mem parmys_synth_time max_parmys_mem abc_depth abc_synth_time abc_cec_time abc_sec_time max_abc_mem ace_time max_ace_mem num_clb num_io num_memories num_mult vpr_status vpr_revision vpr_build_info vpr_compiler vpr_compiled hostname rundir max_vpr_mem num_primary_inputs num_primary_outputs num_pre_packed_nets num_pre_packed_blocks num_netlist_clocks num_post_packed_nets num_post_packed_blocks device_width device_height device_grid_tiles device_limiting_resources device_name pack_mem pack_time placed_wirelength_est total_swap accepted_swap rejected_swap aborted_swap place_mem place_time place_quench_time placed_CPD_est placed_setup_TNS_est placed_setup_WNS_est placed_geomean_nonvirtual_intradomain_critical_path_delay_est place_delay_matrix_lookup_time place_quench_timing_analysis_time place_quench_sta_time place_total_timing_analysis_time place_total_sta_time ap_mem ap_time ap_full_legalizer_mem ap_full_legalizer_time min_chan_width routed_wirelength min_chan_width_route_success_iteration logic_block_area_total logic_block_area_used min_chan_width_routing_area_total min_chan_width_routing_area_per_tile min_chan_width_route_time min_chan_width_total_timing_analysis_time min_chan_width_total_sta_time crit_path_num_rr_graph_nodes crit_path_num_rr_graph_edges crit_path_collapsed_nodes crit_path_routed_wirelength crit_path_route_success_iteration crit_path_total_nets_routed crit_path_total_connections_routed crit_path_total_heap_pushes crit_path_total_heap_pops critical_path_delay geomean_nonvirtual_intradomain_critical_path_delay setup_TNS setup_WNS hold_TNS hold_WNS crit_path_routing_area_total crit_path_routing_area_per_tile router_lookahead_computation_time crit_path_route_time crit_path_create_rr_graph_time crit_path_create_intra_cluster_rr_graph_time crit_path_tile_lookahead_computation_time crit_path_router_lookahead_computation_time crit_path_total_timing_analysis_time crit_path_total_sta_time
2-
fixed_k6_frac_N8_22nm.xml single_wire.v common 4.25 vpr 70.91 MiB -1 -1 0.18 16276 1 0.39 -1 -1 29812 -1 -1 0 1 0 0 success v8.0.0-11571-g5eb3aa508 release VTR_ASSERT_LEVEL=3 GNU 9.4.0 on Linux-4.15.0-213-generic x86_64 2024-10-18T00:28:35 betzgrp-wintermute.eecg.utoronto.ca /home/singera8/vtr-verilog-to-routing/vtr_flow/tasks 72608 1 1 0 2 0 1 2 17 17 289 -1 unnamed_device -1 -1 -1 -1 -1 -1 -1 70.9 MiB 0.14 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 70.9 MiB 0.14 70.9 MiB 0.10 8 14 1 6.79088e+06 0 166176. 575.005 0.36 0.00138004 0.00129992 20206 45088 -1 19 1 1 1 194 45 0.7726 nan -0.7726 -0.7726 0 0 202963. 702.294 0.09 0.00 0.07 -1 -1 0.09 0.00122838 0.00119736
3-
fixed_k6_frac_N8_22nm.xml single_ff.v common 4.68 vpr 71.03 MiB -1 -1 0.20 16236 1 0.39 -1 -1 29696 -1 -1 1 2 0 0 success v8.0.0-11571-g5eb3aa508 release VTR_ASSERT_LEVEL=3 GNU 9.4.0 on Linux-4.15.0-213-generic x86_64 2024-10-18T00:28:35 betzgrp-wintermute.eecg.utoronto.ca /home/singera8/vtr-verilog-to-routing/vtr_flow/tasks 72736 2 1 3 3 1 3 4 17 17 289 -1 unnamed_device -1 -1 -1 -1 -1 -1 -1 71.0 MiB 0.14 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 71.0 MiB 0.14 71.0 MiB 0.10 20 31 1 6.79088e+06 13472 414966. 1435.87 0.63 0.00135413 0.0012936 22510 95286 -1 35 1 2 2 213 52 0.942216 0.942216 -1.68896 -0.942216 0 0 503264. 1741.40 0.17 0.00 0.14 -1 -1 0.17 0.00127341 0.00123431
4-
fixed_k6_frac_N8_22nm.xml ch_intrinsics.v common 6.69 vpr 71.67 MiB -1 -1 0.46 18220 3 0.40 -1 -1 33084 -1 -1 40 99 3 0 success v8.0.0-11571-g5eb3aa508 release VTR_ASSERT_LEVEL=3 GNU 9.4.0 on Linux-4.15.0-213-generic x86_64 2024-10-18T00:28:35 betzgrp-wintermute.eecg.utoronto.ca /home/singera8/vtr-verilog-to-routing/vtr_flow/tasks 73388 99 130 240 229 1 247 272 17 17 289 -1 unnamed_device -1 -1 -1 -1 -1 -1 -1 71.7 MiB 0.28 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 71.7 MiB 0.28 71.7 MiB 0.21 32 3122 15 6.79088e+06 2.18288e+06 586450. 2029.24 1.84 0.271358 0.247517 24814 144142 -1 2952 30 711 1121 349988 188928 2.0466 2.0466 -154.346 -2.0466 -0.04337 -0.04337 744469. 2576.02 0.25 0.25 0.22 -1 -1 0.25 0.102379 0.0937273
5-
fixed_k6_frac_N8_22nm.xml diffeq1.v common 32.17 vpr 74.24 MiB -1 -1 0.75 23104 15 0.61 -1 -1 34204 -1 -1 74 162 0 5 success v8.0.0-11571-g5eb3aa508 release VTR_ASSERT_LEVEL=3 GNU 9.4.0 on Linux-4.15.0-213-generic x86_64 2024-10-18T00:28:35 betzgrp-wintermute.eecg.utoronto.ca /home/singera8/vtr-verilog-to-routing/vtr_flow/tasks 76024 162 96 817 258 1 797 337 17 17 289 -1 unnamed_device -1 -1 -1 -1 -1 -1 -1 74.2 MiB 1.01 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 74.2 MiB 1.01 74.2 MiB 0.60 60 15916 46 6.79088e+06 2.97693e+06 1.01997e+06 3529.29 24.68 3.56948 3.3772 29998 257685 -1 13617 19 4413 11558 1499556 342325 21.9657 21.9657 -1806.56 -21.9657 0 0 1.27783e+06 4421.56 0.40 0.79 0.44 -1 -1 0.40 0.334496 0.31821
2+
fixed_k6_frac_N8_22nm.xml single_wire.v common 2.23 vpr 70.88 MiB -1 -1 0.13 15888 1 0.16 -1 -1 29628 -1 -1 0 1 0 0 success v8.0.0-11573-g5ea68eac9 release VTR_ASSERT_LEVEL=3 GNU 9.4.0 on Linux-4.15.0-213-generic x86_64 2024-10-18T14:02:52 betzgrp-wintermute.eecg.utoronto.ca /home/singera8/vtr-verilog-to-routing/vtr_flow/tasks 72576 1 1 0 2 0 1 2 17 17 289 -1 unnamed_device -1 -1 -1 -1 -1 -1 -1 70.9 MiB 0.13 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 70.9 MiB 0.13 70.9 MiB 0.09 8 14 1 6.79088e+06 0 166176. 575.005 0.37 0.001421 0.00133975 20206 45088 -1 19 1 1 1 194 45 0.7726 nan -0.7726 -0.7726 0 0 202963. 702.294 0.09 0.00 0.07 -1 -1 0.09 0.00128701 0.00125353
3+
fixed_k6_frac_N8_22nm.xml single_ff.v common 2.68 vpr 70.80 MiB -1 -1 0.14 16336 1 0.16 -1 -1 29640 -1 -1 1 2 0 0 success v8.0.0-11573-g5ea68eac9 release VTR_ASSERT_LEVEL=3 GNU 9.4.0 on Linux-4.15.0-213-generic x86_64 2024-10-18T14:02:52 betzgrp-wintermute.eecg.utoronto.ca /home/singera8/vtr-verilog-to-routing/vtr_flow/tasks 72500 2 1 3 3 1 3 4 17 17 289 -1 unnamed_device -1 -1 -1 -1 -1 -1 -1 70.8 MiB 0.14 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 70.8 MiB 0.14 70.8 MiB 0.09 20 31 1 6.79088e+06 13472 414966. 1435.87 0.65 0.00147526 0.00140277 22510 95286 -1 35 1 2 2 213 52 0.942216 0.942216 -1.68896 -0.942216 0 0 503264. 1741.40 0.18 0.00 0.15 -1 -1 0.18 0.00127433 0.00123146
4+
fixed_k6_frac_N8_22nm.xml ch_intrinsics.v common 4.51 vpr 71.77 MiB -1 -1 0.47 18200 3 0.09 -1 -1 33188 -1 -1 41 99 3 0 success v8.0.0-11573-g5ea68eac9 release VTR_ASSERT_LEVEL=3 GNU 9.4.0 on Linux-4.15.0-213-generic x86_64 2024-10-18T14:02:52 betzgrp-wintermute.eecg.utoronto.ca /home/singera8/vtr-verilog-to-routing/vtr_flow/tasks 73496 99 130 240 229 1 247 273 17 17 289 -1 unnamed_device -1 -1 -1 -1 -1 -1 -1 71.8 MiB 0.30 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 71.8 MiB 0.30 71.8 MiB 0.23 32 3145 19 6.79088e+06 2.19635e+06 586450. 2029.24 1.54 0.232941 0.21265 24814 144142 -1 2897 11 635 1003 105957 24691 2.0466 2.0466 -155.681 -2.0466 -0.21204 -0.16867 744469. 2576.02 0.27 0.09 0.22 -1 -1 0.27 0.046062 0.042439
5+
fixed_k6_frac_N8_22nm.xml diffeq1.v common 30.51 vpr 74.25 MiB -1 -1 0.73 23000 15 0.35 -1 -1 34316 -1 -1 65 162 0 5 success v8.0.0-11573-g5ea68eac9 release VTR_ASSERT_LEVEL=3 GNU 9.4.0 on Linux-4.15.0-213-generic x86_64 2024-10-18T14:02:52 betzgrp-wintermute.eecg.utoronto.ca /home/singera8/vtr-verilog-to-routing/vtr_flow/tasks 76028 162 96 817 258 1 792 328 17 17 289 -1 unnamed_device -1 -1 -1 -1 -1 -1 -1 74.2 MiB 1.81 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 74.2 MiB 1.81 74.2 MiB 0.61 68 16708 27 6.79088e+06 2.85568e+06 1.14541e+06 3963.36 22.39 3.52495 3.33579 31438 289477 -1 14397 19 4042 10567 1489293 328107 22.3059 22.3059 -1909.12 -22.3059 0 0 1.42693e+06 4937.46 0.51 0.78 0.51 -1 -1 0.51 0.316463 0.300694

0 commit comments

Comments
 (0)