From 06366f5db7db26b7b4bd56bcec8d193ff4a975dd Mon Sep 17 00:00:00 2001 From: "Vinay Shankar (e24113)" Date: Fri, 18 Aug 2017 13:55:58 -0500 Subject: [PATCH] KARAF-5314 Added a features cache that increases the performance of the filtering of the required features from the available features. The performance of the filtering logic decresed when the java streams API was used in 4.1 compared to for loops in 4.0. I reverted the filtering logic to use for loops and also introduced a features cache. This increased the performance by a huge margin, almost 20X faster when there are ~900 available features and ~300 required features from a highly complex and huge feature dependency tree. --- .../karaf/profile/assembly/Builder.java | 38 +++++++++++++++---- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/profile/src/main/java/org/apache/karaf/profile/assembly/Builder.java b/profile/src/main/java/org/apache/karaf/profile/assembly/Builder.java index 27b83a6deb3..e9cc67e07d8 100644 --- a/profile/src/main/java/org/apache/karaf/profile/assembly/Builder.java +++ b/profile/src/main/java/org/apache/karaf/profile/assembly/Builder.java @@ -39,6 +39,7 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.TreeMap; import java.util.TreeSet; @@ -49,7 +50,6 @@ import java.util.jar.Attributes; import java.util.jar.Manifest; import java.util.regex.Pattern; -import java.util.stream.Collectors; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; @@ -805,10 +805,11 @@ private void installStage(Profile installedProfile, Set allBootFeatures allInstalledFeatures.addAll(repo.getFeature()); } Set installedFeatures = new LinkedHashSet<>(); + Map> featuresCache = new HashMap<>(); // Add boot features for search allInstalledFeatures.addAll(allBootFeatures); for (String feature : installedEffective.getFeatures()) { - addFeatures(allInstalledFeatures, feature, installedFeatures, true); + addFeatures(allInstalledFeatures, feature, installedFeatures, true, featuresCache); } ArtifactInstaller installer = new ArtifactInstaller(systemDirectory, downloader, blacklistedBundles); for (Feature feature : installedFeatures) { @@ -882,7 +883,8 @@ private Set bootStage(Profile bootProfile, Profile startupEffective) th // Compute startup feature dependencies Set bootFeatures = new HashSet<>(); - addFeatures(allBootFeatures, generated.getName(), bootFeatures, true); + Map> featuresCache = new HashMap<>(); + addFeatures(allBootFeatures, generated.getName(), bootFeatures, true, featuresCache); for (Feature feature : bootFeatures) { // the feature is a startup feature, updating startup.properties file LOGGER.info(" Feature " + feature.getId() + " is defined as a boot feature"); @@ -1239,14 +1241,16 @@ private Profile startupStage(Profile startupProfile) throws Exception { return startupEffective; } - private void addFeatures(Set allFeatures, String feature, Set features, boolean mandatory) { + private void addFeatures(Set allFeatures, String feature, Set features, boolean mandatory, Map> featuresCache) { String name; + Version osgiVersion; VersionRange range; int idx = feature.indexOf('/'); if (idx > 0) { name = feature.substring(0, idx); String version = feature.substring(idx + 1); version = version.trim(); + osgiVersion = VersionTable.getVersion(version); if (version.equals(org.apache.karaf.features.internal.model.Feature.DEFAULT_VERSION)) { range = new VersionRange(Version.emptyVersion); } else { @@ -1254,18 +1258,36 @@ private void addFeatures(Set allFeatures, String feature, Set } } else { name = feature; + osgiVersion = Version.emptyVersion; range = new VersionRange(Version.emptyVersion); } - Set set = allFeatures.stream() - .filter(f -> f.getName().equals(name) && range.contains(VersionTable.getVersion(f.getVersion()))) - .collect(Collectors.toSet()); + Set set = new HashSet<>(); + boolean featurePresentInCache = false; + Optional> optionalVersionFeatureMap = Optional.ofNullable(featuresCache.get(name)); + if(optionalVersionFeatureMap.isPresent()) { + Optional cachedFeature = Optional.ofNullable(optionalVersionFeatureMap.get().get(osgiVersion)); + if(cachedFeature.isPresent()){ + set.add(cachedFeature.get()); + featurePresentInCache = true; + } + } + if(!featurePresentInCache) { + for (Feature f : allFeatures) { + if (f.getName().equals(name) && range.contains(VersionTable.getVersion(f.getVersion()))) { + set.add(f); + Map versionFeatureMap = Optional.ofNullable(featuresCache.get(name)).orElse(new HashMap<>()); + versionFeatureMap.put(osgiVersion, f); + featuresCache.put(name, versionFeatureMap); + } + } + } if (mandatory && set.isEmpty()) { throw new IllegalStateException("Could not find matching feature for " + feature); } for (Feature f : set) { features.add(f); for (Dependency dep : f.getFeature()) { - addFeatures(allFeatures, dep.toString(), features, !dep.isDependency() && !dep.isPrerequisite()); + addFeatures(allFeatures, dep.toString(), features, !dep.isDependency() && !dep.isPrerequisite(), featuresCache); } } }