size_t PrefixLen(const std::string& a, const std::string& b) {
size_t len = 0; for (; len < a.length() && len < b.length() && a[len] == b[len]; ++len) {} return len;
}
dex_code_bytes_ += code_item.InsnsSizeInBytes();
unique_code_items.insert(method.GetCodeItemOffset()); for (const DexInstructionPcPair& inst : code_item) { switch (inst->Opcode()) { case Instruction::CONST_STRING: { const dex::StringIndex string_index(inst->VRegB_21c());
unique_string_ids.insert(string_index.index_);
++num_string_ids_from_code_; break;
} case Instruction::IGET: case Instruction::IGET_WIDE: case Instruction::IGET_OBJECT: case Instruction::IGET_BOOLEAN: case Instruction::IGET_BYTE: case Instruction::IGET_CHAR: case Instruction::IGET_SHORT: {
ProcessInstanceField(
inst.Inst(), first_arg_reg, instance_field_index_map_, &iget_stats_); break;
} case Instruction::IPUT: case Instruction::IPUT_WIDE: case Instruction::IPUT_OBJECT: case Instruction::IPUT_BOOLEAN: case Instruction::IPUT_BYTE: case Instruction::IPUT_CHAR: case Instruction::IPUT_SHORT: {
ProcessInstanceField(
inst.Inst(), first_arg_reg, instance_field_index_map_, &iput_stats_); break;
} case Instruction::SGET: case Instruction::SGET_WIDE: case Instruction::SGET_OBJECT: case Instruction::SGET_BOOLEAN: case Instruction::SGET_BYTE: case Instruction::SGET_CHAR: case Instruction::SGET_SHORT: {
ProcessStaticField(inst.Inst(), static_field_index_map_, &sget_stats_); break;
} case Instruction::SPUT: case Instruction::SPUT_WIDE: case Instruction::SPUT_OBJECT: case Instruction::SPUT_BOOLEAN: case Instruction::SPUT_BYTE: case Instruction::SPUT_CHAR: case Instruction::SPUT_SHORT: {
ProcessStaticField(inst.Inst(), static_field_index_map_, &sput_stats_); break;
} case Instruction::CONST_STRING_JUMBO: { const dex::StringIndex string_index(inst->VRegB_31c());
unique_string_ids.insert(string_index.index_);
++num_string_ids_from_code_; break;
} // Invoke cases. case Instruction::INVOKE_VIRTUAL: case Instruction::INVOKE_VIRTUAL_RANGE: {
uint32_t method_idx = DexMethodIndex(inst.Inst());
++types_accessed[dex_file.GetMethodId(method_idx).class_idx_.index_]; if (dex_file.GetMethodId(method_idx).class_idx_ == accessor.GetClassIdx()) {
++same_class_virtual_;
}
++total_virtual_;
unique_method_ids.insert(method_idx); break;
} case Instruction::INVOKE_DIRECT: case Instruction::INVOKE_DIRECT_RANGE: {
uint32_t method_idx = DexMethodIndex(inst.Inst());
++types_accessed[dex_file.GetMethodId(method_idx).class_idx_.index_]; if (dex_file.GetMethodId(method_idx).class_idx_ == accessor.GetClassIdx()) {
++same_class_direct_;
}
++total_direct_;
unique_method_ids.insert(method_idx); break;
} case Instruction::INVOKE_STATIC: case Instruction::INVOKE_STATIC_RANGE: {
uint32_t method_idx = DexMethodIndex(inst.Inst());
++types_accessed[dex_file.GetMethodId(method_idx).class_idx_.index_]; if (dex_file.GetMethodId(method_idx).class_idx_ == accessor.GetClassIdx()) {
++same_class_static_;
}
++total_static_;
unique_method_ids.insert(method_idx); break;
} case Instruction::INVOKE_INTERFACE: case Instruction::INVOKE_INTERFACE_RANGE: {
uint32_t method_idx = DexMethodIndex(inst.Inst());
++types_accessed[dex_file.GetMethodId(method_idx).class_idx_.index_]; if (dex_file.GetMethodId(method_idx).class_idx_ == accessor.GetClassIdx()) {
++same_class_interface_;
}
++total_interface_;
unique_method_ids.insert(method_idx); break;
} case Instruction::INVOKE_SUPER: case Instruction::INVOKE_SUPER_RANGE: {
uint32_t method_idx = DexMethodIndex(inst.Inst());
++types_accessed[dex_file.GetMethodId(method_idx).class_idx_.index_]; if (dex_file.GetMethodId(method_idx).class_idx_ == accessor.GetClassIdx()) {
++same_class_super_;
}
++total_super_;
unique_method_ids.insert(method_idx); break;
} case Instruction::NEW_ARRAY: {
++types_accessed[inst->VRegC_22c()]; break;
} case Instruction::FILLED_NEW_ARRAY: {
++types_accessed[inst->VRegB_35c()]; break;
} case Instruction::FILLED_NEW_ARRAY_RANGE: {
++types_accessed[inst->VRegB_3rc()]; break;
} case Instruction::CONST_CLASS: case Instruction::CHECK_CAST: case Instruction::NEW_INSTANCE: {
++types_accessed[inst->VRegB_21c()]; break;
} case Instruction::INSTANCE_OF: {
++types_accessed[inst->VRegB_21c()]; break;
} default: break;
}
}
} // Count uses of top 16n.
std::vector<size_t> uses;
uses.reserve(types_accessed.size()); for (auto&& p : types_accessed) {
uses.push_back(p.second);
}
std::sort(uses.rbegin(), uses.rend()); for (size_t i = 0; i < uses.size(); ++i) { if (i < 16) {
uses_top_types_ += uses[i];
}
uses_all_types_ += uses[i];
}
total_unique_types_ += types_accessed.size();
total_unique_method_ids_ += unique_method_ids.size();
total_unique_string_ids_ += unique_string_ids.size();
}
total_unique_code_items_ += unique_code_items.size();
}
void CountDexIndices::Dump(std::ostream& os, uint64_t total_size) const { auto DumpFieldIndexes = [&](const FieldAccessStats& stats) { const uint64_t fields_idx_total = std::accumulate(
stats.field_index_,
stats.field_index_ + FieldAccessStats::kMaxFieldIndex,
stats.field_index_other_ + stats.field_index_other_class_); for (size_t i = 0; i < FieldAccessStats::kMaxFieldIndex; ++i) {
os << " field_idx=" << i << ": " << Percent(stats.field_index_[i], fields_idx_total) << "\n";
}
os << " field_idx=other: " << Percent(stats.field_index_other_, fields_idx_total) << "\n";
os << " field_idx=other_class: " << Percent(stats.field_index_other_class_, fields_idx_total)
<< "\n";
}; auto DumpInstanceFieldStats = [&](constchar* tag, const InstanceFieldAccessStats& stats) { const uint64_t fields_total = std::accumulate(stats.inout_, stats.inout_ + 16u, 0u);
os << tag << "\n"; for (size_t i = 0; i < 16; ++i) {
os << " receiver_reg=" << i << ": " << Percent(stats.receiver_[i], fields_total) << "\n";
}
DCHECK(tag[1] == 'G' || tag[1] == 'P'); constchar* inout_tag = (tag[1] == 'G') ? "output_reg" : "input_reg"; for (size_t i = 0; i < 16; ++i) {
os << " " << inout_tag << "=" << i << ": " << Percent(stats.inout_[i], fields_total) << "\n";
}
DumpFieldIndexes(stats);
os << " short_bytecode: " << Percent(stats.short_bytecode_, fields_total) << "\n";
os << " short_bytecode_savings=" << Percent(stats.short_bytecode_ * 2, total_size) << "\n";
};
DumpInstanceFieldStats("IGET", iget_stats_);
DumpInstanceFieldStats("IPUT", iput_stats_);
auto DumpStaticFieldStats = [&](constchar* tag, const StaticFieldAccessStats& stats) { const uint64_t fields_total =
std::accumulate(stats.inout_, stats.inout_ + 16u, stats.inout_other_);
os << tag << "\n";
DCHECK(tag[1] == 'G' || tag[1] == 'P'); constchar* inout_tag = (tag[1] == 'G') ? "output_reg" : "input_reg"; for (size_t i = 0; i < 16; ++i) {
os << " " << inout_tag << "=" << i << ": " << Percent(stats.inout_[i], fields_total) << "\n";
}
os << " " << inout_tag << "=other: " << Percent(stats.inout_other_, fields_total) << "\n";
DumpFieldIndexes(stats);
os << " short_bytecode: " << Percent(stats.short_bytecode_, fields_total) << "\n";
os << " short_bytecode_savings=" << Percent(stats.short_bytecode_ * 2, total_size) << "\n";
};
DumpStaticFieldStats("SGET", sget_stats_);
DumpStaticFieldStats("SPUT", sput_stats_);
os << "Num string ids: " << num_string_ids_ << "\n";
os << "Num method ids: " << num_method_ids_ << "\n";
os << "Num field ids: " << num_field_ids_ << "\n";
os << "Num type ids: " << num_type_ids_ << "\n";
os << "Num class defs: " << num_class_defs_ << "\n";
os << "Direct same class: " << PercentDivide(same_class_direct_, total_direct_) << "\n";
os << "Virtual same class: " << PercentDivide(same_class_virtual_, total_virtual_) << "\n";
os << "Static same class: " << PercentDivide(same_class_static_, total_static_) << "\n";
os << "Interface same class: " << PercentDivide(same_class_interface_, total_interface_) << "\n";
os << "Super same class: " << PercentDivide(same_class_super_, total_super_) << "\n";
os << "Num strings accessed from code: " << num_string_ids_from_code_ << "\n";
os << "Avg unique methods accessed per class: "
<< static_cast<double>(total_unique_method_ids_) / static_cast<double>(num_class_defs_) << "\n";
os << "Avg unique strings accessed per class: "
<< static_cast<double>(total_unique_string_ids_) / static_cast<double>(num_class_defs_) << "\n";
os << "Avg unique types accessed per class " << static_cast<double>(total_unique_types_) / static_cast<double>(num_class_defs_) << "\n";
os << "Total unique methods accessed per class: "
<< Percent(total_unique_method_ids_, total_size) << "\n";
os << "Total unique strings accessed per class: "
<< Percent(total_unique_string_ids_, total_size) << "\n";
os << "Total unique types accessed per class: "
<< Percent(total_unique_types_, total_size) << "\n"; const size_t same_class_total =
same_class_direct_ +
same_class_virtual_ +
same_class_static_ +
same_class_interface_ +
same_class_super_; const size_t other_class_total =
total_direct_ +
total_virtual_ +
total_static_ +
total_interface_ +
total_super_;
os << "Unique method names: " << Percent(total_unique_method_names_, num_field_ids_) << "\n";
os << "Unique field names: " << Percent(total_unique_field_names_, num_method_ids_) << "\n";
os << "Unique type names: " << Percent(total_unique_type_names_, num_type_ids_) << "\n";
os << "Unique method/field names: "
<< Percent(total_unique_mf_names_, num_field_ids_ + num_method_ids_) << "\n";
os << "Same class invokes: " << PercentDivide(same_class_total, other_class_total) << "\n";
os << "Invokes from code: " << (same_class_total + other_class_total) << "\n";
os << "Type uses on top types: " << PercentDivide(uses_top_types_, uses_all_types_) << "\n";
os << "Type uses 1b savings: " << PercentDivide(uses_top_types_, total_size) << "\n";
os << "Total Dex code bytes: " << Percent(dex_code_bytes_, total_size) << "\n";
os << "Total unique code items: " << total_unique_code_items_ << "\n";
os << "Total Dex size: " << total_size << "\n";
}
void CodeMetrics::ProcessDexFile(const DexFile& dex_file) { for (ClassAccessor accessor : dex_file.GetClasses()) { for (const ClassAccessor::Method& method : accessor.GetMethods()) { bool space_for_out_arg = false; for (const DexInstructionPcPair& inst : method.GetInstructions()) { switch (inst->Opcode()) { case Instruction::INVOKE_VIRTUAL: case Instruction::INVOKE_DIRECT: case Instruction::INVOKE_SUPER: case Instruction::INVOKE_INTERFACE: case Instruction::INVOKE_STATIC: { const uint32_t args = NumberOfArgs(inst.Inst());
CHECK_LT(args, kMaxArgCount);
++arg_counts_[args];
space_for_out_arg = args < kMaxArgCount - 1; break;
} case Instruction::MOVE_RESULT: case Instruction::MOVE_RESULT_OBJECT: { if (space_for_out_arg && inst->VRegA_11x() < 16) {
move_result_savings_ += inst->SizeInCodeUnits() * 2;
} break;
} default:
space_for_out_arg = false; break;
}
}
}
}
}
void CodeMetrics::Dump(std::ostream& os, uint64_t total_size) const { const uint64_t total = std::accumulate(arg_counts_, arg_counts_ + kMaxArgCount, 0u); for (size_t i = 0; i < kMaxArgCount; ++i) {
os << "args=" << i << ": " << Percent(arg_counts_[i], total) << "\n";
}
os << "Move result savings: " << Percent(move_result_savings_, total_size) << "\n";
os << "One byte invoke savings: " << Percent(total, total_size) << "\n"; const uint64_t low_arg_total = std::accumulate(arg_counts_, arg_counts_ + 2, 0u);
os << "Low arg savings: " << Percent(low_arg_total * 2, total_size) << "\n";
}
} // namespace dexanalyze
} // namespace art
Messung V0.5 in Prozent
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.13Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-09-28)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.