Repository: holoviz/panel

Issue:
Speed up initial load by 5-10% by making look up of config values efficient
I've been profiling different apps. What I see is that `config.__getattribute__` is mentioned all over the place taking up more than 5% of the time. Looking up the `loading_indicator` value on `pn.config` should not be costly 😄 

## Reproduce

```python
import panel as pn

N=10

pn.Column(
    *[pn.Row(
        *[pn.pane.Markdown("Hello World") for i in range(N)]
    ) for i in range(N)]
).servable()
```

```bash
panel serve script.py --autoreload --index script --admin --profiler pyinstrument
```

Open the app 5 times and visit the pyinstrument report.

![image](https://github.com/holoviz/panel/assets/42288570/6930a72f-f34f-4ebd-b50f-441fac029837)

For me `config.__get_attribute__` adds up to 11.5% of the time spent!.


Discussion:

Resolving patch:
diff --git a/panel/config.py b/panel/config.py
--- a/panel/config.py
+++ b/panel/config.py
@@ -43,7 +43,6 @@
 
 _PATH = os.path.abspath(os.path.dirname(__file__))
 
-
 def validate_config(config, parameter, value):
     """
     Validates parameter setting on a hidden config parameter.
@@ -319,14 +318,14 @@ class _config(_base_config):
         The theme to apply to components.""")
 
     # Global parameters that are shared across all sessions
-    _globals = [
+    _globals = {
         'admin_plugins', 'autoreload', 'comms', 'cookie_secret',
         'nthreads', 'oauth_provider', 'oauth_expiry', 'oauth_key',
         'oauth_secret', 'oauth_jwt_user', 'oauth_redirect_uri',
         'oauth_encryption_key', 'oauth_extra_params', 'npm_cdn',
         'layout_compatibility', 'oauth_refresh_tokens', 'oauth_guest_endpoints',
-        'oauth_optional'
-    ]
+        'oauth_optional', 'admin'
+    }
 
     _truthy = ['True', 'true', '1', True, 1]
 
@@ -335,8 +334,8 @@ class _config(_base_config):
     def __init__(self, **params):
         super().__init__(**params)
         self._validating = False
-        for p in self.param:
-            if p.startswith('_') and p[1:] not in self._globals:
+        for p in self._parameter_set:
+            if p.startswith('_') and p[1:] not in _config._globals:
                 setattr(self, p+'_', None)
         if self.log_level:
             panel_log_handler.setLevel(self.log_level)
@@ -371,8 +370,8 @@ def _enable_notifications(self):
     def set(self, **kwargs):
         values = [(k, v) for k, v in self.param.values().items() if k != 'name']
         overrides = [
-            (k, getattr(self, k+'_')) for k in self.param
-            if k.startswith('_') and k[1:] not in self._globals
+            (k, getattr(self, k+'_')) for k in _config._parameter_set
+            if k.startswith('_') and k[1:] not in _config._globals
         ]
         for k, v in kwargs.items():
             setattr(self, k, v)
@@ -394,12 +393,12 @@ def __setattr__(self, attr, value):
         if not init or (attr.startswith('_') and attr.endswith('_')) or attr == '_validating':
             return super().__setattr__(attr, value)
         value = getattr(self, f'_{attr}_hook', lambda x: x)(value)
-        if attr in self._globals or self.param._TRIGGER:
+        if attr in _config._globals or self.param._TRIGGER:
             super().__setattr__(attr if attr in self.param else f'_{attr}', value)
         elif state.curdoc is not None:
-            if attr in self.param:
+            if attr in _config._parameter_set:
                 validate_config(self, attr, value)
-            elif f'_{attr}' in self.param:
+            elif f'_{attr}' in _config._parameter_set:
                 validate_config(self, f'_{attr}', value)
             else:
                 raise AttributeError(f'{attr!r} is not a valid config parameter.')
@@ -409,7 +408,7 @@ def __setattr__(self, attr, value):
             watchers = param_watchers(self).get(attr, {}).get('value', [])
             for w in watchers:
                 w.fn()
-        elif f'_{attr}' in self.param and hasattr(self, f'_{attr}_'):
+        elif f'_{attr}' in _config._parameter_set and hasattr(self, f'_{attr}_'):
             validate_config(self, f'_{attr}', value)
             super().__setattr__(f'_{attr}_', value)
         else:
@@ -431,21 +430,11 @@ def __getattribute__(self, attr):
         ensure that even on first access mutable parameters do not
         end up being modified.
         """
-        from .io.state import state
+        if attr in ('_param__private', '_globals', '_parameter_set', '__class__', 'param'):
+            return super().__getattribute__(attr)
 
-        if attr == '_param__private':
-            return super().__getattribute__('_param__private')
+        from .io.state import state
 
-        # _param__private added in Param 2
-        try:
-            init = super().__getattribute__('_param__private').initialized
-        except AttributeError:
-            init = super().__getattribute__('initialized')
-        global_params = super().__getattribute__('_globals')
-        if init and not attr.startswith('__'):
-            params = super().__getattribute__('param')
-        else:
-            params = []
         session_config = super().__getattribute__('_session_config')
         curdoc = state.curdoc
         if curdoc and curdoc not in session_config:
@@ -454,11 +443,11 @@ def __getattribute__(self, attr):
             curdoc and attr not in session_config[curdoc]):
             new_obj = copy.copy(super().__getattribute__(attr))
             setattr(self, attr, new_obj)
-        if attr in global_params or attr == 'theme':
+        if attr in _config._globals or attr == 'theme':
             return super().__getattribute__(attr)
         elif curdoc and curdoc in session_config and attr in session_config[curdoc]:
             return session_config[curdoc][attr]
-        elif f'_{attr}' in params and getattr(self, f'_{attr}_') is not None:
+        elif f'_{attr}' in _config._parameter_set and getattr(self, f'_{attr}_') is not None:
             return super().__getattribute__(f'_{attr}_')
         return super().__getattribute__(attr)
 
@@ -624,7 +613,7 @@ def theme(self):
     _params = _config.param.objects()
 else:
     _params = _config.param.params()
-
+_config._parameter_set = set(_params)
 config = _config(**{k: None if p.allow_None else getattr(_config, k)
                     for k, p in _params.items() if k != 'name'})
 


Test patch: