mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-17 10:23:46 +00:00
Cache timestamps by target file.
Previously, to look up the timestamp for a target_file we would first
compute a file name for that target_file, then look up the timestamp by
file name. This occurs frequently, so even though the computed file name
was cached, eliminating one of the two steps can be worthwhile.
This change therefore introduces a cache of timestamps indexed by
target_file, in addition to the existing cache of timestamps indexed
by file name.
Cache invalidation can be complicated because when a file is updated,
it is not straightforward to which target_file(s) may be affected.
Invalidating the entire cache is viable as it happens relatively
infrequently, and the cache will be quickly repopulated anyway.
This change improves the run time of a do-nothing build of Prince
on my machine from 1.7 s to 1.6 s.
compiler/make.make_info.m:
compiler/make.top_level.m:
Add a field to make_info to hold a cache of timestamps indexed by
target_file.
Delete the mki_search_file_name_cache field.
compiler/make.util.m:
Cache timestamps by target_file in get_target_timestamp.
Avoid computing file names until required.
Don't cache computed file names in get_file_name.
get_file_name is now called much less frequently, so caching the
result should not be necessary (but can be reintroduced if some
other code path requires it).
Clear out timestamps for target_files when a file is deleted.
compiler/make.module_target.m:
Delete old timestamp for a target_file when the target is made.
compiler/make.program_target.m:
Clear out timestamps for target_files when Java class files are
made.
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
% vim: ft=mercury ts=4 sw=4 et
|
||||
%---------------------------------------------------------------------------%
|
||||
% Copyright (C) 2002-2012 The University of Melbourne.
|
||||
% Copyright (C) 2013-2021 The Mercury team.
|
||||
% Copyright (C) 2013-2022 The Mercury team.
|
||||
% This file may only be copied under the terms of the GNU General
|
||||
% Public License - see the file COPYING in the Mercury distribution.
|
||||
%---------------------------------------------------------------------------%
|
||||
@@ -28,7 +28,6 @@
|
||||
:- import_module mdbcomp.
|
||||
:- import_module mdbcomp.sym_name.
|
||||
:- import_module parse_tree.
|
||||
:- import_module parse_tree.file_names.
|
||||
:- import_module parse_tree.module_dep_info.
|
||||
:- import_module parse_tree.read_modules.
|
||||
|
||||
@@ -57,10 +56,21 @@
|
||||
mki_module_dependencies :: map(module_name,
|
||||
maybe_module_dep_info),
|
||||
|
||||
% A map of last known timestamps by file name. This assumes
|
||||
% that no external process is updating the same files without
|
||||
% our knowledge.
|
||||
%
|
||||
% If a file is updated or removed, then you need to remove an
|
||||
% entry from this map. Also, there may be an entry in the
|
||||
% following map for a target_file that needs to be invalidated.
|
||||
% If that is difficult, it is simplest to reset the
|
||||
% mki_target_file_timestamps map.
|
||||
mki_file_timestamps :: file_timestamps,
|
||||
|
||||
% Cache chosen file names for a module name and extension.
|
||||
mki_search_file_name_cache :: map(module_name_ext, file_name),
|
||||
% A map of last known timestamps for the file that corresponds
|
||||
% to a target_file.
|
||||
mki_target_file_timestamps
|
||||
:: target_file_timestamps,
|
||||
|
||||
% Any flags required to set detected library grades.
|
||||
mki_detected_grade_flags :: list(string),
|
||||
@@ -148,8 +158,7 @@
|
||||
|
||||
:- type file_timestamps == map(string, maybe_error(timestamp)).
|
||||
|
||||
:- type module_name_ext
|
||||
---> module_name_ext(module_name, ext).
|
||||
:- type target_file_timestamps == map(target_file, timestamp).
|
||||
|
||||
:- type module_index_map
|
||||
---> module_index_map(
|
||||
|
||||
@@ -810,11 +810,12 @@ record_made_target_given_maybe_touched_files(Globals, Succeeded, TargetFile,
|
||||
list.map_foldl2(get_file_name(Globals, do_not_search), TouchedTargetFiles,
|
||||
TouchedTargetFileNames, !Info, !IO),
|
||||
|
||||
some [!Timestamps] (
|
||||
!:Timestamps = !.Info ^ mki_file_timestamps,
|
||||
some [!FileTimestamps] (
|
||||
!:FileTimestamps = !.Info ^ mki_file_timestamps,
|
||||
list.foldl(delete_timestamp(Globals), TouchedTargetFileNames,
|
||||
!Timestamps),
|
||||
list.foldl(delete_timestamp(Globals), OtherTouchedFiles, !Timestamps),
|
||||
!FileTimestamps),
|
||||
list.foldl(delete_timestamp(Globals), OtherTouchedFiles,
|
||||
!FileTimestamps),
|
||||
|
||||
% When an .analysis file is made, that potentially invalidates other
|
||||
% .analysis files so we have to delete their timestamps. The exact list
|
||||
@@ -823,13 +824,17 @@ record_made_target_given_maybe_touched_files(Globals, Succeeded, TargetFile,
|
||||
% timestamps of all the .analysis files that we know about.
|
||||
( if TargetFile = target_file(_, module_target_analysis_registry) then
|
||||
map.foldl(delete_analysis_registry_timestamps(Globals),
|
||||
!.Timestamps, !Timestamps)
|
||||
!.FileTimestamps, !FileTimestamps)
|
||||
else
|
||||
true
|
||||
),
|
||||
|
||||
!Info ^ mki_file_timestamps := !.Timestamps
|
||||
).
|
||||
!Info ^ mki_file_timestamps := !.FileTimestamps
|
||||
),
|
||||
|
||||
TargetFileTimestamps0 = !.Info ^ mki_target_file_timestamps,
|
||||
map.delete(TargetFile, TargetFileTimestamps0, TargetFileTimestamps),
|
||||
!Info ^ mki_target_file_timestamps := TargetFileTimestamps.
|
||||
|
||||
:- pred update_target_status(dependency_status::in, target_file::in,
|
||||
make_info::in, make_info::out) is det.
|
||||
|
||||
@@ -527,8 +527,12 @@ build_linked_target_2(Globals, MainModuleName, FileType, OutputFileName,
|
||||
(
|
||||
InitObjectResult1 = yes(InitObject),
|
||||
% We may need to update the timestamp of the `_init.o' file.
|
||||
!Info ^ mki_file_timestamps :=
|
||||
map.delete(!.Info ^ mki_file_timestamps, InitObject),
|
||||
FileTimestamps0 = !.Info ^ mki_file_timestamps,
|
||||
map.delete(InitObject, FileTimestamps0, FileTimestamps1),
|
||||
!Info ^ mki_file_timestamps := FileTimestamps1,
|
||||
% There is no module_target_type for the `_init.o' file,
|
||||
% so mki_target_file_timestamps should not contain anything
|
||||
% that needs to be invalidated.
|
||||
InitObjects = [InitObject],
|
||||
DepsResult2 = BuildDepsResult
|
||||
;
|
||||
@@ -654,9 +658,12 @@ build_linked_target_2(Globals, MainModuleName, FileType, OutputFileName,
|
||||
!Info ^ mki_command_line_targets := CmdLineTargets,
|
||||
(
|
||||
Succeeded = succeeded,
|
||||
FileTimestamps0 = !.Info ^ mki_file_timestamps,
|
||||
map.delete(OutputFileName, FileTimestamps0, FileTimestamps),
|
||||
FileTimestamps2 = !.Info ^ mki_file_timestamps,
|
||||
map.delete(OutputFileName, FileTimestamps2, FileTimestamps),
|
||||
!Info ^ mki_file_timestamps := FileTimestamps
|
||||
% There is no module_target_type for the linked target,
|
||||
% so mki_target_file_timestamps should not contain anything
|
||||
% that needs to be invalidated.
|
||||
;
|
||||
Succeeded = did_not_succeed,
|
||||
file_error(!.Info, OutputFileName, !IO)
|
||||
@@ -730,7 +737,9 @@ make_java_files(Globals, MainModuleName, ObjModules, Succeeded, !Info, !IO) :-
|
||||
Timestamps0 = !.Info ^ mki_file_timestamps,
|
||||
map.foldl(delete_java_class_timestamps, Timestamps0,
|
||||
map.init, Timestamps),
|
||||
!Info ^ mki_file_timestamps := Timestamps
|
||||
!Info ^ mki_file_timestamps := Timestamps,
|
||||
% For simplicity, clear out all target file timestamps.
|
||||
!Info ^ mki_target_file_timestamps := map.init
|
||||
).
|
||||
|
||||
:- pred out_of_date_java_modules(globals::in, list(module_name)::in,
|
||||
|
||||
@@ -116,14 +116,14 @@ make_process_compiler_args(Globals, DetectedGradeFlags, Variables, OptionArgs,
|
||||
|
||||
map.init(ModuleDependencies),
|
||||
map.init(FileTimestamps),
|
||||
map.init(SearchFileNameCache),
|
||||
map.init(TargetTimestamps),
|
||||
set.init(ErrorFileModules),
|
||||
MaybeImportingModule = maybe.no,
|
||||
MaybeStdoutLock = maybe.no,
|
||||
MakeInfo0 = make_info(
|
||||
ModuleDependencies,
|
||||
FileTimestamps,
|
||||
SearchFileNameCache,
|
||||
TargetTimestamps,
|
||||
DetectedGradeFlags,
|
||||
OptionArgs,
|
||||
Variables,
|
||||
|
||||
@@ -330,13 +330,34 @@ get_dependency_timestamp(Globals, DependencyFile, MaybeTimestamp, !Info,
|
||||
get_target_timestamp(Globals, Search, TargetFile, MaybeTimestamp, !Info,
|
||||
!IO) :-
|
||||
TargetFile = target_file(_ModuleName, TargetType),
|
||||
get_file_name(Globals, Search, TargetFile, FileName, !Info, !IO),
|
||||
( if TargetType = module_target_analysis_registry then
|
||||
get_file_name(Globals, Search, TargetFile, FileName, !Info, !IO),
|
||||
get_target_timestamp_analysis_registry(Globals, Search, TargetFile,
|
||||
FileName, MaybeTimestamp, !Info, !IO)
|
||||
else
|
||||
get_target_timestamp_2(Globals, Search, TargetFile,
|
||||
FileName, MaybeTimestamp, !Info, !IO)
|
||||
% This path is hit very frequently so it is worth caching timestamps by
|
||||
% target_file. It avoids having to compute a file name for a
|
||||
% target_file first, before looking up the timestamp for that file.
|
||||
TargetFileTimestamps0 = !.Info ^ mki_target_file_timestamps,
|
||||
( if map.search(TargetFileTimestamps0, TargetFile, Timestamp) then
|
||||
MaybeTimestamp = ok(Timestamp)
|
||||
else
|
||||
get_file_name(Globals, Search, TargetFile, FileName, !Info, !IO),
|
||||
get_target_timestamp_2(Globals, Search, TargetFile,
|
||||
FileName, MaybeTimestamp, !Info, !IO),
|
||||
(
|
||||
MaybeTimestamp = ok(Timestamp),
|
||||
TargetFileTimestamps1 = !.Info ^ mki_target_file_timestamps,
|
||||
map.det_insert(TargetFile, Timestamp,
|
||||
TargetFileTimestamps1, TargetFileTimestamps),
|
||||
!Info ^ mki_target_file_timestamps := TargetFileTimestamps
|
||||
;
|
||||
MaybeTimestamp = error(_)
|
||||
% Do not record errors. These would usually be due to files not
|
||||
% yet made, and the result would have to be updated once the
|
||||
% file is made.
|
||||
)
|
||||
)
|
||||
).
|
||||
|
||||
% Special treatment for `.analysis' files. If the corresponding
|
||||
@@ -437,11 +458,10 @@ get_file_name(Globals, Search, TargetFile, FileName, !Info, !IO) :-
|
||||
( if target_type_to_extension(Globals, TargetType, Ext) then
|
||||
(
|
||||
Search = do_search,
|
||||
module_name_to_search_file_name_cache(Globals, Ext,
|
||||
ModuleName, FileName, !Info, !IO)
|
||||
module_name_to_search_file_name(Globals, $pred, Ext,
|
||||
ModuleName, FileName, !IO)
|
||||
;
|
||||
Search = do_not_search,
|
||||
% Not common enough to cache.
|
||||
module_name_to_file_name(Globals, $pred, do_not_create_dirs,
|
||||
Ext, ModuleName, FileName, !IO)
|
||||
)
|
||||
@@ -451,23 +471,6 @@ get_file_name(Globals, Search, TargetFile, FileName, !Info, !IO) :-
|
||||
)
|
||||
).
|
||||
|
||||
:- pred module_name_to_search_file_name_cache(globals::in, ext::in,
|
||||
module_name::in, string::out, make_info::in, make_info::out,
|
||||
io::di, io::uo) is det.
|
||||
|
||||
module_name_to_search_file_name_cache(Globals, Ext, ModuleName, FileName,
|
||||
!Info, !IO) :-
|
||||
Key = module_name_ext(ModuleName, Ext),
|
||||
Cache0 = !.Info ^ mki_search_file_name_cache,
|
||||
( if map.search(Cache0, Key, FileName0) then
|
||||
FileName = FileName0
|
||||
else
|
||||
module_name_to_search_file_name(Globals, $pred, Ext,
|
||||
ModuleName, FileName, !IO),
|
||||
map.det_insert(Key, FileName, Cache0, Cache),
|
||||
!Info ^ mki_search_file_name_cache := Cache
|
||||
).
|
||||
|
||||
get_file_timestamp(SearchDirs, FileName, MaybeTimestamp, !Info, !IO) :-
|
||||
FileTimestamps0 = !.Info ^ mki_file_timestamps,
|
||||
( if map.search(FileTimestamps0, FileName, MaybeTimestamp0) then
|
||||
@@ -562,7 +565,10 @@ make_remove_file(Globals, VerboseOption, FileName, !Info, !IO) :-
|
||||
io.file.remove_file_recursively(FileName, _, !IO),
|
||||
FileTimestamps0 = !.Info ^ mki_file_timestamps,
|
||||
map.delete(FileName, FileTimestamps0, FileTimestamps),
|
||||
!Info ^ mki_file_timestamps := FileTimestamps.
|
||||
!Info ^ mki_file_timestamps := FileTimestamps,
|
||||
|
||||
% For simplicity, clear out all target file timestamps.
|
||||
!Info ^ mki_target_file_timestamps := map.init.
|
||||
|
||||
:- pred report_remove_file(string::in, io::di, io::uo) is det.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user