Optimizations for TypeSafeDriverConfig (#1616)

* TypesafeDriverConfig: getProfile can avoid calling containsKey

On a typical applicative workload, 0.53% of CPU is spend resolving
configuration profiles. By getting the profile first and failing if it
is null we can easily cut that in half.

* TypesafeDriverConfig: optimize getDefaultProfile

We could easily get a dedicated field for the default profile thus
avoiding recurring maps lookups.

* fixup! TypesafeDriverConfig: optimize getDefaultProfile
diff --git a/core/src/main/java/com/datastax/oss/driver/internal/core/config/typesafe/TypesafeDriverConfig.java b/core/src/main/java/com/datastax/oss/driver/internal/core/config/typesafe/TypesafeDriverConfig.java
index cc3f841..ca5d919 100644
--- a/core/src/main/java/com/datastax/oss/driver/internal/core/config/typesafe/TypesafeDriverConfig.java
+++ b/core/src/main/java/com/datastax/oss/driver/internal/core/config/typesafe/TypesafeDriverConfig.java
@@ -32,6 +32,7 @@
 import edu.umd.cs.findbugs.annotations.NonNull;
 import java.net.URL;
 import java.util.Map;
+import java.util.Optional;
 import java.util.concurrent.ConcurrentHashMap;
 import net.jcip.annotations.ThreadSafe;
 import org.slf4j.Logger;
@@ -50,6 +51,8 @@
 
   private final Map<DriverOption, Object> defaultOverrides = new ConcurrentHashMap<>();
 
+  private final TypesafeDriverExecutionProfile.Base defaultProfile;
+
   public TypesafeDriverConfig(Config config) {
     this.lastLoadedConfig = config;
     Map<String, Config> profileConfigs = extractProfiles(config);
@@ -62,6 +65,7 @@
           new TypesafeDriverExecutionProfile.Base(entry.getKey(), entry.getValue()));
     }
     this.profiles = builder.build();
+    this.defaultProfile = profiles.get(DriverExecutionProfile.DEFAULT_NAME);
   }
 
   /** @return whether the configuration changed */
@@ -136,14 +140,19 @@
     return result.build();
   }
 
+  @Override
+  public DriverExecutionProfile getDefaultProfile() {
+    return defaultProfile;
+  }
+
   @NonNull
   @Override
   public DriverExecutionProfile getProfile(@NonNull String profileName) {
-    Preconditions.checkArgument(
-        profiles.containsKey(profileName),
-        "Unknown profile '%s'. Check your configuration.",
-        profileName);
-    return profiles.get(profileName);
+    if (profileName.equals(DriverExecutionProfile.DEFAULT_NAME)) {
+      return defaultProfile;
+    }
+    return Optional.ofNullable(profiles.get(profileName))
+      .orElseThrow(() -> new IllegalArgumentException(String.format("Unknown profile '%s'. Check your configuration.", profileName)));
   }
 
   @NonNull
diff --git a/core/src/test/java/com/datastax/oss/driver/internal/core/config/typesafe/TypesafeDriverConfigTest.java b/core/src/test/java/com/datastax/oss/driver/internal/core/config/typesafe/TypesafeDriverConfigTest.java
index 019d50a..b394796 100644
--- a/core/src/test/java/com/datastax/oss/driver/internal/core/config/typesafe/TypesafeDriverConfigTest.java
+++ b/core/src/test/java/com/datastax/oss/driver/internal/core/config/typesafe/TypesafeDriverConfigTest.java
@@ -171,6 +171,14 @@
             entry("int1", 45));
   }
 
+  @Test
+  public void should_update_default_profile_on_reload() {
+    TypesafeDriverConfig config = parse("int1 = 42\n profiles { profile1 { int1 = 43 } }");
+    assertThat(config.getDefaultProfile().getInt(MockOptions.INT1)).isEqualTo(42);
+    config.reload(ConfigFactory.parseString("int1 = 44\n profiles { profile1 { int1 = 45 } }"));
+    assertThat(config.getDefaultProfile().getInt(MockOptions.INT1)).isEqualTo(44);
+  }
+
   private TypesafeDriverConfig parse(String configString) {
     Config config = ConfigFactory.parseString(configString);
     return new TypesafeDriverConfig(config);