CompilerArgs: make lookup faster

in order to deduplicate arguments as much as possible, we need to check
if a argument is already part of the list. Which is quite slow because
we are checking 3 lists for that.

In smaller projects this might be not of interested. However, in efl
things are quite "heavy", alone generating the ninja file took 40 sec..
16 sec. are spent in __iadd__ of CompilerArgs.

What this patch does to speed this all up is:
1. We only check if a element is in post when we know that it must be in
   post same for pre.
2. the checks for if we already do contain a specific value are now done
   via a dict, and not via the list.

This overall brings the time from 16 sec. spent in __iadd__ to 9 sec. in
__iadd__.

Another possible solution for all this is to have a internal structure
of CompileArgs of trees, so you have the "basic" layer of arguments
where the position does not matter. Additionally, you have two stacks of
lists, the pre stack and the post stack, duplicates can then be checked
when itereting, which would safe another ~4s in terms of efl. However, i
do not have time for this undertaking right now.
pull/6718/head
Marcel Hollerbach 5 years ago committed by Jussi Pakkanen
parent 25cbcb19a9
commit 4524088d38
  1. 33
      mesonbuild/compilers/compilers.py

@ -15,6 +15,7 @@
import contextlib, os.path, re, tempfile
import collections.abc
import typing as T
from functools import lru_cache
from ..linkers import StaticLinker, GnuLikeDynamicLinkerMixin, SolarisDynamicLinker
from .. import coredata
@ -445,6 +446,9 @@ class CompilerArgs(collections.abc.MutableSequence):
iterable: T.Optional[T.Iterable[str]] = None):
self.compiler = compiler
self.__container = list(iterable) if iterable is not None else [] # type: T.List[str]
self.__seen_args = set()
for arg in self.__container:
self.__seen_args.add(arg)
@T.overload # noqa: F811
def __getitem__(self, index: int) -> str: # noqa: F811
@ -467,20 +471,27 @@ class CompilerArgs(collections.abc.MutableSequence):
def __setitem__(self, index, value) -> None: # noqa: F811
self.__container[index] = value
for v in value:
self.__seen_args.add(v)
def __delitem__(self, index: T.Union[int, slice]) -> None:
value = self.__container[index]
del self.__container[index]
if value in self.__seen_args and value in self.__container: # this is also honoring that you can have duplicated entries
self.__seen_args.remove(value)
def __len__(self) -> int:
return len(self.__container)
def insert(self, index: int, value: str) -> None:
self.__container.insert(index, value)
self.__seen_args.add(value)
def copy(self) -> 'CompilerArgs':
return CompilerArgs(self.compiler, self.__container.copy())
@classmethod
@lru_cache(maxsize=None)
def _can_dedup(cls, arg):
'''
Returns whether the argument can be safely de-duped. This is dependent
@ -526,6 +537,7 @@ class CompilerArgs(collections.abc.MutableSequence):
return 0
@classmethod
@lru_cache(maxsize=None)
def _should_prepend(cls, arg):
if arg.startswith(cls.prepend_prefixes):
return True
@ -588,6 +600,7 @@ class CompilerArgs(collections.abc.MutableSequence):
self.append(arg)
else:
self.__container.append(arg)
self.__seen_args.add(arg)
def extend_direct(self, iterable: T.Iterable[str]) -> None:
'''
@ -619,6 +632,7 @@ class CompilerArgs(collections.abc.MutableSequence):
Add two CompilerArgs while taking into account overriding of arguments
and while preserving the order of arguments as much as possible
'''
this_round_added = set() # a dict that contains a value, when the value was added this round
pre = [] # type: T.List[str]
post = [] # type: T.List[str]
if not isinstance(args, collections.abc.Iterable):
@ -630,20 +644,25 @@ class CompilerArgs(collections.abc.MutableSequence):
dedup = self._can_dedup(arg)
if dedup == 1:
# Argument already exists and adding a new instance is useless
if arg in self or arg in pre or arg in post:
if arg in self.__seen_args or arg in pre or arg in post:
continue
should_prepend = self._should_prepend(arg)
if dedup == 2:
# Remove all previous occurrences of the arg and add it anew
if arg in self:
if arg in self.__seen_args and arg not in this_round_added: #if __seen_args contains arg as well as this_round_added, then its not yet part in self.
self.remove(arg)
if arg in pre:
pre.remove(arg)
if arg in post:
post.remove(arg)
if self._should_prepend(arg):
if should_prepend:
if arg in pre:
pre.remove(arg)
else:
if arg in post:
post.remove(arg)
if should_prepend:
pre.append(arg)
else:
post.append(arg)
self.__seen_args.add(arg)
this_round_added.add(arg)
# Insert at the beginning
self[:0] = pre
# Append to the end

Loading…
Cancel
Save