Skip to content

Commit

Permalink
move interdex reshuffle impl to a separate file
Browse files Browse the repository at this point in the history
Summary: as title, it is behavior-preserving change.

Reviewed By: NTillmann

Differential Revision: D51718848

fbshipit-source-id: 014c56b99e45dfeb587f566301c98f110a6f0326
  • Loading branch information
beicy authored and facebook-github-bot committed Dec 8, 2023
1 parent f1976ac commit 1d16eca
Show file tree
Hide file tree
Showing 5 changed files with 502 additions and 458 deletions.
1 change: 1 addition & 0 deletions Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,7 @@ libopt_la_SOURCES = \
opt/int_type_patcher/IntTypePatcher.cpp \
opt/interdex/InterDex.cpp \
opt/interdex/InterDexPass.cpp \
opt/interdex/InterDexReshuffleImpl.cpp \
opt/interdex/InterDexReshufflePass.cpp \
opt/interdex/SortRemainingClassesPass.cpp \
opt/kotlin-lambda/RewriteKotlinSingletonInstance.cpp \
Expand Down
227 changes: 227 additions & 0 deletions opt/interdex/InterDexReshuffleImpl.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

#include "InterDexReshuffleImpl.h"
#include "InterDexPass.h"
#include "Show.h"
#include "Trace.h"
#include <boost/algorithm/string/predicate.hpp>

#include <algorithm>
#include <cinttypes>
#include <string>

InterDexReshuffleImpl::InterDexReshuffleImpl(ConfigFiles& conf,
PassManager& mgr,
ReshuffleConfig& config,
DexClasses& original_scope,
DexClassesVector& dexen)
: m_conf(conf),
m_mgr(mgr),
m_config(config),
m_init_classes_with_side_effects(original_scope,
conf.create_init_class_insns()),
m_dexen(dexen) {
m_dexes_structure.set_min_sdk(mgr.get_redex_options().min_sdk);
const auto& interdex_metrics = mgr.get_interdex_metrics();
auto it = interdex_metrics.find(interdex::METRIC_LINEAR_ALLOC_LIMIT);
m_linear_alloc_limit = (it != interdex_metrics.end() ? it->second : 0) +
m_config.extra_linear_alloc_limit;
it = interdex_metrics.find(interdex::METRIC_ORDER_INTERDEX);
m_order_interdex = it == interdex_metrics.end() || it->second;
auto refs_info = mgr.get_reserved_refs();
m_dexes_structure.set_reserve_frefs(refs_info.frefs +
m_config.reserved_extra_frefs);
m_dexes_structure.set_reserve_trefs(refs_info.trefs +
m_config.reserved_extra_trefs);
m_dexes_structure.set_reserve_mrefs(refs_info.mrefs +
m_config.reserved_extra_mrefs);

m_mutable_dexen.resize(dexen.size());
m_mutable_dexen_strings.resize(dexen.size());

Timer t("init");
DexClasses classes;
for (size_t dex_index = m_first_dex_index; dex_index < dexen.size();
dex_index++) {
auto& dex = dexen.at(dex_index);
if (dex_index == m_first_dex_index &&
!std::any_of(dex.begin(), dex.end(),
[this](auto* cls) { return can_move(cls); })) {
m_first_dex_index++;
continue;
}
for (auto cls : dex) {
classes.push_back(cls);
m_class_refs.emplace(cls, Refs());
if (!can_move(cls)) {
continue;
}
m_movable_classes.push_back(cls);
m_class_dex_indices.emplace(cls, dex_index);
}
}
walk::parallel::classes(classes, [&](DexClass* cls) {
always_assert(m_class_refs.count(cls));
auto& refs = m_class_refs.at(cls);
cls->gather_methods(refs.mrefs);
cls->gather_fields(refs.frefs);
cls->gather_types(refs.trefs);
std::vector<DexType*> itrefs;
cls->gather_init_classes(itrefs);
refs.itrefs.insert(itrefs.begin(), itrefs.end());
cls->gather_strings(refs.srefs);
});
workqueue_run_for<size_t>(
m_first_dex_index, dexen.size(), [&](size_t dex_idx) {
auto& dex = dexen.at(dex_idx);
auto& mutable_dex = m_mutable_dexen.at(dex_idx);
auto& mutable_dex_strings = m_mutable_dexen_strings.at(dex_idx);
for (auto cls : dex) {
always_assert(m_class_refs.count(cls));
const auto& refs = m_class_refs.at(cls);
TypeRefs pending_init_class_fields;
TypeRefs pending_init_class_types;
mutable_dex.resolve_init_classes(&m_init_classes_with_side_effects,
refs.frefs, refs.trefs, refs.itrefs,
&pending_init_class_fields,
&pending_init_class_types);
auto laclazz = estimate_linear_alloc(cls);
mutable_dex.add_class_no_checks(
refs.mrefs, refs.frefs, refs.trefs, pending_init_class_fields,
pending_init_class_types, laclazz, cls);
for (auto* sref : refs.srefs) {
mutable_dex_strings[sref]++;
}
}
});
}

