code_relocation: Cache list of obj files

As part of relocation, this script matches each source file to its
corresponding object file. This matching is done inside of get_obj_filename
which is called once per source file.

get_obj_filename traverses the entire build directory on every invocation.
This is unnecessary since built object files don't change. On a
sufficiently large project (like mine), this script takes over a minute and
the majority of that time is spent needlessly traversing the build
directory again and again.

Caching the list of object files enables this script to run in less than a
second.

I tested by building my project (which enables the relocation script) and
comparing memory breakdown before / after.

Signed-off-by: Galen Krulce <gkrulce@meta.com>
This commit is contained in:
Galen Krulce 2025-02-24 21:27:57 -08:00 committed by Benjamin Cabé
commit 5ae9f44825

View file

@ -44,7 +44,6 @@ this will place data and bss inside SRAM2.
import sys import sys
import argparse import argparse
import os
import glob import glob
import re import re
import warnings import warnings
@ -491,19 +490,18 @@ def parse_args():
help="Verbose Output") help="Verbose Output")
args = parser.parse_args() args = parser.parse_args()
def gen_all_obj_files(searchpath):
return list(Path(searchpath).rglob('*.o')) + list(Path(searchpath).rglob('*.obj'))
# return the absolute path for the object file. # return the absolute path for the object file.
def get_obj_filename(searchpath, filename): def get_obj_filename(all_obj_files, filename):
# get the object file name which is almost always pended with .obj # get the object file name which is almost always pended with .obj
obj_filename = filename.split("/")[-1] + ".obj" obj_filename = filename.split("/")[-1] + ".obj"
for dirpath, _, files in os.walk(searchpath): for obj_file in all_obj_files:
for filename1 in files: if obj_file.name == obj_filename:
if filename1 == obj_filename: if filename.split("/")[-2] in obj_file.parent.name:
if filename.split("/")[-2] in dirpath.split("/")[-1]: return str(obj_file)
fullname = os.path.join(dirpath, filename1)
return fullname
# Extracts all possible components for the input string: # Extracts all possible components for the input string:
# <mem_region>[\ :program_header]:<flag_1>[;<flag_2>...]:<file_1>[;<file_2>...][,filter] # <mem_region>[\ :program_header]:<flag_1>[;<flag_2>...]:<file_1>[;<file_2>...][,filter]
@ -587,6 +585,7 @@ def main():
mpu_align = {} mpu_align = {}
parse_args() parse_args()
searchpath = args.directory searchpath = args.directory
all_obj_files = gen_all_obj_files(searchpath)
linker_file = args.output linker_file = args.output
sram_data_linker_file = args.output_sram_data sram_data_linker_file = args.output_sram_data
sram_bss_linker_file = args.output_sram_bss sram_bss_linker_file = args.output_sram_bss
@ -602,7 +601,7 @@ def main():
full_list_of_sections: 'dict[SectionKind, list[OutputSection]]' = defaultdict(list) full_list_of_sections: 'dict[SectionKind, list[OutputSection]]' = defaultdict(list)
for filename, symbol_filter in files: for filename, symbol_filter in files:
obj_filename = get_obj_filename(searchpath, filename) obj_filename = get_obj_filename(all_obj_files, filename)
# the obj file wasn't found. Probably not compiled. # the obj file wasn't found. Probably not compiled.
if not obj_filename: if not obj_filename:
continue continue