We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Checking isinstance of a Protocol is slow
### Version of Awkward Array

2.0.2

### Description and code to reproduce

In a profiling run of a coffea processor with awkward2 (eager mode) I found that 20% of the time was spent in the following line:
https://github.com/scikit-hep/awkward/blob/7e6f504c3cb0310cdbe0be7b5d662722ee73aaa7/src/awkward/contents/content.py#L94
![image](https://user-images.githubusercontent.com/6587412/209037377-c6ad3e62-7349-470c-a340-d0dd223f0a90.png)
This instance check would normally be very fast but I suspect because the type is a `@runtime_checkable` protocol, it is doing more work.
https://github.com/scikit-hep/awkward/blob/7e6f504c3cb0310cdbe0be7b5d662722ee73aaa7/src/awkward/_backends.py#L42-L45

Perhaps there is a way to have it first check the class `__mro__` and then fall back to the protocol?

If this time is removed from the profile, the remaining time is in line with what I get running the same processor in awkward 1.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `src/awkward/_backends.py`
Content:
```
1 from __future__ import annotations
2 
3 from abc import abstractmethod
4 
5 import awkward_cpp
6 
7 import awkward as ak
8 from awkward._nplikes import (
9     Cupy,
10     CupyKernel,
11     Jax,
12     JaxKernel,
13     Numpy,
14     NumpyKernel,
15     NumpyLike,
16     NumpyMetadata,
17     Singleton,
18     nplike_of,
19 )
20 from awkward._typetracer import NoKernel, TypeTracer
21 from awkward.typing import (
22     Any,
23     Callable,
24     Final,
25     Protocol,
26     Self,
27     Tuple,
28     TypeAlias,
29     TypeVar,
30     Unpack,
31     runtime_checkable,
32 )
33 
34 np = NumpyMetadata.instance()
35 
36 
37 T = TypeVar("T", covariant=True)
38 KernelKeyType: TypeAlias = Tuple[str, Unpack[Tuple[np.dtype, ...]]]
39 KernelType: TypeAlias = Callable[..., None]
40 
41 
42 @runtime_checkable
43 class Backend(Protocol[T]):
44     name: str
45 
46     @property
47     @abstractmethod
48     def nplike(self) -> NumpyLike:
49         raise ak._errors.wrap_error(NotImplementedError)
50 
51     @property
52     @abstractmethod
53     def index_nplike(self) -> NumpyLike:
54         raise ak._errors.wrap_error(NotImplementedError)
55 
56     @classmethod
57     @abstractmethod
58     def instance(cls) -> Self:
59         raise ak._errors.wrap_error(NotImplementedError)
60 
61     def __getitem__(self, key: KernelKeyType) -> KernelType:
62         raise ak._errors.wrap_error(NotImplementedError)
63 
64 
65 class NumpyBackend(Singleton, Backend[Any]):
66     name: Final[str] = "cpu"
67 
68     _numpy: Numpy
69 
70     @property
71     def nplike(self) -> Numpy:
72         return self._numpy
73 
74     @property
75     def index_nplike(self) -> Numpy:
76         return self._numpy
77 
78     def __init__(self):
79         self._numpy = Numpy.instance()
80 
81     def __getitem__(self, index: KernelKeyType) -> NumpyKernel:
82         return NumpyKernel(awkward_cpp.cpu_kernels.kernel[index], index)
83 
84 
85 class CupyBackend(Singleton, Backend[Any]):
86     name: Final[str] = "cuda"
87 
88     _cupy: Cupy
89 
90     @property
91     def nplike(self) -> Cupy:
92         return self._cupy
93 
94     @property
95     def index_nplike(self) -> Cupy:
96         return self._cupy
97 
98     def __init__(self):
99         self._cupy = Cupy.instance()
100 
101     def __getitem__(self, index: KernelKeyType) -> CupyKernel | NumpyKernel:
102         from awkward._connect import cuda
103 
104         cupy = cuda.import_cupy("Awkward Arrays with CUDA")
105         _cuda_kernels = cuda.initialize_cuda_kernels(cupy)
106         func = _cuda_kernels[index]
107         if func is not None:
108             return CupyKernel(func, index)
109         else:
110             raise ak._errors.wrap_error(
111                 AssertionError(f"CuPyKernel not found: {index!r}")
112             )
113 
114 
115 class JaxBackend(Singleton, Backend[Any]):
116     name: Final[str] = "jax"
117 
118     _jax: Jax
119     _numpy: Numpy
120 
121     @property
122     def nplike(self) -> Jax:
123         return self._jax
124 
125     @property
126     def index_nplike(self) -> Numpy:
127         return self._numpy
128 
129     def __init__(self):
130         self._jax = Jax.instance()
131         self._numpy = Numpy.instance()
132 
133     def __getitem__(self, index: KernelKeyType) -> JaxKernel:
134         # JAX uses Awkward's C++ kernels for index-only operations
135         return JaxKernel(awkward_cpp.cpu_kernels.kernel[index], index)
136 
137 
138 class TypeTracerBackend(Singleton, Backend[Any]):
139     name: Final[str] = "typetracer"
140 
141     _typetracer: TypeTracer
142 
143     @property
144     def nplike(self) -> TypeTracer:
145         return self._typetracer
146 
147     @property
148     def index_nplike(self) -> TypeTracer:
149         return self._typetracer
150 
151     def __init__(self):
152         self._typetracer = TypeTracer.instance()
153 
154     def __getitem__(self, index: KernelKeyType) -> NoKernel:
155         return NoKernel(index)
156 
157 
158 def _backend_for_nplike(nplike: ak._nplikes.NumpyLike) -> Backend:
159     # Currently there exists a one-to-one relationship between the nplike
160     # and the backend. In future, this might need refactoring
161     if isinstance(nplike, Numpy):
162         return NumpyBackend.instance()
163     elif isinstance(nplike, Cupy):
164         return CupyBackend.instance()
165     elif isinstance(nplike, Jax):
166         return JaxBackend.instance()
167     elif isinstance(nplike, TypeTracer):
168         return TypeTracerBackend.instance()
169     else:
170         raise ak._errors.wrap_error(ValueError("unrecognised nplike", nplike))
171 
172 
173 _UNSET = object()
174 D = TypeVar("D")
175 
176 
177 def backend_of(*objects, default: D = _UNSET) -> Backend | D:
178     """
179     Args:
180         objects: objects for which to find a suitable backend
181         default: value to return if no backend is found.
182 
183     Return the most suitable backend for the given objects (e.g. arrays, layouts). If no
184     suitable backend is found, return the `default` value, or raise a `ValueError` if
185     no default is given.
186     """
187     nplike = nplike_of(*objects, default=None)
188     if nplike is not None:
189         return _backend_for_nplike(nplike)
190     elif default is _UNSET:
191         raise ak._errors.wrap_error(ValueError("could not find backend for", objects))
192     else:
193         return default
194 
195 
196 _backends: Final[dict[str, type[Backend]]] = {
197     b.name: b for b in (NumpyBackend, CupyBackend, JaxBackend, TypeTracerBackend)
198 }
199 
200 
201 def regularize_backend(backend: str | Backend) -> Backend:
202     if isinstance(backend, Backend):
203         return backend
204     elif backend in _backends:
205         return _backends[backend].instance()
206     else:
207         raise ak._errors.wrap_error(ValueError(f"No such backend {backend!r} exists."))
208
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/src/awkward/_backends.py b/src/awkward/_backends.py
--- a/src/awkward/_backends.py
+++ b/src/awkward/_backends.py
@@ -1,6 +1,6 @@
 from __future__ import annotations
 
-from abc import abstractmethod
+from abc import ABC, abstractmethod
 
 import awkward_cpp
 
@@ -18,18 +18,7 @@
     nplike_of,
 )
 from awkward._typetracer import NoKernel, TypeTracer
-from awkward.typing import (
-    Any,
-    Callable,
-    Final,
-    Protocol,
-    Self,
-    Tuple,
-    TypeAlias,
-    TypeVar,
-    Unpack,
-    runtime_checkable,
-)
+from awkward.typing import Callable, Final, Tuple, TypeAlias, TypeVar, Unpack
 
 np = NumpyMetadata.instance()
 
@@ -39,8 +28,7 @@
 KernelType: TypeAlias = Callable[..., None]
 
 
-@runtime_checkable
-class Backend(Protocol[T]):
+class Backend(Singleton, ABC):
     name: str
 
     @property
@@ -53,16 +41,11 @@
     def index_nplike(self) -> NumpyLike:
         raise ak._errors.wrap_error(NotImplementedError)
 
-    @classmethod
-    @abstractmethod
-    def instance(cls) -> Self:
-        raise ak._errors.wrap_error(NotImplementedError)
-
     def __getitem__(self, key: KernelKeyType) -> KernelType:
         raise ak._errors.wrap_error(NotImplementedError)
 
 
-class NumpyBackend(Singleton, Backend[Any]):
+class NumpyBackend(Backend):
     name: Final[str] = "cpu"
 
     _numpy: Numpy
@@ -82,7 +65,7 @@
         return NumpyKernel(awkward_cpp.cpu_kernels.kernel[index], index)
 
 
-class CupyBackend(Singleton, Backend[Any]):
+class CupyBackend(Backend):
     name: Final[str] = "cuda"
 
     _cupy: Cupy
@@ -112,7 +95,7 @@
             )
 
 
-class JaxBackend(Singleton, Backend[Any]):
+class JaxBackend(Backend):
     name: Final[str] = "jax"
 
     _jax: Jax
@@ -135,7 +118,7 @@
         return JaxKernel(awkward_cpp.cpu_kernels.kernel[index], index)
 
 
-class TypeTracerBackend(Singleton, Backend[Any]):
+class TypeTracerBackend(Backend):
     name: Final[str] = "typetracer"
 
     _typetracer: TypeTracer

{"golden_diff": "diff --git a/src/awkward/_backends.py b/src/awkward/_backends.py\n--- a/src/awkward/_backends.py\n+++ b/src/awkward/_backends.py\n@@ -1,6 +1,6 @@\n from __future__ import annotations\n \n-from abc import abstractmethod\n+from abc import ABC, abstractmethod\n \n import awkward_cpp\n \n@@ -18,18 +18,7 @@\n     nplike_of,\n )\n from awkward._typetracer import NoKernel, TypeTracer\n-from awkward.typing import (\n-    Any,\n-    Callable,\n-    Final,\n-    Protocol,\n-    Self,\n-    Tuple,\n-    TypeAlias,\n-    TypeVar,\n-    Unpack,\n-    runtime_checkable,\n-)\n+from awkward.typing import Callable, Final, Tuple, TypeAlias, TypeVar, Unpack\n \n np = NumpyMetadata.instance()\n \n@@ -39,8 +28,7 @@\n KernelType: TypeAlias = Callable[..., None]\n \n \n-@runtime_checkable\n-class Backend(Protocol[T]):\n+class Backend(Singleton, ABC):\n     name: str\n \n     @property\n@@ -53,16 +41,11 @@\n     def index_nplike(self) -> NumpyLike:\n         raise ak._errors.wrap_error(NotImplementedError)\n \n-    @classmethod\n-    @abstractmethod\n-    def instance(cls) -> Self:\n-        raise ak._errors.wrap_error(NotImplementedError)\n-\n     def __getitem__(self, key: KernelKeyType) -> KernelType:\n         raise ak._errors.wrap_error(NotImplementedError)\n \n \n-class NumpyBackend(Singleton, Backend[Any]):\n+class NumpyBackend(Backend):\n     name: Final[str] = \"cpu\"\n \n     _numpy: Numpy\n@@ -82,7 +65,7 @@\n         return NumpyKernel(awkward_cpp.cpu_kernels.kernel[index], index)\n \n \n-class CupyBackend(Singleton, Backend[Any]):\n+class CupyBackend(Backend):\n     name: Final[str] = \"cuda\"\n \n     _cupy: Cupy\n@@ -112,7 +95,7 @@\n             )\n \n \n-class JaxBackend(Singleton, Backend[Any]):\n+class JaxBackend(Backend):\n     name: Final[str] = \"jax\"\n \n     _jax: Jax\n@@ -135,7 +118,7 @@\n         return JaxKernel(awkward_cpp.cpu_kernels.kernel[index], index)\n \n \n-class TypeTracerBackend(Singleton, Backend[Any]):\n+class TypeTracerBackend(Backend):\n     name: Final[str] = \"typetracer\"\n \n     _typetracer: TypeTracer\n", "issue": "Checking isinstance of a Protocol is slow\n### Version of Awkward Array\n\n2.0.2\n\n### Description and code to reproduce\n\nIn a profiling run of a coffea processor with awkward2 (eager mode) I found that 20% of the time was spent in the following line:\r\nhttps://github.com/scikit-hep/awkward/blob/7e6f504c3cb0310cdbe0be7b5d662722ee73aaa7/src/awkward/contents/content.py#L94\r\n![image](https://user-images.githubusercontent.com/6587412/209037377-c6ad3e62-7349-470c-a340-d0dd223f0a90.png)\r\nThis instance check would normally be very fast but I suspect because the type is a `@runtime_checkable` protocol, it is doing more work.\r\nhttps://github.com/scikit-hep/awkward/blob/7e6f504c3cb0310cdbe0be7b5d662722ee73aaa7/src/awkward/_backends.py#L42-L45\r\n\r\nPerhaps there is a way to have it first check the class `__mro__` and then fall back to the protocol?\r\n\r\nIf this time is removed from the profile, the remaining time is in line with what I get running the same processor in awkward 1.\n", "before_files": [{"content": "from __future__ import annotations\n\nfrom abc import abstractmethod\n\nimport awkward_cpp\n\nimport awkward as ak\nfrom awkward._nplikes import (\n    Cupy,\n    CupyKernel,\n    Jax,\n    JaxKernel,\n    Numpy,\n    NumpyKernel,\n    NumpyLike,\n    NumpyMetadata,\n    Singleton,\n    nplike_of,\n)\nfrom awkward._typetracer import NoKernel, TypeTracer\nfrom awkward.typing import (\n    Any,\n    Callable,\n    Final,\n    Protocol,\n    Self,\n    Tuple,\n    TypeAlias,\n    TypeVar,\n    Unpack,\n    runtime_checkable,\n)\n\nnp = NumpyMetadata.instance()\n\n\nT = TypeVar(\"T\", covariant=True)\nKernelKeyType: TypeAlias = Tuple[str, Unpack[Tuple[np.dtype, ...]]]\nKernelType: TypeAlias = Callable[..., None]\n\n\n@runtime_checkable\nclass Backend(Protocol[T]):\n    name: str\n\n    @property\n    @abstractmethod\n    def nplike(self) -> NumpyLike:\n        raise ak._errors.wrap_error(NotImplementedError)\n\n    @property\n    @abstractmethod\n    def index_nplike(self) -> NumpyLike:\n        raise ak._errors.wrap_error(NotImplementedError)\n\n    @classmethod\n    @abstractmethod\n    def instance(cls) -> Self:\n        raise ak._errors.wrap_error(NotImplementedError)\n\n    def __getitem__(self, key: KernelKeyType) -> KernelType:\n        raise ak._errors.wrap_error(NotImplementedError)\n\n\nclass NumpyBackend(Singleton, Backend[Any]):\n    name: Final[str] = \"cpu\"\n\n    _numpy: Numpy\n\n    @property\n    def nplike(self) -> Numpy:\n        return self._numpy\n\n    @property\n    def index_nplike(self) -> Numpy:\n        return self._numpy\n\n    def __init__(self):\n        self._numpy = Numpy.instance()\n\n    def __getitem__(self, index: KernelKeyType) -> NumpyKernel:\n        return NumpyKernel(awkward_cpp.cpu_kernels.kernel[index], index)\n\n\nclass CupyBackend(Singleton, Backend[Any]):\n    name: Final[str] = \"cuda\"\n\n    _cupy: Cupy\n\n    @property\n    def nplike(self) -> Cupy:\n        return self._cupy\n\n    @property\n    def index_nplike(self) -> Cupy:\n        return self._cupy\n\n    def __init__(self):\n        self._cupy = Cupy.instance()\n\n    def __getitem__(self, index: KernelKeyType) -> CupyKernel | NumpyKernel:\n        from awkward._connect import cuda\n\n        cupy = cuda.import_cupy(\"Awkward Arrays with CUDA\")\n        _cuda_kernels = cuda.initialize_cuda_kernels(cupy)\n        func = _cuda_kernels[index]\n        if func is not None:\n            return CupyKernel(func, index)\n        else:\n            raise ak._errors.wrap_error(\n                AssertionError(f\"CuPyKernel not found: {index!r}\")\n            )\n\n\nclass JaxBackend(Singleton, Backend[Any]):\n    name: Final[str] = \"jax\"\n\n    _jax: Jax\n    _numpy: Numpy\n\n    @property\n    def nplike(self) -> Jax:\n        return self._jax\n\n    @property\n    def index_nplike(self) -> Numpy:\n        return self._numpy\n\n    def __init__(self):\n        self._jax = Jax.instance()\n        self._numpy = Numpy.instance()\n\n    def __getitem__(self, index: KernelKeyType) -> JaxKernel:\n        # JAX uses Awkward's C++ kernels for index-only operations\n        return JaxKernel(awkward_cpp.cpu_kernels.kernel[index], index)\n\n\nclass TypeTracerBackend(Singleton, Backend[Any]):\n    name: Final[str] = \"typetracer\"\n\n    _typetracer: TypeTracer\n\n    @property\n    def nplike(self) -> TypeTracer:\n        return self._typetracer\n\n    @property\n    def index_nplike(self) -> TypeTracer:\n        return self._typetracer\n\n    def __init__(self):\n        self._typetracer = TypeTracer.instance()\n\n    def __getitem__(self, index: KernelKeyType) -> NoKernel:\n        return NoKernel(index)\n\n\ndef _backend_for_nplike(nplike: ak._nplikes.NumpyLike) -> Backend:\n    # Currently there exists a one-to-one relationship between the nplike\n    # and the backend. In future, this might need refactoring\n    if isinstance(nplike, Numpy):\n        return NumpyBackend.instance()\n    elif isinstance(nplike, Cupy):\n        return CupyBackend.instance()\n    elif isinstance(nplike, Jax):\n        return JaxBackend.instance()\n    elif isinstance(nplike, TypeTracer):\n        return TypeTracerBackend.instance()\n    else:\n        raise ak._errors.wrap_error(ValueError(\"unrecognised nplike\", nplike))\n\n\n_UNSET = object()\nD = TypeVar(\"D\")\n\n\ndef backend_of(*objects, default: D = _UNSET) -> Backend | D:\n    \"\"\"\n    Args:\n        objects: objects for which to find a suitable backend\n        default: value to return if no backend is found.\n\n    Return the most suitable backend for the given objects (e.g. arrays, layouts). If no\n    suitable backend is found, return the `default` value, or raise a `ValueError` if\n    no default is given.\n    \"\"\"\n    nplike = nplike_of(*objects, default=None)\n    if nplike is not None:\n        return _backend_for_nplike(nplike)\n    elif default is _UNSET:\n        raise ak._errors.wrap_error(ValueError(\"could not find backend for\", objects))\n    else:\n        return default\n\n\n_backends: Final[dict[str, type[Backend]]] = {\n    b.name: b for b in (NumpyBackend, CupyBackend, JaxBackend, TypeTracerBackend)\n}\n\n\ndef regularize_backend(backend: str | Backend) -> Backend:\n    if isinstance(backend, Backend):\n        return backend\n    elif backend in _backends:\n        return _backends[backend].instance()\n    else:\n        raise ak._errors.wrap_error(ValueError(f\"No such backend {backend!r} exists.\"))\n", "path": "src/awkward/_backends.py"}], "after_files": [{"content": "from __future__ import annotations\n\nfrom abc import ABC, abstractmethod\n\nimport awkward_cpp\n\nimport awkward as ak\nfrom awkward._nplikes import (\n    Cupy,\n    CupyKernel,\n    Jax,\n    JaxKernel,\n    Numpy,\n    NumpyKernel,\n    NumpyLike,\n    NumpyMetadata,\n    Singleton,\n    nplike_of,\n)\nfrom awkward._typetracer import NoKernel, TypeTracer\nfrom awkward.typing import Callable, Final, Tuple, TypeAlias, TypeVar, Unpack\n\nnp = NumpyMetadata.instance()\n\n\nT = TypeVar(\"T\", covariant=True)\nKernelKeyType: TypeAlias = Tuple[str, Unpack[Tuple[np.dtype, ...]]]\nKernelType: TypeAlias = Callable[..., None]\n\n\nclass Backend(Singleton, ABC):\n    name: str\n\n    @property\n    @abstractmethod\n    def nplike(self) -> NumpyLike:\n        raise ak._errors.wrap_error(NotImplementedError)\n\n    @property\n    @abstractmethod\n    def index_nplike(self) -> NumpyLike:\n        raise ak._errors.wrap_error(NotImplementedError)\n\n    def __getitem__(self, key: KernelKeyType) -> KernelType:\n        raise ak._errors.wrap_error(NotImplementedError)\n\n\nclass NumpyBackend(Backend):\n    name: Final[str] = \"cpu\"\n\n    _numpy: Numpy\n\n    @property\n    def nplike(self) -> Numpy:\n        return self._numpy\n\n    @property\n    def index_nplike(self) -> Numpy:\n        return self._numpy\n\n    def __init__(self):\n        self._numpy = Numpy.instance()\n\n    def __getitem__(self, index: KernelKeyType) -> NumpyKernel:\n        return NumpyKernel(awkward_cpp.cpu_kernels.kernel[index], index)\n\n\nclass CupyBackend(Backend):\n    name: Final[str] = \"cuda\"\n\n    _cupy: Cupy\n\n    @property\n    def nplike(self) -> Cupy:\n        return self._cupy\n\n    @property\n    def index_nplike(self) -> Cupy:\n        return self._cupy\n\n    def __init__(self):\n        self._cupy = Cupy.instance()\n\n    def __getitem__(self, index: KernelKeyType) -> CupyKernel | NumpyKernel:\n        from awkward._connect import cuda\n\n        cupy = cuda.import_cupy(\"Awkward Arrays with CUDA\")\n        _cuda_kernels = cuda.initialize_cuda_kernels(cupy)\n        func = _cuda_kernels[index]\n        if func is not None:\n            return CupyKernel(func, index)\n        else:\n            raise ak._errors.wrap_error(\n                AssertionError(f\"CuPyKernel not found: {index!r}\")\n            )\n\n\nclass JaxBackend(Backend):\n    name: Final[str] = \"jax\"\n\n    _jax: Jax\n    _numpy: Numpy\n\n    @property\n    def nplike(self) -> Jax:\n        return self._jax\n\n    @property\n    def index_nplike(self) -> Numpy:\n        return self._numpy\n\n    def __init__(self):\n        self._jax = Jax.instance()\n        self._numpy = Numpy.instance()\n\n    def __getitem__(self, index: KernelKeyType) -> JaxKernel:\n        # JAX uses Awkward's C++ kernels for index-only operations\n        return JaxKernel(awkward_cpp.cpu_kernels.kernel[index], index)\n\n\nclass TypeTracerBackend(Backend):\n    name: Final[str] = \"typetracer\"\n\n    _typetracer: TypeTracer\n\n    @property\n    def nplike(self) -> TypeTracer:\n        return self._typetracer\n\n    @property\n    def index_nplike(self) -> TypeTracer:\n        return self._typetracer\n\n    def __init__(self):\n        self._typetracer = TypeTracer.instance()\n\n    def __getitem__(self, index: KernelKeyType) -> NoKernel:\n        return NoKernel(index)\n\n\ndef _backend_for_nplike(nplike: ak._nplikes.NumpyLike) -> Backend:\n    # Currently there exists a one-to-one relationship between the nplike\n    # and the backend. In future, this might need refactoring\n    if isinstance(nplike, Numpy):\n        return NumpyBackend.instance()\n    elif isinstance(nplike, Cupy):\n        return CupyBackend.instance()\n    elif isinstance(nplike, Jax):\n        return JaxBackend.instance()\n    elif isinstance(nplike, TypeTracer):\n        return TypeTracerBackend.instance()\n    else:\n        raise ak._errors.wrap_error(ValueError(\"unrecognised nplike\", nplike))\n\n\n_UNSET = object()\nD = TypeVar(\"D\")\n\n\ndef backend_of(*objects, default: D = _UNSET) -> Backend | D:\n    \"\"\"\n    Args:\n        objects: objects for which to find a suitable backend\n        default: value to return if no backend is found.\n\n    Return the most suitable backend for the given objects (e.g. arrays, layouts). If no\n    suitable backend is found, return the `default` value, or raise a `ValueError` if\n    no default is given.\n    \"\"\"\n    nplike = nplike_of(*objects, default=None)\n    if nplike is not None:\n        return _backend_for_nplike(nplike)\n    elif default is _UNSET:\n        raise ak._errors.wrap_error(ValueError(\"could not find backend for\", objects))\n    else:\n        return default\n\n\n_backends: Final[dict[str, type[Backend]]] = {\n    b.name: b for b in (NumpyBackend, CupyBackend, JaxBackend, TypeTracerBackend)\n}\n\n\ndef regularize_backend(backend: str | Backend) -> Backend:\n    if isinstance(backend, Backend):\n        return backend\n    elif backend in _backends:\n        return _backends[backend].instance()\n    else:\n        raise ak._errors.wrap_error(ValueError(f\"No such backend {backend!r} exists.\"))\n", "path": "src/awkward/_backends.py"}]}