void InterDexReshuffleImpl::compute_plan() {
Timer t("compute_plan");
MoveGains move_gains(m_first_dex_index, m_movable_classes,
m_class_dex_indices, m_class_refs, m_mutable_dexen,
m_mutable_dexen_strings);
size_t batches{0};
size_t total_moves{0};
size_t max_move_gains{0};
for (; batches < m_config.max_batches; batches++) {
Timer u("batch");
move_gains.recompute_gains();
max_move_gains = std::max(max_move_gains, move_gains.size());

while (move_gains.moves_this_epoch() < m_config.max_batch_size) {
std::optional<Move> move_opt = move_gains.pop_max_gain();
if (!move_opt) {
break;
}

const Move& move = *move_opt;
auto recomputed_gain =
move_gains.compute_move_gain(move.cls, move.target_dex_index);
if (recomputed_gain <= 0) {
continue;
}

// Check if it is a valid move.
if (!try_plan_move(move)) {
continue;
}
if (traceEnabled(IDEXR, 5)) {
print_stats();
}
move_gains.moved_class(move);
}
total_moves += move_gains.moves_this_epoch();
TRACE(IDEXR, 2, "executed %zu moves in epoch %zu",
move_gains.moves_this_epoch(), batches);
if (move_gains.should_stop()) {
break;
}
}

m_mgr.incr_metric("max_move_gains", max_move_gains);
m_mgr.incr_metric("total_moves", total_moves);
m_mgr.incr_metric("batches", batches);
m_mgr.incr_metric("first_dex_index", m_first_dex_index);
TRACE(IDEXR, 1, "executed %zu moves in %zu batches", total_moves, batches);
}

void InterDexReshuffleImpl::apply_plan() {
Timer t("finish");
workqueue_run_for<size_t>(
m_first_dex_index, m_mutable_dexen.size(), [&](size_t dex_idx) {
auto& dex = m_dexen.at(dex_idx);
const auto& mutable_dex = m_mutable_dexen.at(dex_idx);
auto classes = mutable_dex.get_classes(/* perf_based */ true);
TRACE(IDEXR, 2, "dex %zu: %zu => %zu classes", dex_idx, dex.size(),
classes.size());
dex = std::move(classes);
});
}

void InterDexReshuffleImpl::print_stats() {
size_t n_classes = 0;
size_t n_mrefs = 0;
size_t n_frefs = 0;
for (size_t idx = 0; idx < m_mutable_dexen.size(); ++idx) {
auto& mutable_dex = m_mutable_dexen.at(idx);
n_classes += mutable_dex.get_num_classes();
n_mrefs += mutable_dex.get_num_mrefs();
n_frefs += mutable_dex.get_num_frefs();
}

TRACE(IDEXR, 5, "Global stats:");
TRACE(IDEXR, 5, "\t %zu classes", n_classes);
TRACE(IDEXR, 5, "\t %zu mrefs", n_mrefs);
TRACE(IDEXR, 5, "\t %zu frefs", n_frefs);
}

bool InterDexReshuffleImpl::try_plan_move(const Move& move) {
auto& target_dex = m_mutable_dexen.at(move.target_dex_index);
always_assert(m_class_refs.count(move.cls));
const auto& refs = m_class_refs.at(move.cls);
TypeRefs pending_init_class_fields;
TypeRefs pending_init_class_types;
target_dex.resolve_init_classes(
&m_init_classes_with_side_effects, refs.frefs, refs.trefs, refs.itrefs,
&pending_init_class_fields, &pending_init_class_types);
auto laclazz = estimate_linear_alloc(move.cls);
if (!target_dex.add_class_if_fits(
refs.mrefs, refs.frefs, refs.trefs, pending_init_class_fields,
pending_init_class_types, m_linear_alloc_limit,
m_dexes_structure.get_frefs_limit(),
m_dexes_structure.get_mrefs_limit(),
m_dexes_structure.get_trefs_limit(), move.cls)) {
return false;
}
auto& target_dex_strings = m_mutable_dexen_strings.at(move.target_dex_index);
for (auto* sref : refs.srefs) {
target_dex_strings[sref]++;
}
always_assert(m_class_dex_indices.count(move.cls));
auto& dex_index = m_class_dex_indices.at(move.cls);
auto& source_dex = m_mutable_dexen.at(dex_index);
source_dex.remove_class(&m_init_classes_with_side_effects, refs.mrefs,
refs.frefs, refs.trefs, pending_init_class_fields,
pending_init_class_types, laclazz, move.cls);
auto& source_dex_strings = m_mutable_dexen_strings.at(dex_index);
for (auto* sref : refs.srefs) {
auto it = source_dex_strings.find(sref);
if (--it->second == 0) {
source_dex_strings.erase(it);
}
}
dex_index = move.target_dex_index;
return true;
}

bool InterDexReshuffleImpl::can_move(DexClass* cls) {
return (!m_order_interdex ||
cls->get_perf_sensitive() != PerfSensitiveGroup::BETAMAP_ORDERED) &&
!is_canary(cls);
}
Loading

0 comments on commit 1d16eca

Please sign in to comment.