We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[tune][rllib] Windows: GPU not recognized
### What is the problem?

I'm getting `ray.tune.error.TuneError: Insufficient cluster resources to launch trial`.
I specified a GPU in my config but ray does not recognize my GPU (RTX 2080) and throws an error.
I can get passed this by setting `num_gpus: 0` in my config for now.

https://gist.github.com/juliusfrost/fa7ebbb8d1dfc66eea0bbc4babcbe5aa

### Reproduction (REQUIRED)

```
git clone https://github.com/juliusfrost/rllib-tune-atari.git
cd rllib-tune-atari
pip install -r requirements.txt
python train.py --algo a2c
```

- [x] I have verified my script runs in a clean environment and reproduces the issue.
- [x] I have verified the issue also occurs with the [latest wheels](https://docs.ray.io/en/latest/installation.html).


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `python/ray/resource_spec.py`
Content:
```
1 import math
2 from collections import namedtuple
3 import logging
4 import multiprocessing
5 import os
6 
7 import ray
8 import ray.ray_constants as ray_constants
9 
10 logger = logging.getLogger(__name__)
11 
12 # Prefix for the node id resource that is automatically added to each node.
13 # For example, a node may have id `node:172.23.42.1`.
14 NODE_ID_PREFIX = "node:"
15 
16 
17 class ResourceSpec(
18         namedtuple("ResourceSpec", [
19             "num_cpus", "num_gpus", "memory", "object_store_memory",
20             "resources", "redis_max_memory"
21         ])):
22     """Represents the resource configuration passed to a raylet.
23 
24     All fields can be None. Before starting services, resolve() should be
25     called to return a ResourceSpec with unknown values filled in with
26     defaults based on the local machine specifications.
27 
28     Attributes:
29         num_cpus: The CPUs allocated for this raylet.
30         num_gpus: The GPUs allocated for this raylet.
31         memory: The memory allocated for this raylet.
32         object_store_memory: The object store memory allocated for this raylet.
33             Note that when calling to_resource_dict(), this will be scaled down
34             by 30% to account for the global plasma LRU reserve.
35         resources: The custom resources allocated for this raylet.
36         redis_max_memory: The max amount of memory (in bytes) to allow each
37             redis shard to use. Once the limit is exceeded, redis will start
38             LRU eviction of entries. This only applies to the sharded redis
39             tables (task, object, and profile tables). By default, this is
40             capped at 10GB but can be set higher.
41     """
42 
43     def __new__(cls,
44                 num_cpus=None,
45                 num_gpus=None,
46                 memory=None,
47                 object_store_memory=None,
48                 resources=None,
49                 redis_max_memory=None):
50         return super(ResourceSpec, cls).__new__(cls, num_cpus, num_gpus,
51                                                 memory, object_store_memory,
52                                                 resources, redis_max_memory)
53 
54     def resolved(self):
55         """Returns if this ResourceSpec has default values filled out."""
56         for v in self._asdict().values():
57             if v is None:
58                 return False
59         return True
60 
61     def to_resource_dict(self):
62         """Returns a dict suitable to pass to raylet initialization.
63 
64         This renames num_cpus / num_gpus to "CPU" / "GPU", translates memory
65         from bytes into 100MB memory units, and checks types.
66         """
67         assert self.resolved()
68 
69         memory_units = ray_constants.to_memory_units(
70             self.memory, round_up=False)
71         reservable_object_store_memory = (
72             self.object_store_memory *
73             ray_constants.PLASMA_RESERVABLE_MEMORY_FRACTION)
74         if (reservable_object_store_memory <
75                 ray_constants.MEMORY_RESOURCE_UNIT_BYTES):
76             raise ValueError(
77                 "The minimum amount of object_store_memory that can be "
78                 "requested is {}, but you specified {}.".format(
79                     int(
80                         math.ceil(
81                             ray_constants.MEMORY_RESOURCE_UNIT_BYTES /
82                             ray_constants.PLASMA_RESERVABLE_MEMORY_FRACTION)),
83                     self.object_store_memory))
84         object_store_memory_units = ray_constants.to_memory_units(
85             self.object_store_memory *
86             ray_constants.PLASMA_RESERVABLE_MEMORY_FRACTION,
87             round_up=False)
88 
89         resources = dict(
90             self.resources,
91             CPU=self.num_cpus,
92             GPU=self.num_gpus,
93             memory=memory_units,
94             object_store_memory=object_store_memory_units)
95 
96         resources = {
97             resource_label: resource_quantity
98             for resource_label, resource_quantity in resources.items()
99             if resource_quantity != 0
100         }
101 
102         # Check types.
103         for resource_label, resource_quantity in resources.items():
104             assert (isinstance(resource_quantity, int)
105                     or isinstance(resource_quantity, float))
106             if (isinstance(resource_quantity, float)
107                     and not resource_quantity.is_integer()):
108                 raise ValueError(
109                     "Resource quantities must all be whole numbers. "
110                     "Violated by resource '{}' in {}.".format(
111                         resource_label, resources))
112             if resource_quantity < 0:
113                 raise ValueError("Resource quantities must be nonnegative. "
114                                  "Violated by resource '{}' in {}.".format(
115                                      resource_label, resources))
116             if resource_quantity > ray_constants.MAX_RESOURCE_QUANTITY:
117                 raise ValueError("Resource quantities must be at most {}. "
118                                  "Violated by resource '{}' in {}.".format(
119                                      ray_constants.MAX_RESOURCE_QUANTITY,
120                                      resource_label, resources))
121 
122         return resources
123 
124     def resolve(self, is_head, node_ip_address=None):
125         """Returns a copy with values filled out with system defaults.
126 
127         Args:
128             is_head (bool): Whether this is the head node.
129             node_ip_address (str): The IP address of the node that we are on.
130                 This is used to automatically create a node id resource.
131         """
132 
133         resources = (self.resources or {}).copy()
134         assert "CPU" not in resources, resources
135         assert "GPU" not in resources, resources
136         assert "memory" not in resources, resources
137         assert "object_store_memory" not in resources, resources
138 
139         if node_ip_address is None:
140             node_ip_address = ray.services.get_node_ip_address()
141 
142         # Automatically create a node id resource on each node. This is
143         # queryable with ray.state.node_ids() and ray.state.current_node_id().
144         resources[NODE_ID_PREFIX + node_ip_address] = 1.0
145 
146         num_cpus = self.num_cpus
147         if num_cpus is None:
148             num_cpus = multiprocessing.cpu_count()
149 
150         num_gpus = self.num_gpus
151         gpu_ids = ray.utils.get_cuda_visible_devices()
152         # Check that the number of GPUs that the raylet wants doesn't
153         # excede the amount allowed by CUDA_VISIBLE_DEVICES.
154         if (num_gpus is not None and gpu_ids is not None
155                 and num_gpus > len(gpu_ids)):
156             raise ValueError("Attempting to start raylet with {} GPUs, "
157                              "but CUDA_VISIBLE_DEVICES contains {}.".format(
158                                  num_gpus, gpu_ids))
159         if num_gpus is None:
160             # Try to automatically detect the number of GPUs.
161             num_gpus = _autodetect_num_gpus()
162             # Don't use more GPUs than allowed by CUDA_VISIBLE_DEVICES.
163             if gpu_ids is not None:
164                 num_gpus = min(num_gpus, len(gpu_ids))
165 
166         # Choose a default object store size.
167         system_memory = ray.utils.get_system_memory()
168         avail_memory = ray.utils.estimate_available_memory()
169         object_store_memory = self.object_store_memory
170         if object_store_memory is None:
171             object_store_memory = int(avail_memory * 0.3)
172             # Cap memory to avoid memory waste and perf issues on large nodes
173             if (object_store_memory >
174                     ray_constants.DEFAULT_OBJECT_STORE_MAX_MEMORY_BYTES):
175                 logger.debug(
176                     "Warning: Capping object memory store to {}GB. ".format(
177                         ray_constants.DEFAULT_OBJECT_STORE_MAX_MEMORY_BYTES //
178                         1e9) +
179                     "To increase this further, specify `object_store_memory` "
180                     "when calling ray.init() or ray start.")
181                 object_store_memory = (
182                     ray_constants.DEFAULT_OBJECT_STORE_MAX_MEMORY_BYTES)
183 
184         redis_max_memory = self.redis_max_memory
185         if redis_max_memory is None:
186             redis_max_memory = min(
187                 ray_constants.DEFAULT_REDIS_MAX_MEMORY_BYTES,
188                 max(
189                     int(avail_memory * 0.1),
190                     ray_constants.REDIS_MINIMUM_MEMORY_BYTES))
191         if redis_max_memory < ray_constants.REDIS_MINIMUM_MEMORY_BYTES:
192             raise ValueError(
193                 "Attempting to cap Redis memory usage at {} bytes, "
194                 "but the minimum allowed is {} bytes.".format(
195                     redis_max_memory,
196                     ray_constants.REDIS_MINIMUM_MEMORY_BYTES))
197 
198         memory = self.memory
199         if memory is None:
200             memory = (avail_memory - object_store_memory - (redis_max_memory
201                                                             if is_head else 0))
202             if memory < 100e6 and memory < 0.05 * system_memory:
203                 raise ValueError(
204                     "After taking into account object store and redis memory "
205                     "usage, the amount of memory on this node available for "
206                     "tasks and actors ({} GB) is less than {}% of total. "
207                     "You can adjust these settings with "
208                     "ray.init(memory=<bytes>, "
209                     "object_store_memory=<bytes>).".format(
210                         round(memory / 1e9, 2),
211                         int(100 * (memory / system_memory))))
212 
213         logger.info(
214             "Starting Ray with {} GiB memory available for workers and up to "
215             "{} GiB for objects. You can adjust these settings "
216             "with ray.init(memory=<bytes>, "
217             "object_store_memory=<bytes>).".format(
218                 round(
219                     ray_constants.round_to_memory_units(
220                         memory, round_up=False) / (1024**3), 2),
221                 round(object_store_memory / (1024**3), 2)))
222 
223         spec = ResourceSpec(num_cpus, num_gpus, memory, object_store_memory,
224                             resources, redis_max_memory)
225         assert spec.resolved()
226         return spec
227 
228 
229 def _autodetect_num_gpus():
230     """Attempt to detect the number of GPUs on this machine.
231 
232     TODO(rkn): This currently assumes Nvidia GPUs and Linux.
233 
234     Returns:
235         The number of GPUs if any were detected, otherwise 0.
236     """
237     proc_gpus_path = "/proc/driver/nvidia/gpus"
238     if os.path.isdir(proc_gpus_path):
239         return len(os.listdir(proc_gpus_path))
240     return 0
241
```
--- 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/python/ray/resource_spec.py b/python/ray/resource_spec.py
--- a/python/ray/resource_spec.py
+++ b/python/ray/resource_spec.py
@@ -3,6 +3,8 @@
 import logging
 import multiprocessing
 import os
+import subprocess
+import sys
 
 import ray
 import ray.ray_constants as ray_constants
@@ -229,12 +231,23 @@
 def _autodetect_num_gpus():
     """Attempt to detect the number of GPUs on this machine.
 
-    TODO(rkn): This currently assumes Nvidia GPUs and Linux.
+    TODO(rkn): This currently assumes NVIDIA GPUs on Linux.
+    TODO(mehrdadn): This currently does not work on macOS.
+    TODO(mehrdadn): Use a better mechanism for Windows.
+
+    Possibly useful: tensorflow.config.list_physical_devices()
 
     Returns:
         The number of GPUs if any were detected, otherwise 0.
     """
-    proc_gpus_path = "/proc/driver/nvidia/gpus"
-    if os.path.isdir(proc_gpus_path):
-        return len(os.listdir(proc_gpus_path))
-    return 0
+    result = 0
+    if sys.platform.startswith("linux"):
+        proc_gpus_path = "/proc/driver/nvidia/gpus"
+        if os.path.isdir(proc_gpus_path):
+            result = len(os.listdir(proc_gpus_path))
+    elif sys.platform == "win32":
+        props = "AdapterCompatibility"
+        cmdargs = ["WMIC", "PATH", "Win32_VideoController", "GET", props]
+        lines = subprocess.check_output(cmdargs).splitlines()[1:]
+        result = len([l.rstrip() for l in lines if l.startswith(b"NVIDIA")])
+    return result

{"golden_diff": "diff --git a/python/ray/resource_spec.py b/python/ray/resource_spec.py\n--- a/python/ray/resource_spec.py\n+++ b/python/ray/resource_spec.py\n@@ -3,6 +3,8 @@\n import logging\n import multiprocessing\n import os\n+import subprocess\n+import sys\n \n import ray\n import ray.ray_constants as ray_constants\n@@ -229,12 +231,23 @@\n def _autodetect_num_gpus():\n     \"\"\"Attempt to detect the number of GPUs on this machine.\n \n-    TODO(rkn): This currently assumes Nvidia GPUs and Linux.\n+    TODO(rkn): This currently assumes NVIDIA GPUs on Linux.\n+    TODO(mehrdadn): This currently does not work on macOS.\n+    TODO(mehrdadn): Use a better mechanism for Windows.\n+\n+    Possibly useful: tensorflow.config.list_physical_devices()\n \n     Returns:\n         The number of GPUs if any were detected, otherwise 0.\n     \"\"\"\n-    proc_gpus_path = \"/proc/driver/nvidia/gpus\"\n-    if os.path.isdir(proc_gpus_path):\n-        return len(os.listdir(proc_gpus_path))\n-    return 0\n+    result = 0\n+    if sys.platform.startswith(\"linux\"):\n+        proc_gpus_path = \"/proc/driver/nvidia/gpus\"\n+        if os.path.isdir(proc_gpus_path):\n+            result = len(os.listdir(proc_gpus_path))\n+    elif sys.platform == \"win32\":\n+        props = \"AdapterCompatibility\"\n+        cmdargs = [\"WMIC\", \"PATH\", \"Win32_VideoController\", \"GET\", props]\n+        lines = subprocess.check_output(cmdargs).splitlines()[1:]\n+        result = len([l.rstrip() for l in lines if l.startswith(b\"NVIDIA\")])\n+    return result\n", "issue": "[tune][rllib] Windows: GPU not recognized\n### What is the problem?\r\n\r\nI'm getting `ray.tune.error.TuneError: Insufficient cluster resources to launch trial`.\r\nI specified a GPU in my config but ray does not recognize my GPU (RTX 2080) and throws an error.\r\nI can get passed this by setting `num_gpus: 0` in my config for now.\r\n\r\nhttps://gist.github.com/juliusfrost/fa7ebbb8d1dfc66eea0bbc4babcbe5aa\r\n\r\n### Reproduction (REQUIRED)\r\n\r\n```\r\ngit clone https://github.com/juliusfrost/rllib-tune-atari.git\r\ncd rllib-tune-atari\r\npip install -r requirements.txt\r\npython train.py --algo a2c\r\n```\r\n\r\n- [x] I have verified my script runs in a clean environment and reproduces the issue.\r\n- [x] I have verified the issue also occurs with the [latest wheels](https://docs.ray.io/en/latest/installation.html).\r\n\n", "before_files": [{"content": "import math\nfrom collections import namedtuple\nimport logging\nimport multiprocessing\nimport os\n\nimport ray\nimport ray.ray_constants as ray_constants\n\nlogger = logging.getLogger(__name__)\n\n# Prefix for the node id resource that is automatically added to each node.\n# For example, a node may have id `node:172.23.42.1`.\nNODE_ID_PREFIX = \"node:\"\n\n\nclass ResourceSpec(\n        namedtuple(\"ResourceSpec\", [\n            \"num_cpus\", \"num_gpus\", \"memory\", \"object_store_memory\",\n            \"resources\", \"redis_max_memory\"\n        ])):\n    \"\"\"Represents the resource configuration passed to a raylet.\n\n    All fields can be None. Before starting services, resolve() should be\n    called to return a ResourceSpec with unknown values filled in with\n    defaults based on the local machine specifications.\n\n    Attributes:\n        num_cpus: The CPUs allocated for this raylet.\n        num_gpus: The GPUs allocated for this raylet.\n        memory: The memory allocated for this raylet.\n        object_store_memory: The object store memory allocated for this raylet.\n            Note that when calling to_resource_dict(), this will be scaled down\n            by 30% to account for the global plasma LRU reserve.\n        resources: The custom resources allocated for this raylet.\n        redis_max_memory: The max amount of memory (in bytes) to allow each\n            redis shard to use. Once the limit is exceeded, redis will start\n            LRU eviction of entries. This only applies to the sharded redis\n            tables (task, object, and profile tables). By default, this is\n            capped at 10GB but can be set higher.\n    \"\"\"\n\n    def __new__(cls,\n                num_cpus=None,\n                num_gpus=None,\n                memory=None,\n                object_store_memory=None,\n                resources=None,\n                redis_max_memory=None):\n        return super(ResourceSpec, cls).__new__(cls, num_cpus, num_gpus,\n                                                memory, object_store_memory,\n                                                resources, redis_max_memory)\n\n    def resolved(self):\n        \"\"\"Returns if this ResourceSpec has default values filled out.\"\"\"\n        for v in self._asdict().values():\n            if v is None:\n                return False\n        return True\n\n    def to_resource_dict(self):\n        \"\"\"Returns a dict suitable to pass to raylet initialization.\n\n        This renames num_cpus / num_gpus to \"CPU\" / \"GPU\", translates memory\n        from bytes into 100MB memory units, and checks types.\n        \"\"\"\n        assert self.resolved()\n\n        memory_units = ray_constants.to_memory_units(\n            self.memory, round_up=False)\n        reservable_object_store_memory = (\n            self.object_store_memory *\n            ray_constants.PLASMA_RESERVABLE_MEMORY_FRACTION)\n        if (reservable_object_store_memory <\n                ray_constants.MEMORY_RESOURCE_UNIT_BYTES):\n            raise ValueError(\n                \"The minimum amount of object_store_memory that can be \"\n                \"requested is {}, but you specified {}.\".format(\n                    int(\n                        math.ceil(\n                            ray_constants.MEMORY_RESOURCE_UNIT_BYTES /\n                            ray_constants.PLASMA_RESERVABLE_MEMORY_FRACTION)),\n                    self.object_store_memory))\n        object_store_memory_units = ray_constants.to_memory_units(\n            self.object_store_memory *\n            ray_constants.PLASMA_RESERVABLE_MEMORY_FRACTION,\n            round_up=False)\n\n        resources = dict(\n            self.resources,\n            CPU=self.num_cpus,\n            GPU=self.num_gpus,\n            memory=memory_units,\n            object_store_memory=object_store_memory_units)\n\n        resources = {\n            resource_label: resource_quantity\n            for resource_label, resource_quantity in resources.items()\n            if resource_quantity != 0\n        }\n\n        # Check types.\n        for resource_label, resource_quantity in resources.items():\n            assert (isinstance(resource_quantity, int)\n                    or isinstance(resource_quantity, float))\n            if (isinstance(resource_quantity, float)\n                    and not resource_quantity.is_integer()):\n                raise ValueError(\n                    \"Resource quantities must all be whole numbers. \"\n                    \"Violated by resource '{}' in {}.\".format(\n                        resource_label, resources))\n            if resource_quantity < 0:\n                raise ValueError(\"Resource quantities must be nonnegative. \"\n                                 \"Violated by resource '{}' in {}.\".format(\n                                     resource_label, resources))\n            if resource_quantity > ray_constants.MAX_RESOURCE_QUANTITY:\n                raise ValueError(\"Resource quantities must be at most {}. \"\n                                 \"Violated by resource '{}' in {}.\".format(\n                                     ray_constants.MAX_RESOURCE_QUANTITY,\n                                     resource_label, resources))\n\n        return resources\n\n    def resolve(self, is_head, node_ip_address=None):\n        \"\"\"Returns a copy with values filled out with system defaults.\n\n        Args:\n            is_head (bool): Whether this is the head node.\n            node_ip_address (str): The IP address of the node that we are on.\n                This is used to automatically create a node id resource.\n        \"\"\"\n\n        resources = (self.resources or {}).copy()\n        assert \"CPU\" not in resources, resources\n        assert \"GPU\" not in resources, resources\n        assert \"memory\" not in resources, resources\n        assert \"object_store_memory\" not in resources, resources\n\n        if node_ip_address is None:\n            node_ip_address = ray.services.get_node_ip_address()\n\n        # Automatically create a node id resource on each node. This is\n        # queryable with ray.state.node_ids() and ray.state.current_node_id().\n        resources[NODE_ID_PREFIX + node_ip_address] = 1.0\n\n        num_cpus = self.num_cpus\n        if num_cpus is None:\n            num_cpus = multiprocessing.cpu_count()\n\n        num_gpus = self.num_gpus\n        gpu_ids = ray.utils.get_cuda_visible_devices()\n        # Check that the number of GPUs that the raylet wants doesn't\n        # excede the amount allowed by CUDA_VISIBLE_DEVICES.\n        if (num_gpus is not None and gpu_ids is not None\n                and num_gpus > len(gpu_ids)):\n            raise ValueError(\"Attempting to start raylet with {} GPUs, \"\n                             \"but CUDA_VISIBLE_DEVICES contains {}.\".format(\n                                 num_gpus, gpu_ids))\n        if num_gpus is None:\n            # Try to automatically detect the number of GPUs.\n            num_gpus = _autodetect_num_gpus()\n            # Don't use more GPUs than allowed by CUDA_VISIBLE_DEVICES.\n            if gpu_ids is not None:\n                num_gpus = min(num_gpus, len(gpu_ids))\n\n        # Choose a default object store size.\n        system_memory = ray.utils.get_system_memory()\n        avail_memory = ray.utils.estimate_available_memory()\n        object_store_memory = self.object_store_memory\n        if object_store_memory is None:\n            object_store_memory = int(avail_memory * 0.3)\n            # Cap memory to avoid memory waste and perf issues on large nodes\n            if (object_store_memory >\n                    ray_constants.DEFAULT_OBJECT_STORE_MAX_MEMORY_BYTES):\n                logger.debug(\n                    \"Warning: Capping object memory store to {}GB. \".format(\n                        ray_constants.DEFAULT_OBJECT_STORE_MAX_MEMORY_BYTES //\n                        1e9) +\n                    \"To increase this further, specify `object_store_memory` \"\n                    \"when calling ray.init() or ray start.\")\n                object_store_memory = (\n                    ray_constants.DEFAULT_OBJECT_STORE_MAX_MEMORY_BYTES)\n\n        redis_max_memory = self.redis_max_memory\n        if redis_max_memory is None:\n            redis_max_memory = min(\n                ray_constants.DEFAULT_REDIS_MAX_MEMORY_BYTES,\n                max(\n                    int(avail_memory * 0.1),\n                    ray_constants.REDIS_MINIMUM_MEMORY_BYTES))\n        if redis_max_memory < ray_constants.REDIS_MINIMUM_MEMORY_BYTES:\n            raise ValueError(\n                \"Attempting to cap Redis memory usage at {} bytes, \"\n                \"but the minimum allowed is {} bytes.\".format(\n                    redis_max_memory,\n                    ray_constants.REDIS_MINIMUM_MEMORY_BYTES))\n\n        memory = self.memory\n        if memory is None:\n            memory = (avail_memory - object_store_memory - (redis_max_memory\n                                                            if is_head else 0))\n            if memory < 100e6 and memory < 0.05 * system_memory:\n                raise ValueError(\n                    \"After taking into account object store and redis memory \"\n                    \"usage, the amount of memory on this node available for \"\n                    \"tasks and actors ({} GB) is less than {}% of total. \"\n                    \"You can adjust these settings with \"\n                    \"ray.init(memory=<bytes>, \"\n                    \"object_store_memory=<bytes>).\".format(\n                        round(memory / 1e9, 2),\n                        int(100 * (memory / system_memory))))\n\n        logger.info(\n            \"Starting Ray with {} GiB memory available for workers and up to \"\n            \"{} GiB for objects. You can adjust these settings \"\n            \"with ray.init(memory=<bytes>, \"\n            \"object_store_memory=<bytes>).\".format(\n                round(\n                    ray_constants.round_to_memory_units(\n                        memory, round_up=False) / (1024**3), 2),\n                round(object_store_memory / (1024**3), 2)))\n\n        spec = ResourceSpec(num_cpus, num_gpus, memory, object_store_memory,\n                            resources, redis_max_memory)\n        assert spec.resolved()\n        return spec\n\n\ndef _autodetect_num_gpus():\n    \"\"\"Attempt to detect the number of GPUs on this machine.\n\n    TODO(rkn): This currently assumes Nvidia GPUs and Linux.\n\n    Returns:\n        The number of GPUs if any were detected, otherwise 0.\n    \"\"\"\n    proc_gpus_path = \"/proc/driver/nvidia/gpus\"\n    if os.path.isdir(proc_gpus_path):\n        return len(os.listdir(proc_gpus_path))\n    return 0\n", "path": "python/ray/resource_spec.py"}], "after_files": [{"content": "import math\nfrom collections import namedtuple\nimport logging\nimport multiprocessing\nimport os\nimport subprocess\nimport sys\n\nimport ray\nimport ray.ray_constants as ray_constants\n\nlogger = logging.getLogger(__name__)\n\n# Prefix for the node id resource that is automatically added to each node.\n# For example, a node may have id `node:172.23.42.1`.\nNODE_ID_PREFIX = \"node:\"\n\n\nclass ResourceSpec(\n        namedtuple(\"ResourceSpec\", [\n            \"num_cpus\", \"num_gpus\", \"memory\", \"object_store_memory\",\n            \"resources\", \"redis_max_memory\"\n        ])):\n    \"\"\"Represents the resource configuration passed to a raylet.\n\n    All fields can be None. Before starting services, resolve() should be\n    called to return a ResourceSpec with unknown values filled in with\n    defaults based on the local machine specifications.\n\n    Attributes:\n        num_cpus: The CPUs allocated for this raylet.\n        num_gpus: The GPUs allocated for this raylet.\n        memory: The memory allocated for this raylet.\n        object_store_memory: The object store memory allocated for this raylet.\n            Note that when calling to_resource_dict(), this will be scaled down\n            by 30% to account for the global plasma LRU reserve.\n        resources: The custom resources allocated for this raylet.\n        redis_max_memory: The max amount of memory (in bytes) to allow each\n            redis shard to use. Once the limit is exceeded, redis will start\n            LRU eviction of entries. This only applies to the sharded redis\n            tables (task, object, and profile tables). By default, this is\n            capped at 10GB but can be set higher.\n    \"\"\"\n\n    def __new__(cls,\n                num_cpus=None,\n                num_gpus=None,\n                memory=None,\n                object_store_memory=None,\n                resources=None,\n                redis_max_memory=None):\n        return super(ResourceSpec, cls).__new__(cls, num_cpus, num_gpus,\n                                                memory, object_store_memory,\n                                                resources, redis_max_memory)\n\n    def resolved(self):\n        \"\"\"Returns if this ResourceSpec has default values filled out.\"\"\"\n        for v in self._asdict().values():\n            if v is None:\n                return False\n        return True\n\n    def to_resource_dict(self):\n        \"\"\"Returns a dict suitable to pass to raylet initialization.\n\n        This renames num_cpus / num_gpus to \"CPU\" / \"GPU\", translates memory\n        from bytes into 100MB memory units, and checks types.\n        \"\"\"\n        assert self.resolved()\n\n        memory_units = ray_constants.to_memory_units(\n            self.memory, round_up=False)\n        reservable_object_store_memory = (\n            self.object_store_memory *\n            ray_constants.PLASMA_RESERVABLE_MEMORY_FRACTION)\n        if (reservable_object_store_memory <\n                ray_constants.MEMORY_RESOURCE_UNIT_BYTES):\n            raise ValueError(\n                \"The minimum amount of object_store_memory that can be \"\n                \"requested is {}, but you specified {}.\".format(\n                    int(\n                        math.ceil(\n                            ray_constants.MEMORY_RESOURCE_UNIT_BYTES /\n                            ray_constants.PLASMA_RESERVABLE_MEMORY_FRACTION)),\n                    self.object_store_memory))\n        object_store_memory_units = ray_constants.to_memory_units(\n            self.object_store_memory *\n            ray_constants.PLASMA_RESERVABLE_MEMORY_FRACTION,\n            round_up=False)\n\n        resources = dict(\n            self.resources,\n            CPU=self.num_cpus,\n            GPU=self.num_gpus,\n            memory=memory_units,\n            object_store_memory=object_store_memory_units)\n\n        resources = {\n            resource_label: resource_quantity\n            for resource_label, resource_quantity in resources.items()\n            if resource_quantity != 0\n        }\n\n        # Check types.\n        for resource_label, resource_quantity in resources.items():\n            assert (isinstance(resource_quantity, int)\n                    or isinstance(resource_quantity, float))\n            if (isinstance(resource_quantity, float)\n                    and not resource_quantity.is_integer()):\n                raise ValueError(\n                    \"Resource quantities must all be whole numbers. \"\n                    \"Violated by resource '{}' in {}.\".format(\n                        resource_label, resources))\n            if resource_quantity < 0:\n                raise ValueError(\"Resource quantities must be nonnegative. \"\n                                 \"Violated by resource '{}' in {}.\".format(\n                                     resource_label, resources))\n            if resource_quantity > ray_constants.MAX_RESOURCE_QUANTITY:\n                raise ValueError(\"Resource quantities must be at most {}. \"\n                                 \"Violated by resource '{}' in {}.\".format(\n                                     ray_constants.MAX_RESOURCE_QUANTITY,\n                                     resource_label, resources))\n\n        return resources\n\n    def resolve(self, is_head, node_ip_address=None):\n        \"\"\"Returns a copy with values filled out with system defaults.\n\n        Args:\n            is_head (bool): Whether this is the head node.\n            node_ip_address (str): The IP address of the node that we are on.\n                This is used to automatically create a node id resource.\n        \"\"\"\n\n        resources = (self.resources or {}).copy()\n        assert \"CPU\" not in resources, resources\n        assert \"GPU\" not in resources, resources\n        assert \"memory\" not in resources, resources\n        assert \"object_store_memory\" not in resources, resources\n\n        if node_ip_address is None:\n            node_ip_address = ray.services.get_node_ip_address()\n\n        # Automatically create a node id resource on each node. This is\n        # queryable with ray.state.node_ids() and ray.state.current_node_id().\n        resources[NODE_ID_PREFIX + node_ip_address] = 1.0\n\n        num_cpus = self.num_cpus\n        if num_cpus is None:\n            num_cpus = multiprocessing.cpu_count()\n\n        num_gpus = self.num_gpus\n        gpu_ids = ray.utils.get_cuda_visible_devices()\n        # Check that the number of GPUs that the raylet wants doesn't\n        # excede the amount allowed by CUDA_VISIBLE_DEVICES.\n        if (num_gpus is not None and gpu_ids is not None\n                and num_gpus > len(gpu_ids)):\n            raise ValueError(\"Attempting to start raylet with {} GPUs, \"\n                             \"but CUDA_VISIBLE_DEVICES contains {}.\".format(\n                                 num_gpus, gpu_ids))\n        if num_gpus is None:\n            # Try to automatically detect the number of GPUs.\n            num_gpus = _autodetect_num_gpus()\n            # Don't use more GPUs than allowed by CUDA_VISIBLE_DEVICES.\n            if gpu_ids is not None:\n                num_gpus = min(num_gpus, len(gpu_ids))\n\n        # Choose a default object store size.\n        system_memory = ray.utils.get_system_memory()\n        avail_memory = ray.utils.estimate_available_memory()\n        object_store_memory = self.object_store_memory\n        if object_store_memory is None:\n            object_store_memory = int(avail_memory * 0.3)\n            # Cap memory to avoid memory waste and perf issues on large nodes\n            if (object_store_memory >\n                    ray_constants.DEFAULT_OBJECT_STORE_MAX_MEMORY_BYTES):\n                logger.debug(\n                    \"Warning: Capping object memory store to {}GB. \".format(\n                        ray_constants.DEFAULT_OBJECT_STORE_MAX_MEMORY_BYTES //\n                        1e9) +\n                    \"To increase this further, specify `object_store_memory` \"\n                    \"when calling ray.init() or ray start.\")\n                object_store_memory = (\n                    ray_constants.DEFAULT_OBJECT_STORE_MAX_MEMORY_BYTES)\n\n        redis_max_memory = self.redis_max_memory\n        if redis_max_memory is None:\n            redis_max_memory = min(\n                ray_constants.DEFAULT_REDIS_MAX_MEMORY_BYTES,\n                max(\n                    int(avail_memory * 0.1),\n                    ray_constants.REDIS_MINIMUM_MEMORY_BYTES))\n        if redis_max_memory < ray_constants.REDIS_MINIMUM_MEMORY_BYTES:\n            raise ValueError(\n                \"Attempting to cap Redis memory usage at {} bytes, \"\n                \"but the minimum allowed is {} bytes.\".format(\n                    redis_max_memory,\n                    ray_constants.REDIS_MINIMUM_MEMORY_BYTES))\n\n        memory = self.memory\n        if memory is None:\n            memory = (avail_memory - object_store_memory - (redis_max_memory\n                                                            if is_head else 0))\n            if memory < 100e6 and memory < 0.05 * system_memory:\n                raise ValueError(\n                    \"After taking into account object store and redis memory \"\n                    \"usage, the amount of memory on this node available for \"\n                    \"tasks and actors ({} GB) is less than {}% of total. \"\n                    \"You can adjust these settings with \"\n                    \"ray.init(memory=<bytes>, \"\n                    \"object_store_memory=<bytes>).\".format(\n                        round(memory / 1e9, 2),\n                        int(100 * (memory / system_memory))))\n\n        logger.info(\n            \"Starting Ray with {} GiB memory available for workers and up to \"\n            \"{} GiB for objects. You can adjust these settings \"\n            \"with ray.init(memory=<bytes>, \"\n            \"object_store_memory=<bytes>).\".format(\n                round(\n                    ray_constants.round_to_memory_units(\n                        memory, round_up=False) / (1024**3), 2),\n                round(object_store_memory / (1024**3), 2)))\n\n        spec = ResourceSpec(num_cpus, num_gpus, memory, object_store_memory,\n                            resources, redis_max_memory)\n        assert spec.resolved()\n        return spec\n\n\ndef _autodetect_num_gpus():\n    \"\"\"Attempt to detect the number of GPUs on this machine.\n\n    TODO(rkn): This currently assumes NVIDIA GPUs on Linux.\n    TODO(mehrdadn): This currently does not work on macOS.\n    TODO(mehrdadn): Use a better mechanism for Windows.\n\n    Possibly useful: tensorflow.config.list_physical_devices()\n\n    Returns:\n        The number of GPUs if any were detected, otherwise 0.\n    \"\"\"\n    result = 0\n    if sys.platform.startswith(\"linux\"):\n        proc_gpus_path = \"/proc/driver/nvidia/gpus\"\n        if os.path.isdir(proc_gpus_path):\n            result = len(os.listdir(proc_gpus_path))\n    elif sys.platform == \"win32\":\n        props = \"AdapterCompatibility\"\n        cmdargs = [\"WMIC\", \"PATH\", \"Win32_VideoController\", \"GET\", props]\n        lines = subprocess.check_output(cmdargs).splitlines()[1:]\n        result = len([l.rstrip() for l in lines if l.startswith(b\"NVIDIA\")])\n    return result\n", "path": "python/ray/resource_spec.py"}]}