Skip to content

Commit b1b766d

Browse files
[AP][Solver] Supporting Unfixed Blocks
When no fixed blocks are provided by the user, the AP flow can still work. Currently, in the first iteration, the solver will put all blocks at 0,0 and use the legalized solution in the next iteration as fixed points. Instead of (0,0), it makes more sense to put the blocks in the center of the device. Also added a guess to the solver to help CG converge faster each iteration. Added a regression test to ensure that not describing the fixed blocks is supported.
1 parent c9e6075 commit b1b766d

File tree

5 files changed

+186
-16
lines changed

5 files changed

+186
-16
lines changed

vpr/src/analytical_place/analytical_solver.cpp

Lines changed: 91 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
#include <memory>
1313
#include <utility>
1414
#include <vector>
15+
#include "device_grid.h"
16+
#include "flat_placement_types.h"
1517
#include "partial_placement.h"
1618
#include "ap_netlist.h"
1719
#include "vpr_error.h"
@@ -36,12 +38,13 @@
3638
#endif // EIGEN_INSTALLED
3739

3840
std::unique_ptr<AnalyticalSolver> make_analytical_solver(e_analytical_solver solver_type,
39-
const APNetlist& netlist) {
41+
const APNetlist& netlist,
42+
const DeviceGrid& device_grid) {
4043
// Based on the solver type passed in, build the solver.
4144
switch (solver_type) {
4245
case e_analytical_solver::QP_HYBRID:
4346
#ifdef EIGEN_INSTALLED
44-
return std::make_unique<QPHybridSolver>(netlist);
47+
return std::make_unique<QPHybridSolver>(netlist, device_grid);
4548
#else
4649
(void)netlist;
4750
VPR_FATAL_ERROR(VPR_ERROR_AP,
@@ -64,8 +67,11 @@ AnalyticalSolver::AnalyticalSolver(const APNetlist& netlist)
6467
// row ID from [0, num_moveable_blocks) for each moveable block in the
6568
// netlist.
6669
num_moveable_blocks_ = 0;
70+
num_fixed_blocks_ = 0;
6771
size_t current_row_id = 0;
6872
for (APBlockId blk_id : netlist.blocks()) {
73+
if (netlist.block_mobility(blk_id) == APBlockMobility::FIXED)
74+
num_fixed_blocks_++;
6975
if (netlist.block_mobility(blk_id) != APBlockMobility::MOVEABLE)
7076
continue;
7177
APRowId new_row_id = APRowId(current_row_id);
@@ -155,10 +161,10 @@ void QPHybridSolver::init_linear_system() {
155161
}
156162

157163
// Initialize the linear system with zeros.
158-
size_t num_variables = num_moveable_blocks_ + num_star_nodes;
159-
A_sparse = Eigen::SparseMatrix<double>(num_variables, num_variables);
160-
b_x = Eigen::VectorXd::Zero(num_variables);
161-
b_y = Eigen::VectorXd::Zero(num_variables);
164+
num_variables_ = num_moveable_blocks_ + num_star_nodes;
165+
A_sparse = Eigen::SparseMatrix<double>(num_variables_, num_variables_);
166+
b_x = Eigen::VectorXd::Zero(num_variables_);
167+
b_y = Eigen::VectorXd::Zero(num_variables_);
162168

163169
// Create a list of triplets that will be used to create the sparse
164170
// coefficient matrix. This is the method recommended by Eigen to initialize
@@ -254,7 +260,55 @@ void QPHybridSolver::update_linear_system_with_anchors(
254260
}
255261
}
256262

263+
void QPHybridSolver::init_guesses(const DeviceGrid& device_grid) {
264+
// If the number of fixed blocks is zero, initialized the guesses to the
265+
// center of the device.
266+
if (num_fixed_blocks_ == 0) {
267+
guess_x = Eigen::VectorXd::Constant(num_variables_, device_grid.width() / 2.0);
268+
guess_y = Eigen::VectorXd::Constant(num_variables_, device_grid.height() / 2.0);
269+
return;
270+
}
271+
272+
// Compute the centroid of all fixed blocks in the netlist.
273+
t_flat_pl_loc centroid({0.0f, 0.0f, 0.0f});
274+
unsigned num_blks_summed = 0;
275+
for (APBlockId blk_id : netlist_.blocks()) {
276+
// We only get the centroid of fixed blocks since these are the only
277+
// blocks with positions that we know.
278+
if (netlist_.block_mobility(blk_id) != APBlockMobility::FIXED)
279+
continue;
280+
// Get the flat location of the fixed block.
281+
APFixedBlockLoc fixed_blk_loc = netlist_.block_loc(blk_id);
282+
VTR_ASSERT_SAFE(fixed_blk_loc.x != APFixedBlockLoc::UNFIXED_DIM);
283+
VTR_ASSERT_SAFE(fixed_blk_loc.y != APFixedBlockLoc::UNFIXED_DIM);
284+
VTR_ASSERT_SAFE(fixed_blk_loc.layer_num != APFixedBlockLoc::UNFIXED_DIM);
285+
t_flat_pl_loc flat_blk_loc;
286+
flat_blk_loc.x = fixed_blk_loc.x;
287+
flat_blk_loc.y = fixed_blk_loc.y;
288+
flat_blk_loc.layer = fixed_blk_loc.layer_num;
289+
// Accumulate into the centroid.
290+
centroid += flat_blk_loc;
291+
num_blks_summed++;
292+
}
293+
// Divide the sum by the number of fixed blocks.
294+
VTR_ASSERT_SAFE(num_blks_summed == num_fixed_blocks_);
295+
centroid /= static_cast<float>(num_blks_summed);
296+
297+
// Set the guesses to the centroid location.
298+
guess_x = Eigen::VectorXd::Constant(num_variables_, centroid.x);
299+
guess_y = Eigen::VectorXd::Constant(num_variables_, centroid.y);
300+
VTR_LOG("(%g, %g)\n", centroid.x, centroid.y);
301+
}
302+
257303
void QPHybridSolver::solve(unsigned iteration, PartialPlacement& p_placement) {
304+
// In the first iteration, if the number of fixed blocks is 0, set the
305+
// placement to be equal to the guess. The solver below will just set the
306+
// solution to the zero vector if we do not set it to the guess directly.
307+
if (iteration == 0 && num_fixed_blocks_ == 0) {
308+
store_solution_into_placement(guess_x, guess_y, p_placement);
309+
return;
310+
}
311+
258312
// Create a temporary linear system which will contain the original linear
259313
// system which may be updated to include the anchor points.
260314
Eigen::SparseMatrix<double> A_sparse_diff = Eigen::SparseMatrix<double>(A_sparse);
@@ -280,14 +334,24 @@ void QPHybridSolver::solve(unsigned iteration, PartialPlacement& p_placement) {
280334
cg.compute(A_sparse_diff);
281335
VTR_ASSERT(cg.info() == Eigen::Success && "Conjugate Gradient failed at compute!");
282336
// Use the solver to solve for x and y using the constant vectors
283-
// TODO: Use solve with guess to make this faster. Use the previous placement
284-
// as a guess.
285-
Eigen::VectorXd x = cg.solve(b_x_diff);
337+
Eigen::VectorXd x = cg.solveWithGuess(b_x_diff, guess_x);
286338
VTR_ASSERT(cg.info() == Eigen::Success && "Conjugate Gradient failed at solving b_x!");
287-
Eigen::VectorXd y = cg.solve(b_y_diff);
339+
Eigen::VectorXd y = cg.solveWithGuess(b_y_diff, guess_y);
288340
VTR_ASSERT(cg.info() == Eigen::Success && "Conjugate Gradient failed at solving b_y!");
289341

290342
// Write the results back into the partial placement object.
343+
store_solution_into_placement(x, y, p_placement);
344+
345+
// Update the guess. The guess for the next iteration is the solution in
346+
// this iteration.
347+
guess_x = x;
348+
guess_y = y;
349+
}
350+
351+
void QPHybridSolver::store_solution_into_placement(const Eigen::VectorXd& x_soln,
352+
const Eigen::VectorXd& y_soln,
353+
PartialPlacement& p_placement) {
354+
291355
// NOTE: The first [0, num_moveable_blocks_) rows always represent the
292356
// moveable APBlocks. The star nodes always come after and are ignored
293357
// in the solution.
@@ -296,8 +360,23 @@ void QPHybridSolver::solve(unsigned iteration, PartialPlacement& p_placement) {
296360
APBlockId blk_id = row_id_to_blk_id_[row_id];
297361
VTR_ASSERT_DEBUG(blk_id.is_valid());
298362
VTR_ASSERT_DEBUG(netlist_.block_mobility(blk_id) == APBlockMobility::MOVEABLE);
299-
p_placement.block_x_locs[blk_id] = x[row_id_idx];
300-
p_placement.block_y_locs[blk_id] = y[row_id_idx];
363+
// Due to the iterative nature of CG, it is possible for the solver to
364+
// overstep 0 and return a negative number by an incredibly small margin.
365+
// Clamp the number to 0 in this case.
366+
// TODO: Should investigate good bounds on this, the bounds below were
367+
// chosen since any difference higher than 1e-9 would concern me.
368+
double x_pos = x_soln[row_id_idx];
369+
if (x_pos < 0.0) {
370+
VTR_ASSERT_SAFE(std::abs(x_pos) < 1e-9);
371+
x_pos = 0.0;
372+
}
373+
double y_pos = y_soln[row_id_idx];
374+
if (y_pos < 0.0) {
375+
VTR_ASSERT_SAFE(std::abs(y_pos) < 1e-9);
376+
y_pos = 0.0;
377+
}
378+
p_placement.block_x_locs[blk_id] = x_pos;
379+
p_placement.block_y_locs[blk_id] = y_pos;
301380
}
302381
}
303382

vpr/src/analytical_place/analytical_solver.h

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
#pragma once
1010

1111
#include <memory>
12-
#include "ap_netlist_fwd.h"
12+
#include "ap_netlist.h"
13+
#include "device_grid.h"
1314
#include "vtr_strong_id.h"
1415
#include "vtr_vector.h"
1516

@@ -98,6 +99,9 @@ class AnalyticalSolver {
9899
/// when allocating matrices.
99100
size_t num_moveable_blocks_ = 0;
100101

102+
/// @brief The number of fixed blocks in the netlist.
103+
size_t num_fixed_blocks_ = 0;
104+
101105
/// @brief A lookup between a moveable APBlock and its linear ID from
102106
/// [0, num_moveable_blocks). Fixed blocks will return an invalid row
103107
/// ID. This is useful when knowing which row in the matrix
@@ -114,7 +118,8 @@ class AnalyticalSolver {
114118
* @brief A factory method which creates an Analytical Solver of the given type.
115119
*/
116120
std::unique_ptr<AnalyticalSolver> make_analytical_solver(e_analytical_solver solver_type,
117-
const APNetlist& netlist);
121+
const APNetlist& netlist,
122+
const DeviceGrid& device_grid);
118123

119124
// The Eigen library is used to solve matrix equations in the following solvers.
120125
// The solver cannot be built if Eigen is not installed.
@@ -180,6 +185,14 @@ class QPHybridSolver : public AnalyticalSolver {
180185
*/
181186
void init_linear_system();
182187

188+
/**
189+
* @brief Intializes the guesses which will be used in the solver.
190+
*
191+
* The guesses will be used as starting points for the CG solver. The better
192+
* these guesses are, the faster the solver will converge.
193+
*/
194+
void init_guesses(const DeviceGrid& device_grid);
195+
183196
/**
184197
* @brief Helper method to update the linear system with anchors to the
185198
* current partial placement.
@@ -209,6 +222,14 @@ class QPHybridSolver : public AnalyticalSolver {
209222
PartialPlacement& p_placement,
210223
unsigned iteration);
211224

225+
/**
226+
* @brief Store the x and y solutions in Eigen's vectors into the partial
227+
* placement object.
228+
*/
229+
void store_solution_into_placement(const Eigen::VectorXd& x_soln,
230+
const Eigen::VectorXd& y_soln,
231+
PartialPlacement& p_placement);
232+
212233
// The following variables represent the linear system without any anchor
213234
// points. These are filled in the constructor and never modified.
214235
// When the anchor-points are taken into consideration, the diagonal of the
@@ -224,19 +245,31 @@ class QPHybridSolver : public AnalyticalSolver {
224245
Eigen::VectorXd b_x;
225246
/// @brief The constant vector in the y dimension for the linear system.
226247
Eigen::VectorXd b_y;
248+
/// @brief The number of variables in the solver. This is the sum of the
249+
/// number of moveable blocks in the netlist and the number of star
250+
/// nodes that exist.
251+
size_t num_variables_ = 0;
252+
253+
/// @brief The current guess for the x positions of the blocks.
254+
Eigen::VectorXd guess_x;
255+
/// @brief The current guess for the y positions of the blocks.
256+
Eigen::VectorXd guess_y;
227257

228258
public:
229259
/**
230260
* @brief Constructor of the QPHybridSolver
231261
*
232262
* Initializes internal data and constructs the initial linear system.
233263
*/
234-
QPHybridSolver(const APNetlist& netlist)
264+
QPHybridSolver(const APNetlist& netlist, const DeviceGrid& device_grid)
235265
: AnalyticalSolver(netlist) {
236266
// Initializing the linear system only depends on the netlist and fixed
237267
// block locations. Both are provided by the netlist, allowing this to
238268
// be initialized in the constructor.
239269
init_linear_system();
270+
271+
// Initialize the guesses for the first iteration.
272+
init_guesses(device_grid);
240273
}
241274

242275
/**

vpr/src/analytical_place/global_placer.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,8 @@ SimPLGlobalPlacer::SimPLGlobalPlacer(e_partial_legalizer partial_legalizer_type,
7777
// Build the solver.
7878
VTR_LOGV(log_verbosity_ >= 10, "\tBuilding the solver...\n");
7979
solver_ = make_analytical_solver(e_analytical_solver::QP_HYBRID,
80-
ap_netlist_);
80+
ap_netlist_,
81+
device_grid);
8182

8283
// Build the density manager used by the partial legalizer.
8384
VTR_LOGV(log_verbosity_ >= 10, "\tBuilding the density manager...\n");
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
###############################################################################
2+
# Configuration file for running the MCNC benchmarks through the AP flow.
3+
#
4+
# The AP flow requires that each circuit contains fixed blocks and is fixed
5+
# to a specific device size. The device sizes here were chosen to match the
6+
# device sizes of the default VTR flow.
7+
###############################################################################
8+
9+
# Path to directory of circuits to use
10+
circuits_dir=benchmarks/verilog
11+
12+
# Path to directory of architectures to use
13+
archs_dir=arch/timing
14+
15+
# Add architectures to list to sweep
16+
arch_list_add=k6_frac_N10_frac_chain_mem32K_40nm.xml
17+
18+
# Add circuits to list to sweep
19+
circuit_list_add=boundtop.v
20+
circuit_list_add=ch_intrinsics.v
21+
circuit_list_add=or1200.v
22+
circuit_list_add=spree.v
23+
circuit_list_add=stereovision3.v
24+
25+
# Constrain the circuits to their devices
26+
circuit_constraint_list_add=(stereovision3.v, device=vtr_extra_small)
27+
circuit_constraint_list_add=(ch_intrinsics.v, device=vtr_extra_small)
28+
circuit_constraint_list_add=(spree.v, device=vtr_extra_small)
29+
circuit_constraint_list_add=(boundtop.v, device=vtr_extra_small)
30+
circuit_constraint_list_add=(or1200.v, device=vtr_small)
31+
32+
# Constrain the circuits to their channel widths
33+
# 1.3 * minW
34+
circuit_constraint_list_add=(stereovision3.v, route_chan_width=44)
35+
circuit_constraint_list_add=(ch_intrinsics.v, route_chan_width=52)
36+
circuit_constraint_list_add=(spree.v, route_chan_width=78)
37+
circuit_constraint_list_add=(boundtop.v, route_chan_width=50)
38+
circuit_constraint_list_add=(or1200.v, route_chan_width=118)
39+
40+
# Parse info and how to parse
41+
parse_file=vpr_fixed_chan_width.txt
42+
43+
# How to parse QoR info
44+
qor_parse_file=qor_ap_fixed_chan_width.txt
45+
46+
# Pass requirements
47+
pass_requirements_file=pass_requirements_ap_fixed_chan_width.txt
48+
49+
# Pass the script params while writing the vpr constraints.
50+
script_params=-track_memory_usage -crit_path_router_iterations 100 --analytical_place --route
51+
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
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 initial_placed_wirelength_est placed_wirelength_est total_swap accepted_swap rejected_swap aborted_swap place_mem place_time place_quench_time initial_placed_CPD_est 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 routed_wirelength avg_routed_wirelength routed_wiresegment avg_routed_wiresegment total_nets_routed total_connections_routed total_heap_pushes total_heap_pops logic_block_area_total logic_block_area_used routing_area_total routing_area_per_tile crit_path_route_success_iteration num_rr_graph_nodes num_rr_graph_edges collapsed_nodes critical_path_delay geomean_nonvirtual_intradomain_critical_path_delay setup_TNS setup_WNS hold_TNS hold_WNS create_rr_graph_time create_intra_cluster_rr_graph_time adding_internal_edges route_mem crit_path_route_time crit_path_total_timing_analysis_time crit_path_total_sta_time router_lookahead_mem tile_lookahead_computation_time router_lookahead_computation_time
2+
k6_frac_N10_frac_chain_mem32K_40nm.xml boundtop.v common 15.01 vpr 82.36 MiB -1 -1 9.68 47504 3 0.64 -1 -1 38420 -1 -1 47 196 1 0 success v8.0.0-12284-g51bddabcb-dirty release VTR_ASSERT_LEVEL=3 GNU 13.2.0 on Linux-6.8.0-49-generic x86_64 2025-03-20T13:31:45 srivatsan-Precision-Tower-5810 /home/alex/vtr-verilog-to-routing 84332 196 193 800 0 1 602 437 20 20 400 -1 vtr_extra_small -1 -1 10070 3652 132997 35694 84550 12753 82.4 MiB 1.72 0.01 3.2653 2.38057 -1112.29 -2.38057 2.38057 0.06 0.00193977 0.00167903 0.171366 0.148678 82.4 MiB 1.72 82.4 MiB 0.91 5647 9.52277 1600 2.69814 1586 2429 170107 42789 2.07112e+07 3.08102e+06 1.26946e+06 3173.65 12 38988 203232 -1 2.67914 2.67914 -1215.24 -2.67914 0 0 0.19 -1 -1 82.4 MiB 0.11 0.257849 0.228742 82.4 MiB -1 0.06
3+
k6_frac_N10_frac_chain_mem32K_40nm.xml ch_intrinsics.v common 2.43 vpr 77.15 MiB -1 -1 0.25 22148 3 0.07 -1 -1 36668 -1 -1 68 99 1 0 success v8.0.0-12284-g51bddabcb-dirty release VTR_ASSERT_LEVEL=3 GNU 13.2.0 on Linux-6.8.0-49-generic x86_64 2025-03-20T13:31:45 srivatsan-Precision-Tower-5810 /home/alex/vtr-verilog-to-routing 79000 99 130 264 0 1 224 298 20 20 400 -1 vtr_extra_small -1 -1 3122 687 77908 28584 34410 14914 77.1 MiB 0.68 0.01 3.02031 1.87385 -123.342 -1.87385 1.87385 0.06 0.00108168 0.000960787 0.0635174 0.0563103 77.1 MiB 0.68 77.1 MiB 0.30 1330 8.06061 400 2.42424 409 668 26732 7858 2.07112e+07 4.21279e+06 1.31074e+06 3276.84 11 39388 210115 -1 1.99317 1.99317 -137.96 -1.99317 0 0 0.19 -1 -1 77.1 MiB 0.04 0.0899682 0.0807575 77.1 MiB -1 0.06
4+
k6_frac_N10_frac_chain_mem32K_40nm.xml or1200.v common 44.46 vpr 128.95 MiB -1 -1 3.76 64528 8 3.02 -1 -1 44620 -1 -1 248 385 2 1 success v8.0.0-12284-g51bddabcb-dirty release VTR_ASSERT_LEVEL=3 GNU 13.2.0 on Linux-6.8.0-49-generic x86_64 2025-03-20T13:31:45 srivatsan-Precision-Tower-5810 /home/alex/vtr-verilog-to-routing 132040 385 362 3324 0 1 2366 998 30 30 900 -1 vtr_small -1 -1 70396 30779 584243 232629 326108 25506 128.9 MiB 15.88 0.07 12.6272 9.30433 -9557.78 -9.30433 9.30433 0.27 0.00915945 0.00804694 1.17687 1.02821 128.9 MiB 15.88 128.9 MiB 8.51 42328 18.0043 10832 4.60740 9932 32198 1817849 333160 4.8774e+07 1.48577e+07 6.56785e+06 7297.61 14 120772 1084977 -1 9.70184 9.70184 -9948.19 -9.70184 0 0 1.27 -1 -1 128.9 MiB 0.82 1.65611 1.47053 128.9 MiB -1 0.27
5+
k6_frac_N10_frac_chain_mem32K_40nm.xml spree.v common 11.31 vpr 85.20 MiB -1 -1 2.11 35716 16 0.42 -1 -1 38820 -1 -1 61 45 3 1 success v8.0.0-12284-g51bddabcb-dirty release VTR_ASSERT_LEVEL=3 GNU 13.2.0 on Linux-6.8.0-49-generic x86_64 2025-03-20T13:31:45 srivatsan-Precision-Tower-5810 /home/alex/vtr-verilog-to-routing 87240 45 32 936 0 1 769 142 20 20 400 -1 vtr_extra_small -1 -1 14278 6632 31222 9013 20166 2043 85.2 MiB 3.44 0.01 14.223 10.6451 -7116.98 -10.6451 10.6451 0.08 0.00250891 0.00209652 0.234093 0.194408 85.2 MiB 3.44 85.2 MiB 2.40 11049 14.4243 2887 3.76893 3469 9247 708024 169456 2.07112e+07 5.32753e+06 1.91495e+06 4787.38 18 44576 305072 -1 10.6885 10.6885 -7341.62 -10.6885 0 0 0.34 -1 -1 85.2 MiB 0.28 0.384243 0.329459 85.2 MiB -1 0.08
6+
k6_frac_N10_frac_chain_mem32K_40nm.xml stereovision3.v common 2.20 vpr 76.85 MiB -1 -1 0.46 26500 4 0.11 -1 -1 36476 -1 -1 15 11 0 0 success v8.0.0-12284-g51bddabcb-dirty release VTR_ASSERT_LEVEL=3 GNU 13.2.0 on Linux-6.8.0-49-generic x86_64 2025-03-20T13:31:45 srivatsan-Precision-Tower-5810 /home/alex/vtr-verilog-to-routing 78692 11 2 140 0 2 87 28 20 20 400 -1 vtr_extra_small -1 -1 1104 346 1498 346 953 199 76.8 MiB 0.42 0.00 3.08719 2.10685 -179.409 -2.10685 1.95087 0.06 0.000442371 0.000365901 0.0198711 0.0167916 76.8 MiB 0.42 76.8 MiB 0.28 569 7.02469 145 1.79012 164 245 5326 1432 2.07112e+07 808410 1.12964e+06 2824.09 7 37792 180905 -1 2.08145 1.90829 -178.671 -2.08145 0 0 0.16 -1 -1 76.8 MiB 0.02 0.0370597 0.0326026 76.8 MiB -1 0.06

0 commit comments

Comments
 (0)