chore: import upstream snapshot with attribution
cffconvert / validate (push) Has been skipped
License Check / license-check (push) Failing after 2s

This commit is contained in:
wehub-resource-sync
2026-07-13 12:14:16 +08:00
commit 8a852e4b4e
36502 changed files with 9277225 additions and 0 deletions
@@ -0,0 +1,44 @@
load("@xla//third_party/rules_python/python:py_library.bzl", "py_library")
load("//tensorflow:tensorflow.bzl", "py_test")
# Description:
# Tensorflow builder compatibility checker.
package(
# copybara:uncomment default_applicable_licenses = ["//tensorflow:license"],
default_visibility = [
"//tensorflow/tools/tensorflow_builder:__subpackages__",
],
)
licenses(["notice"])
py_library(
name = "compat_checker",
srcs = ["compat_checker.py"],
strict_deps = True,
deps = [
"//tensorflow/python/platform:tf_logging",
"//tensorflow/python/util:tf_inspect",
],
)
py_test(
name = "compat_checker_test",
srcs = ["compat_checker_test.py"],
data = [
":test_config",
],
strict_deps = True,
tags = ["no_pip"],
deps = [
":compat_checker",
# copybara:uncomment "//third_party/py/google/protobuf:use_fast_cpp_protos",
"//tensorflow/python/platform:test",
],
)
filegroup(
name = "test_config",
srcs = ["test_config.ini"],
)
@@ -0,0 +1,902 @@
# Copyright 2019 The TensorFlow Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# ==============================================================================
"""Checks if a set of configuration(s) is version and dependency compatible."""
import configparser
import re
import sys
from tensorflow.python.platform import tf_logging as logging
from tensorflow.python.util import tf_inspect
PATH_TO_DIR = "tensorflow/tools/tensorflow_builder/compat_checker"
def _compare_versions(v1, v2):
"""Compare two versions and return information on which is smaller vs. larger.
Args:
v1: String that is a version to be compared against `v2`.
v2: String that is a version to be compared against `v1`.
Returns:
Dict that stores larger version with key `larger` and smaller version with
key `smaller`.
e.g. {`larger`: `1.5.0`, `smaller`: `1.2.0`}
Raises:
RuntimeError: If asked to compare `inf` to `inf`.
"""
# Throw error is asked to compare `inf` to `inf`.
if v1 == "inf" and v2 == "inf":
raise RuntimeError("Cannot compare `inf` to `inf`.")
rtn_dict = {"smaller": None, "larger": None}
v1_list = v1.split(".")
v2_list = v2.split(".")
# Take care of cases with infinity (arg=`inf`).
if v1_list[0] == "inf":
v1_list[0] = str(int(v2_list[0]) + 1)
if v2_list[0] == "inf":
v2_list[0] = str(int(v1_list[0]) + 1)
# Determine which of the two lists are longer vs. shorter.
v_long = v1_list if len(v1_list) >= len(v2_list) else v2_list
v_short = v1_list if len(v1_list) < len(v2_list) else v2_list
larger, smaller = None, None
for i, ver in enumerate(v_short, start=0):
if int(ver) > int(v_long[i]):
larger = _list_to_string(v_short, ".")
smaller = _list_to_string(v_long, ".")
elif int(ver) < int(v_long[i]):
larger = _list_to_string(v_long, ".")
smaller = _list_to_string(v_short, ".")
else:
if i == len(v_short) - 1:
if v_long[i + 1:] == ["0"]*(len(v_long) - 1 - i):
larger = "equal"
smaller = "equal"
else:
larger = _list_to_string(v_long, ".")
smaller = _list_to_string(v_short, ".")
else:
# Go to next round.
pass
if larger:
break
rtn_dict["smaller"] = smaller
rtn_dict["larger"] = larger
return rtn_dict
def _list_to_string(l, s):
"""Concatenates list items into a single string separated by `s`.
Args:
l: List with items to be concatenated into a single string.
s: String or char that will be concatenated in between each item.
Returns:
String that has all items in list `l` concatenated with `s` separator.
"""
return s.join(l)
def _get_func_name():
"""Get the name of current function.
Returns:
String that is the name of current function.
"""
return tf_inspect.stack()[1][3]
class ConfigCompatChecker:
"""Class that checks configuration versions and dependency compatibilities.
`ConfigCompatChecker` checks a given set of configurations and their versions
against supported versions and dependency rules defined in `.ini` config file.
For project `TensorFlow Builder`, it functions as a sub-module for the builder
service that validates requested build configurations from a client prior to
initiating a TensorFlow build.
"""
class _Reqs(object):
"""Class that stores specifications related to a single requirement.
`_Reqs` represents a single version or dependency requirement specified in
the `.ini` config file. It is meant ot be used inside `ConfigCompatChecker`
to help organize and identify version and dependency compatibility for a
given configuration (e.g. gcc version) required by the client.
"""
def __init__(self, req, config, section):
"""Initializes a version or dependency requirement object.
Args:
req: List that contains individual supported versions or a single string
that contains `range` definition.
e.g. [`range(1.0, 2.0) include(3.0) exclude(1.5)`]
e.g. [`1.0`, `3.0`, `7.1`]
config: String that is the configuration name.
e.g. `platform`
section: String that is the section name from the `.ini` config file
under which the requirement is defined.
e.g. `Required`, `Optional`, `Unsupported`, `Dependency`
"""
# Req class variables.
self.req = req
self.exclude = None
self.include = None
self.range = [None, None] # for [min, max]
self.config = config
self._req_type = "" # e.g. `range` or `no_range`
self._section = section
self._initialized = None
self._error_message = []
# Parse and store requirement specifications.
self.parse_single_req()
@property
def get_status(self):
"""Get status of `_Reqs` initialization.
Returns:
Tuple
(Boolean indicating initialization status,
List of error messages, if any)
"""
return self._initialized, self._error_message
def __str__(self):
"""Prints a requirement and its components.
Returns:
String that has concatenated information about a requirement.
"""
info = {
"section": self._section,
"config": self.config,
"req_type": self._req_type,
"req": str(self.req),
"range": str(self.range),
"exclude": str(self.exclude),
"include": str(self.include),
"init": str(self._initialized)
}
req_str = "\n >>> _Reqs Instance <<<\n"
req_str += "Section: {section}\n"
req_str += "Configuration name: {config}\n"
req_str += "Requirement type: {req_type}\n"
req_str += "Requirement: {req}\n"
req_str += "Range: {range}\n"
req_str += "Exclude: {exclude}\n"
req_str += "Include: {include}\n"
req_str += "Initialized: {init}\n\n"
return req_str.format(**info)
def parse_single_req(self):
"""Parses a requirement and stores information.
`self.req` _initialized in `__init__` is called for retrieving the
requirement.
A requirement can come in two forms:
[1] String that includes `range` indicating range syntax for defining
a requirement.
e.g. `range(1.0, 2.0) include(3.0) exclude(1.5)`
[2] List that includes individual supported versions or items.
e.g. [`1.0`, `3.0`, `7.1`]
For a list type requirement, it directly stores the list to
`self.include`.
Call `get_status` for checking the status of the parsing. This function
sets `self._initialized` to `False` and immediately returns with an error
message upon encountering a failure. It sets `self._initialized` to `True`
and returns without an error message upon success.
"""
# Regex expression for filtering requirement line. Please refer
# to docstring above for more information.
expr = r"(range\()?([\d\.\,\s]+)(\))?( )?(include\()?"
expr += r"([\d\.\,\s]+)?(\))?( )?(exclude\()?([\d\.\,\s]+)?(\))?"
# Check that arg `req` is not empty.
if not self.req:
err_msg = "[Error] Requirement is missing. "
err_msg += "(section = %s, " % str(self._section)
err_msg += "config = %s, req = %s)" % (str(self.config), str(self.req))
logging.error(err_msg)
self._initialized = False
self._error_message.append(err_msg)
return
# For requirement given in format with `range`. For example:
# python = [range(3.3, 3.7) include(2.7)] as opposed to
# python = [2.7, 3.3, 3.4, 3.5, 3.6, 3.7]
if "range" in self.req[0]:
self._req_type = "range"
match = re.match(expr, self.req[0])
if not match:
err_msg = "[Error] Encountered issue when parsing the requirement."
err_msg += " (req = %s, match = %s)" % (str(self.req), str(match))
logging.error(err_msg)
self._initialized = False
self._error_message.append(err_msg)
return
else:
match_grp = match.groups()
match_size = len(match_grp)
for i, m in enumerate(match_grp[0:match_size-1], start=0):
# Get next index. For example:
# | idx | next_idx |
# +------------+------------+
# | `range(` | `1.1, 1.5` |
# | `exclude(` | `1.1, 1.5` |
# | `include(` | `1.1, 1.5` |
next_match = match_grp[i + 1]
if m not in ["", None, " ", ")"]:
if "range" in m:
# Check that the range definition contains only one comma.
# If more than one comma, then there is format error with the
# requirement config file.
comma_count = next_match.count(",")
if comma_count > 1 or comma_count == 0:
err_msg = "[Error] Found zero or more than one comma in range"
err_msg += " definition. (req = %s, " % str(self.req)
err_msg += "match = %s)" % str(next_match)
logging.error(err_msg)
self._initialized = False
self._error_message.append(err_msg)
return
# Remove empty space in range and separate min, max by
# comma. (e.g. `1.0, 2.0` => `1.0,2.0` => [`1.0`, `2.0`])
min_max = next_match.replace(" ", "").split(",")
# Explicitly define min and max values.
# If min_max = ['', ''], then `range(, )` was provided as
# req, which is equivalent to `include all versions`.
if not min_max[0]:
min_max[0] = "0"
if not min_max[1]:
min_max[1] = "inf"
self.range = min_max
if "exclude" in m:
self.exclude = next_match.replace(" ", "").split(",")
if "include" in m:
self.include = next_match.replace(" ", "").split(",")
self._initialized = True
# For requirement given in format without a `range`. For example:
# python = [2.7, 3.3, 3.4, 3.5, 3.6, 3.7] as opposed to
# python = [range(3.3, 3.7) include(2.7)]
else:
self._req_type = "no_range"
# Requirement (self.req) should be a list.
if not isinstance(self.req, list):
err_msg = "[Error] Requirement is not a list."
err_msg += "(req = %s, " % str(self.req)
err_msg += "type(req) = %s)" % str(type(self.req))
logging.error(err_msg)
self._initialized = False
self._error_message.append(err_msg)
else:
self.include = self.req
self._initialized = True
return
def __init__(self, usr_config, req_file):
"""Initializes a configuration compatibility checker.
Args:
usr_config: Dict of all configuration(s) whose version compatibilities are
to be checked against the rules defined in the `.ini` config
file.
req_file: String that is the full name of the `.ini` config file.
e.g. `config.ini`
"""
# ConfigCompatChecker class variables.
self.usr_config = usr_config
self.req_file = req_file
self.warning_msg = []
self.error_msg = []
# Get and store requirements.
reqs_all = self.get_all_reqs()
self.required = reqs_all["required"]
self.optional = reqs_all["optional"]
self.unsupported = reqs_all["unsupported"]
self.dependency = reqs_all["dependency"]
self.successes = []
self.failures = []
def get_all_reqs(self):
"""Parses all compatibility specifications listed in the `.ini` config file.
Reads and parses each and all compatibility specifications from the `.ini`
config file by sections. It then populates appropriate dicts that represent
each section (e.g. `self.required`) and returns a tuple of the populated
dicts.
Returns:
Dict of dict
{ `required`: Dict of `Required` configs and supported versions,
`optional`: Dict of `Optional` configs and supported versions,
`unsupported`: Dict of `Unsupported` configs and supported versions,
`dependency`: Dict of `Dependency` configs and supported versions }
"""
# First check if file exists. Exit on failure.
try:
open(self.req_file, "rb")
except IOError:
msg = "[Error] Cannot read file '%s'." % self.req_file
logging.error(msg)
sys.exit(1)
# Store status of parsing requirements. For local usage only.
curr_status = True
# Initialize config parser for parsing version requirements file.
parser = configparser.ConfigParser()
parser.read(self.req_file)
if not parser.sections():
err_msg = "[Error] Empty config file. "
err_msg += "(file = %s, " % str(self.req_file)
err_msg += "parser sectons = %s)" % str(parser.sections())
self.error_msg.append(err_msg)
logging.error(err_msg)
curr_status = False
# Each dependency dict will have the following format.
# _dict = {
# `<config_name>` : [_Reqs()],
# `<config_name>` : [_Reqs()]
# }
required_dict = {}
optional_dict = {}
unsupported_dict = {}
dependency_dict = {}
# Parse every config under each section defined in config file
# and populate requirement dict(s).
for section in parser.sections():
all_configs = parser.options(section)
for config in all_configs:
spec = parser.get(section, config)
# Separately manage each section:
# `Required`,
# `Optional`,
# `Unsupported`,
# `Dependency`
# One of the sections is required.
if section == "Dependency":
dependency_dict[config] = []
spec_split = spec.split(",\n")
# First dependency item may only or not have `[` depending
# on the indentation style in the config (.ini) file.
# If it has `[`, then either skip or remove from string.
if spec_split[0] == "[":
spec_split = spec_split[1:]
elif "[" in spec_split[0]:
spec_split[0] = spec_split[0].replace("[", "")
else:
warn_msg = "[Warning] Config file format error: Missing `[`."
warn_msg += "(section = %s, " % str(section)
warn_msg += "config = %s)" % str(config)
logging.warning(warn_msg)
self.warning_msg.append(warn_msg)
# Last dependency item may only or not have `]` depending
# on the indentation style in the config (.ini) file.
# If it has `[`, then either skip or remove from string.
if spec_split[-1] == "]":
spec_split = spec_split[:-1]
elif "]" in spec_split[-1]:
spec_split[-1] = spec_split[-1].replace("]", "")
else:
warn_msg = "[Warning] Config file format error: Missing `]`."
warn_msg += "(section = %s, " % str(section)
warn_msg += "config = %s)" % str(config)
logging.warning(warn_msg)
self.warning_msg.append(warn_msg)
# Parse `spec_split` which is a list of all dependency rules
# retrieved from the config file.
# Create a _Reqs() instance for each rule and store it under
# appropriate class dict (e.g. dependency_dict) with a proper
# key.
#
# For dependency definition, it creates one _Reqs() instance each
# for requirement and dependency. For example, it would create
# a list in the following indexing sequence:
#
# [`config', <`config` _Reqs()>, `dep', <`dep` _Reqs()>]
#
# For example:
# [`python`, _Reqs(), `tensorflow`, _Reqs()] for
# `python 3.7 requires tensorflow 1.13`
for rule in spec_split:
# Filter out only the necessary information from `rule` string.
spec_dict = self.filter_dependency(rule)
# Create _Reqs() instance for each rule.
cfg_name = spec_dict["cfg"] # config name
dep_name = spec_dict["cfgd"] # dependency name
cfg_req = self._Reqs(
self.convert_to_list(spec_dict["cfg_spec"], " "),
config=cfg_name,
section=section
)
dep_req = self._Reqs(
self.convert_to_list(spec_dict["cfgd_spec"], " "),
config=dep_name,
section=section
)
# Check status of _Reqs() initialization. If wrong formats are
# detected from the config file, it would return `False` for
# initialization status.
# `<_Reqs>.get_status` returns [_initialized, _error_message]
cfg_req_status = cfg_req.get_status
dep_req_status = dep_req.get_status
if not cfg_req_status[0] or not dep_req_status[0]:
# `<_Reqs>.get_status()[1]` returns empty upon successful init.
msg = "[Error] Failed to create _Reqs() instance for a "
msg += "dependency item. (config = %s, " % str(cfg_name)
msg += "dep = %s)" % str(dep_name)
logging.error(msg)
self.error_msg.append(cfg_req_status[1])
self.error_msg.append(dep_req_status[1])
curr_status = False
break
else:
dependency_dict[config].append(
[cfg_name, cfg_req, dep_name, dep_req])
# Break out of `if section == 'Dependency'` block.
if not curr_status:
break
else:
if section == "Required":
add_to = required_dict
elif section == "Optional":
add_to = optional_dict
elif section == "Unsupported":
add_to = unsupported_dict
else:
msg = "[Error] Section name `%s` is not accepted." % str(section)
msg += "Accepted section names are `Required`, `Optional`, "
msg += "`Unsupported`, and `Dependency`."
logging.error(msg)
self.error_msg.append(msg)
curr_status = False
break
# Need to make sure `req` argument for _Reqs() instance is always
# a list. If not, convert to list.
req_list = self.convert_to_list(self.filter_line(spec), " ")
add_to[config] = self._Reqs(req_list, config=config, section=section)
# Break out of `for config in all_configs` loop.
if not curr_status:
break
# Break out of `for section in parser.sections()` loop.
if not curr_status:
break
return_dict = {
"required": required_dict,
"optional": optional_dict,
"unsupported": unsupported_dict,
"dependency": dependency_dict
}
return return_dict
def filter_dependency(self, line):
"""Filters dependency compatibility rules defined in the `.ini` config file.
Dependency specifications are defined as the following:
`<config> <config_version> requires <dependency> <dependency_version>`
e.g.
`python 3.7 requires tensorflow 1.13`
`tensorflow range(1.0.0, 1.13.1) requires gcc range(4.8, )`
Args:
line: String that is a dependency specification defined under `Dependency`
section in the `.ini` config file.
Returns:
Dict with configuration and its dependency information.
e.g. {`cfg`: `python`, # configuration name
`cfg_spec`: `3.7`, # configuration version
`cfgd`: `tensorflow`, # dependency name
`cfgd_spec`: `4.8`} # dependency version
"""
line = line.strip("\n")
expr = r"(?P<cfg>[\S]+) (?P<cfg_spec>range\([\d\.\,\s]+\)( )?"
expr += r"(include\([\d\.\,\s]+\))?( )?(exclude\([\d\.\,\s]+\))?( )?"
expr += r"|[\d\,\.\s]+) requires (?P<cfgd>[\S]+) (?P<cfgd_spec>range"
expr += r"\([\d\.\,\s]+\)( )?(include\([\d\.\,\s]+\))?( )?"
expr += r"(exclude\([\d\.\,\s]+\))?( )?|[\d\,\.\s]+)"
r = re.match(expr, line.strip("\n"))
return r.groupdict()
def convert_to_list(self, item, separator):
"""Converts a string into a list with a separator.
Args:
item: String that needs to be separated into a list by a given separator.
List item is also accepted but will take no effect.
separator: String with which the `item` will be splited.
Returns:
List that is a splited version of a given input string.
e.g. Input: `1.0, 2.0, 3.0` with `, ` separator
Output: [1.0, 2.0, 3.0]
"""
out = None
if not isinstance(item, list):
if "range" in item:
# If arg `item` is a single string, then create a list with just
# the item.
out = [item]
else:
# arg `item` can come in as the following:
# `1.0, 1.1, 1.2, 1.4`
# if requirements were defined without the `range()` format.
# In such a case, create a list separated by `separator` which is
# an empty string (' ') in this case.
out = item.split(separator)
for i in range(len(out)):
out[i] = out[i].replace(",", "")
# arg `item` is a list already.
else:
out = [item]
return out
def filter_line(self, line):
"""Removes `[` or `]` from the input line.
Args:
line: String that is a compatibility specification line from the `.ini`
config file.
Returns:
String that is a compatibility specification line without `[` and `]`.
"""
filtered = []
warn_msg = []
splited = line.split("\n")
# If arg `line` is empty, then requirement might be missing. Add
# to warning as this issue will be caught in _Reqs() initialization.
if not line and len(splited) < 1:
warn_msg = "[Warning] Empty line detected while filtering lines."
logging.warning(warn_msg)
self.warning_msg.append(warn_msg)
# In general, first line in requirement definition will include `[`
# in the config file (.ini). Remove it.
if splited[0] == "[":
filtered = splited[1:]
elif "[" in splited[0]:
splited = splited[0].replace("[", "")
filtered = splited
# If `[` is missing, then it could be a formatting issue with
# config file (.ini.). Add to warning.
else:
warn_msg = "[Warning] Format error. `[` could be missing in "
warn_msg += "the config (.ini) file. (line = %s)" % str(line)
logging.warning(warn_msg)
self.warning_msg.append(warn_msg)
# In general, last line in requirement definition will include `]`
# in the config file (.ini). Remove it.
if filtered[-1] == "]":
filtered = filtered[:-1]
elif "]" in filtered[-1]:
filtered[-1] = filtered[-1].replace("]", "")
# If `]` is missing, then it could be a formatting issue with
# config file (.ini.). Add to warning.
else:
warn_msg = "[Warning] Format error. `]` could be missing in "
warn_msg += "the config (.ini) file. (line = %s)" % str(line)
logging.warning(warn_msg)
self.warning_msg.append(warn_msg)
return filtered
def in_range(self, ver, req):
"""Checks if a version satisfies a version and/or compatibility requirement.
Args:
ver: List whose first item is a config version that needs to be checked
for support status and version compatibility.
e.g. ver = [`1.0`]
req: `_Reqs` class instance that represents a configuration version and
compatibility specifications.
Returns:
Boolean output of checking if version `ver` meets the requirement
stored in `req` (or a `_Reqs` requirements class instance).
"""
# If `req.exclude` is not empty and `ver` is in `req.exclude`,
# no need to proceed to next set of checks as it is explicitly
# NOT supported.
if req.exclude is not None:
for v in ver:
if v in req.exclude:
return False
# If `req.include` is not empty and `ver` is in `req.include`,
# no need to proceed to next set of checks as it is supported and
# NOT unsupported (`req.exclude`).
include_checked = False
if req.include is not None:
for v in ver:
if v in req.include:
return True
include_checked = True
# If `req.range` is not empty, then `ver` is defined with a `range`
# syntax. Check whether `ver` falls under the defined supported
# range.
if req.range != [None, None]:
min_v = req.range[0] # minimum supported version
max_v = req.range[1] # maximum supported version
ver = ver[0] # version to compare
lg = _compare_versions(min_v, ver)["larger"] # `ver` should be larger
sm = _compare_versions(ver, max_v)["smaller"] # `ver` should be smaller
if lg in [ver, "equal"] and sm in [ver, "equal", "inf"]:
return True
else:
err_msg = "[Error] Version is outside of supported range. "
err_msg += "(config = %s, " % str(req.config)
err_msg += "version = %s, " % str(ver)
err_msg += "supported range = %s)" % str(req.range)
logging.warning(err_msg)
self.warning_msg.append(err_msg)
return False
else:
err_msg = ""
if include_checked:
# user config is not supported as per exclude, include, range
# specification.
err_msg = "[Error] Version is outside of supported range. "
else:
# user config is not defined in exclude, include or range. config file
# error.
err_msg = "[Error] Missing specification. "
err_msg += "(config = %s, " % str(req.config)
err_msg += "version = %s, " % str(ver)
err_msg += "supported range = %s)" % str(req.range)
logging.warning(err_msg)
self.warning_msg.append(err_msg)
return False
def _print(self, *args):
"""Prints compatibility check status and failure or warning messages.
Prints to console without using `logging`.
Args:
*args: String(s) that is one of:
[`failures`, # all failures
`successes`, # all successes
`failure_msgs`, # failure message(s) recorded upon failure(s)
`warning_msgs`] # warning message(s) recorded upon warning(s)
Raises:
Exception: If *args not in:
[`failures`, `successes`, `failure_msgs`, `warning_msg`]
"""
def _format(name, arr):
"""Prints compatibility check results with a format.
Args:
name: String that is the title representing list `arr`.
arr: List of items to be printed in a certain format.
"""
title = "### All Compatibility %s ###" % str(name)
tlen = len(title)
print("-"*tlen)
print(title)
print("-"*tlen)
print(" Total # of %s: %s\n" % (str(name), str(len(arr))))
if arr:
for item in arr:
detail = ""
if isinstance(item[1], list):
for itm in item[1]:
detail += str(itm) + ", "
detail = detail[:-2]
else:
detail = str(item[1])
print(" %s ('%s')\n" % (str(item[0]), detail))
else:
print(" No %s" % name)
print("\n")
for p_item in args:
if p_item == "failures":
_format("Failures", self.failures)
elif p_item == "successes":
_format("Successes", self.successes)
elif p_item == "failure_msgs":
_format("Failure Messages", self.error_msg)
elif p_item == "warning_msgs":
_format("Warning Messages", self.warning_msg)
else:
raise ValueError(
"[Error] Wrong input provided for %s." % _get_func_name()
)
def check_compatibility(self):
"""Checks version and dependency compatibility for a given configuration.
`check_compatibility` immediately returns with `False` (or failure status)
if any child process or checks fail. For error and warning messages, either
print `self.(error_msg|warning_msg)` or call `_print` function.
Returns:
Boolean that is a status of the compatibility check result.
"""
# Check if all `Required` configs are found in user configs.
usr_keys = list(self.usr_config.keys())
for k in self.usr_config.keys():
if k not in usr_keys:
err_msg = "[Error] Required config not found in user config."
err_msg += "(required = %s, " % str(k)
err_msg += "user configs = %s)" % str(usr_keys)
logging.error(err_msg)
self.error_msg.append(err_msg)
self.failures.append([k, err_msg])
return False
# Parse each user config and validate its compatibility.
overall_status = True
for config_name, spec in self.usr_config.items():
temp_status = True
# Check under which section the user config is defined.
in_required = config_name in list(self.required.keys())
in_optional = config_name in list(self.optional.keys())
in_unsupported = config_name in list(self.unsupported.keys())
in_dependency = config_name in list(self.dependency.keys())
# Add to warning if user config is not specified in the config file.
if not (in_required or in_optional or in_unsupported or in_dependency):
warn_msg = "[Error] User config not defined in config file."
warn_msg += "(user config = %s)" % str(config_name)
logging.warning(warn_msg)
self.warning_msg.append(warn_msg)
self.failures.append([config_name, warn_msg])
temp_status = False
else:
if in_unsupported:
if self.in_range(spec, self.unsupported[config_name]):
err_msg = "[Error] User config is unsupported. It is "
err_msg += "defined under 'Unsupported' section in the config file."
err_msg += " (config = %s, spec = %s)" % (config_name, str(spec))
logging.error(err_msg)
self.error_msg.append(err_msg)
self.failures.append([config_name, err_msg])
temp_status = False
if in_required:
if not self.in_range(spec, self.required[config_name]):
err_msg = "[Error] User config cannot be supported. It is not in "
err_msg += "the supported range as defined in the 'Required' "
err_msg += "section. (config = %s, " % config_name
err_msg += "spec = %s)" % str(spec)
logging.error(err_msg)
self.error_msg.append(err_msg)
self.failures.append([config_name, err_msg])
temp_status = False
if in_optional:
if not self.in_range(spec, self.optional[config_name]):
err_msg = "[Error] User config cannot be supported. It is not in "
err_msg += "the supported range as defined in the 'Optional' "
err_msg += "section. (config = %s, " % config_name
err_msg += "spec = %s)" % str(spec)
logging.error(err_msg)
self.error_msg.append(err_msg)
self.failures.append([config_name, err_msg])
temp_status = False
# If user config and version has a dependency, check both user
# config + version and dependency config + version are supported.
if in_dependency:
# Get dependency information. The information gets retrieved in the
# following format:
# [`config`, `config _Reqs()`, `dependency`, `dependency _Reqs()`]
dep_list = self.dependency[config_name]
if dep_list:
for rule in dep_list:
cfg = rule[0] # config name
cfg_req = rule[1] # _Reqs() instance for config requirement
dep = rule[2] # dependency name
dep_req = rule[3] # _Reqs() instance for dependency requirement
# Check if user config has a dependency in the following sequence:
# [1] Check user config and the config that has dependency
# are the same. (This is defined as `cfg_status`.)
# [2] Check if dependency is supported.
try:
cfg_name = self.usr_config[cfg]
dep_name = self.usr_config[dep]
cfg_status = self.in_range(cfg_name, cfg_req)
dep_status = self.in_range(dep_name, dep_req)
# If both status's are `True`, then user config meets dependency
# spec.
if cfg_status:
if not dep_status:
# throw error
err_msg = "[Error] User config has a dependency that cannot"
err_msg += " be supported. "
err_msg += "'%s' has a dependency on " % str(config_name)
err_msg += "'%s'." % str(dep)
logging.error(err_msg)
self.error_msg.append(err_msg)
self.failures.append([config_name, err_msg])
temp_status = False
except KeyError:
err_msg = "[Error] Dependency is missing from `Required`. "
err_msg += "(config = %s, ""dep = %s)" % (cfg, dep)
logging.error(err_msg)
self.error_msg.append(err_msg)
self.failures.append([config_name, err_msg])
temp_status = False
# At this point, all requirement related to the user config has been
# checked and passed. Append to `successes` list.
if temp_status:
self.successes.append([config_name, spec])
else:
overall_status = False
return overall_status
@@ -0,0 +1,118 @@
# Copyright 2019 The TensorFlow Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# ==============================================================================
"""Tests for version compatibility checker for TensorFlow Builder."""
import os
import unittest
from tensorflow.tools.tensorflow_builder.compat_checker import compat_checker
PATH_TO_DIR = "tensorflow/tools/tensorflow_builder/compat_checker"
USER_CONFIG_IN_RANGE = {
"apple": ["1.0"],
"banana": ["3"],
"kiwi": ["2.0"],
"watermelon": ["2.0.0"],
"orange": ["4.1"],
"cherry": ["1.5"],
"cranberry": ["1.0"],
"raspberry": ["3.0"],
"tangerine": ["2.0.0"],
"jackfruit": ["1.0"],
"grapefruit": ["2.0"],
"apricot": ["wind", "flower"],
"grape": ["7.1"],
"blueberry": ["3.0"]
}
USER_CONFIG_NOT_IN_RANGE = {
"apple": ["4.0"],
"banana": ["5"],
"kiwi": ["3.5"],
"watermelon": ["5.0"],
"orange": ["3.5"],
"cherry": ["2.0"],
"raspberry": ["-1"],
"cranberry": ["4.5"],
"tangerine": ["0"],
"jackfruit": ["5.0"],
"grapefruit": ["2.5"],
"apricot": ["hello", "world"],
"blueberry": ["11.0"],
"grape": ["7.0"],
"cantaloupe": ["11.0"]
}
USER_CONFIG_MISSING = {
"avocado": ["3.0"],
"apple": [],
"banana": ""
}
class CompatCheckerTest(unittest.TestCase):
def setUp(self):
"""Set up test."""
super(CompatCheckerTest, self).setUp()
self.test_file = os.path.join(PATH_TO_DIR, "test_config.ini")
def testWithUserConfigInRange(self):
"""Test a set of configs that are supported.
Testing with the following combination should always return `success`:
[1] A set of configurations that are supported and/or compatible.
[2] `.ini` config file with proper formatting.
"""
# Initialize compatibility checker.
self.compat_checker = compat_checker.ConfigCompatChecker(
USER_CONFIG_IN_RANGE, self.test_file)
# Compatibility check should succeed.
self.assertTrue(self.compat_checker.check_compatibility())
# Make sure no warning or error messages are recorded.
self.assertFalse(len(self.compat_checker.error_msg))
# Make sure total # of successes match total # of configs.
cnt = len(list(USER_CONFIG_IN_RANGE.keys()))
self.assertEqual(len(self.compat_checker.successes), cnt)
def testWithUserConfigNotInRange(self):
"""Test a set of configs that are NOT supported.
Testing with the following combination should always return `failure`:
[1] A set of configurations that are NOT supported and/or compatible.
[2] `.ini` config file with proper formatting.
"""
self.compat_checker = compat_checker.ConfigCompatChecker(
USER_CONFIG_NOT_IN_RANGE, self.test_file)
# Compatibility check should fail.
self.assertFalse(self.compat_checker.check_compatibility())
# Check error and warning messages.
err_msg_list = self.compat_checker.failures
self.assertTrue(len(err_msg_list))
# Make sure total # of failures match total # of configs.
cnt = len(list(USER_CONFIG_NOT_IN_RANGE.keys()))
self.assertEqual(len(err_msg_list), cnt)
def testWithUserConfigMissing(self):
"""Test a set of configs that are empty or missing specification."""
self.compat_checker = compat_checker.ConfigCompatChecker(
USER_CONFIG_MISSING, self.test_file)
# With missing specification in config file, the check should
# always fail.
self.assertFalse(self.compat_checker.check_compatibility())
if __name__ == "__main__":
unittest.main()
@@ -0,0 +1,26 @@
[Required]
platform = [linux]
uarch = [x86_64, x86]
cuda = [9.0, 10.0, 10.1]
cudnn = [range(7.0.0, 8.0.0)]
gcc = [range(4.8, )]
python = [range(3.3, 3.7) include(2.7)]
glibc = [range(2.24, )]
libstdcpp = [3.4.25]
tensorflow = [range(1.0.0, 1.13.1)]
tensorflow_gpu = [range(1.0.0, 1.13.1)]
[Optional]
isa = [avx, avx2, avx512f, sse4, sse4_2]
[Unsupported]
uarch = [i386]
platform = [macos, windows]
[Dependency]
python = [
python 3.7 requires tensorflow 1.13]
tensorflow = [
tensorflow range(1.0.0, 1.13.1) requires gcc range(4.8, )]
tensorflow_gpu = [
tensorflow_gpu range(1.0.0, 1.13.1) requires gcc range(4.8, )]
@@ -0,0 +1,33 @@
[Required]
apple = [1.0, 2.0, 3.0]
banana = [1, 2, 3]
kiwi = [1.0.0, 2.0, 3]
watermelon = [range(1.0, 3.0)]
orange = [range(1.0, 3.0) include(4.0, 4.1, 5.0)]
cherry = [range(1.0, 3.0) include(4.0) exclude(2.0)]
raspberry = [range(,)]
cranberry = [range(1.0,) exclude(3.0, 4.0, 4.5)]
tangerine = [range(1.0, )]
jackfruit = [range(,3.0)]
grapefruit = [range( ,3.0) include(4.0) exclude(2.5)]
apricot = [wind, water, flower, light, air]
blueberry = [range(1.0, 10.0)]
[Optional]
mango = [range(1.0, 3.0) include(6.0)]
[Unsupported]
grape = [7.0, 8.0]
cantaloupe = [range(10.0, 15.0) include(2.0) exclude(11.0)]
[Dependency]
apple = [
apple 2.0 requires banana 3]
orange = [orange 4.1 requires banana 3]
watermelon = [
watermelon 2.0 requires kiwi 2.0,
watermelon 1.0 requires kiwi 1.0.0]
banana = [
banana 2 requires cherry 1.5]
cherry = [cherry range(1.0, 2.0) include(2.2) exclude(1.2) requires raspberry 3.0]
jackfruit = [jackfruit 2.0 requires grapefruit range(1.0, 2.0) include(2.2) exclude(1.2)]
@@ -0,0 +1,46 @@
# Description:
# TensorFlow builder (TensorFlow on Demand project).
load("@xla//third_party/rules_python/python:py_binary.bzl", "py_binary")
load("@xla//third_party/rules_python/python:py_library.bzl", "py_library")
package(
# copybara:uncomment default_applicable_licenses = ["//tensorflow:license"],
licenses = ["notice"],
)
py_binary(
name = "config_detector",
srcs = ["config_detector.py"],
data = [
"//tensorflow/tools/tensorflow_builder/config_detector/data/golden:cuda_cc_golden",
],
strict_deps = True,
deps = [
":cuda_compute_capability_lib",
"@absl_py//absl:app",
"@absl_py//absl/flags",
],
)
py_binary(
name = "cuda_compute_capability",
srcs = ["data/cuda_compute_capability.py"],
strict_deps = True,
deps = [
"@absl_py//absl:app",
"@absl_py//absl/flags",
"@six_archive//:six",
],
)
py_library(
name = "cuda_compute_capability_lib",
srcs = ["data/cuda_compute_capability.py"],
strict_deps = True,
deps = [
"@absl_py//absl:app",
"@absl_py//absl/flags",
"@six_archive//:six",
],
)
@@ -0,0 +1,662 @@
# Copyright 2019 The TensorFlow Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# ==============================================================================
"""Auto-detects machine configurations and outputs the results to shell or file.
Supports linux only currently.
Usage:
python config_detector.py [--save_output] [--filename] [--debug]
Example command:
python config_detector.py --save_output=True --filename=configs.json
--debug=False
Flag option(s):
save_output (True | False) Save output to a file.
(Default: True)
filename <file_name>.json Filename(.json) for storing configs.
(Default: `configs.json`)
debug (True | False) View debug and stderr messages.
(Default: False)
The following machine configuration will be detected:
Platform Operating system (linux | macos | windows)
CPU CPU type (e.g. `GenuineIntel`)
CPU architecture Processor type (32-bit | 64-bit)
CPU ISA CPU instruction set (e.g. `sse4`, `sse4_1`, `avx`)
Distribution Operating system distribution (e.g. Ubuntu)
Distribution version Operating system distribution version (e.g. 14.04)
GPU GPU type (e.g. `Tesla K80`)
GPU count Number of GPU's available
CUDA version CUDA version by default (e.g. `10.1`)
CUDA version all CUDA version(s) all available
cuDNN version cuDNN version (e.g. `7.5.0`)
GCC version GCC version (e.g. `7.3.0`)
GLIBC version GLIBC version (e.g. `2.24`)
libstdc++ version libstdc++ version (e.g. `3.4.25`)
Output:
Shell output (print)
A table containing status and info on all configurations will be
printed out to shell.
Configuration file (.json):
Depending on `--save_output` option, this script outputs a .json file
(in the same directory) containing all user machine configurations
that were detected.
"""
# pylint: disable=broad-except
import collections
import json
import re
import subprocess
import sys
from absl import app
from absl import flags
from tensorflow.tools.tensorflow_builder.config_detector.data import cuda_compute_capability
FLAGS = flags.FLAGS
# Define all flags
flags.DEFINE_boolean("save_output", True, "Save output to a file. [True/False]")
flags.DEFINE_string("filename", "configs.json", "Output filename.")
flags.DEFINE_boolean("debug", False, "View debug messages. [True/False]")
# For linux: commands for retrieving user machine configs.
cmds_linux = {
"cpu_type": (
"cat /proc/cpuinfo 2>&1 | grep 'vendor' | uniq"),
"cpu_arch": (
"uname -m"),
"distrib": (
"cat /etc/*-release | grep DISTRIB_ID* | sed 's/^.*=//'"),
"distrib_ver": (
"cat /etc/*-release | grep DISTRIB_RELEASE* | sed 's/^.*=//'"),
"gpu_type": (
"sudo lshw -C display | grep product:* | sed 's/^.*: //'"),
"gpu_type_no_sudo":
r"lspci | grep 'VGA compatible\|3D controller' | cut -d' ' -f 1 | "
r"xargs -i lspci -v -s {} | head -n 2 | tail -1 | "
r"awk '{print $(NF-2), $(NF-1), $NF}'",
"gpu_count": (
"sudo lshw -C display | grep *-display:* | wc -l"),
"gpu_count_no_sudo": (
r"lspci | grep 'VGA compatible\|3D controller' | wc -l"),
"cuda_ver_all": (
"ls -d /usr/local/cuda* 2> /dev/null"),
"cuda_ver_dflt": (
["nvcc --version 2> /dev/null",
"cat /usr/local/cuda/version.txt 2> /dev/null | awk '{print $NF}'"]),
"cudnn_ver": (
["whereis cudnn.h",
"cat `awk '{print $2}'` | grep CUDNN_MAJOR -A 2 | echo "
"`awk '{print $NF}'` | awk '{print $1, $2, $3}' | sed 's/ /./g'"]),
"gcc_ver": (
"gcc --version | awk '{print $NF}' | head -n 1"),
"glibc_ver": (
"ldd --version | tail -n+1 | head -n 1 | awk '{print $NF}'"),
"libstdcpp_ver":
"strings $(/sbin/ldconfig -p | grep libstdc++ | head -n 1 | "
"awk '{print $NF}') | grep LIBCXX | tail -2 | head -n 1",
"cpu_isa": (
"cat /proc/cpuinfo | grep flags | head -n 1"),
}
cmds_all = {
"linux": cmds_linux,
}
# Global variable(s).
PLATFORM = None
GPU_TYPE = None
PATH_TO_DIR = "tensorflow/tools/tensorflow_builder/config_detector"
def run_shell_cmd(args):
"""Executes shell commands and returns output.
Args:
args: String of shell commands to run.
Returns:
Tuple output (stdoutdata, stderrdata) from running the shell commands.
"""
proc = subprocess.Popen(
args,
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT
)
return proc.communicate()
def get_platform():
"""Retrieves platform information.
Currently the script only support linux. If other platoforms such as Windows
or MacOS is detected, it throws an error and terminates.
Returns:
String that is platform type.
e.g. 'linux'
"""
global PLATFORM
cmd = "uname"
out, err = run_shell_cmd(cmd)
platform_detected = out.strip().lower()
if platform_detected != "linux":
if err and FLAGS.debug:
print("Error in detecting platform:\n %s" % str(err))
print("Error: Detected unsupported operating system.\nStopping...")
sys.exit(1)
else:
PLATFORM = platform_detected
return PLATFORM
def get_cpu_type():
"""Retrieves CPU (type) information.
Returns:
String that is name of the CPU.
e.g. 'GenuineIntel'
"""
key = "cpu_type"
out, err = run_shell_cmd(cmds_all[PLATFORM][key])
cpu_detected = out.split(b":")[1].strip()
if err and FLAGS.debug:
print("Error in detecting CPU type:\n %s" % str(err))
return cpu_detected
def get_cpu_arch():
"""Retrieves processor architecture type (32-bit or 64-bit).
Returns:
String that is CPU architecture.
e.g. 'x86_64'
"""
key = "cpu_arch"
out, err = run_shell_cmd(cmds_all[PLATFORM][key])
if err and FLAGS.debug:
print("Error in detecting CPU arch:\n %s" % str(err))
return out.strip(b"\n")
def get_distrib():
"""Retrieves distribution name of the operating system.
Returns:
String that is the name of distribution.
e.g. 'Ubuntu'
"""
key = "distrib"
out, err = run_shell_cmd(cmds_all[PLATFORM][key])
if err and FLAGS.debug:
print("Error in detecting distribution:\n %s" % str(err))
return out.strip(b"\n")
def get_distrib_version():
"""Retrieves distribution version of the operating system.
Returns:
String that is the distribution version.
e.g. '14.04'
"""
key = "distrib_ver"
out, err = run_shell_cmd(cmds_all[PLATFORM][key])
if err and FLAGS.debug:
print(
"Error in detecting distribution version:\n %s" % str(err)
)
return out.strip(b"\n")
def get_gpu_type():
"""Retrieves GPU type.
Returns:
String that is the name of the detected NVIDIA GPU.
e.g. 'Tesla K80'
'unknown' will be returned if detected GPU type is an unknown name.
Unknown name refers to any GPU name that is not specified in this page:
https://developer.nvidia.com/cuda-gpus
"""
global GPU_TYPE
key = "gpu_type_no_sudo"
gpu_dict = cuda_compute_capability.retrieve_from_golden()
out, err = run_shell_cmd(cmds_all[PLATFORM][key])
ret_val = out.split(b" ")
gpu_id = ret_val[0]
if err and FLAGS.debug:
print("Error in detecting GPU type:\n %s" % str(err))
if not isinstance(ret_val, list):
GPU_TYPE = "unknown"
return gpu_id, GPU_TYPE
else:
if "[" or "]" in ret_val[1]:
gpu_release = ret_val[1].replace(b"[", b"") + b" "
gpu_release += ret_val[2].replace(b"]", b"").strip(b"\n")
else:
gpu_release = ret_val[1].replace("\n", " ")
if gpu_release not in gpu_dict:
GPU_TYPE = "unknown"
else:
GPU_TYPE = gpu_release
return gpu_id, GPU_TYPE
def get_gpu_count():
"""Retrieves total number of GPU's available in the system.
Returns:
Integer that is the total # of GPU's found.
"""
key = "gpu_count_no_sudo"
out, err = run_shell_cmd(cmds_all[PLATFORM][key])
if err and FLAGS.debug:
print("Error in detecting GPU count:\n %s" % str(err))
return out.strip(b"\n")
def get_cuda_version_all():
"""Retrieves all additional CUDA versions available (other than default).
For retrieving default CUDA version, use `get_cuda_version` function.
stderr is silenced by default. Setting FLAGS.debug mode will not enable it.
Remove `2> /dev/null` command from `cmds_linux['cuda_ver_dflt']` to enable
stderr.
Returns:
List of all CUDA versions found (except default version).
e.g. ['10.1', '10.2']
"""
key = "cuda_ver_all"
out, err = run_shell_cmd(cmds_all[PLATFORM.lower()][key])
ret_val = out.split(b"\n")
filtered = []
for item in ret_val:
if item not in ["\n", ""]:
filtered.append(item)
all_vers = []
for item in filtered:
ver_re = re.search(r".*/cuda(\-[\d]+\.[\d]+)?", item.decode("utf-8"))
if ver_re.group(1):
all_vers.append(ver_re.group(1).strip("-"))
if err and FLAGS.debug:
print("Error in detecting CUDA version:\n %s" % str(err))
return all_vers
def get_cuda_version_default():
"""Retrieves default CUDA version.
Default version is the version found in `/usr/local/cuda/` installation.
stderr is silenced by default. Setting FLAGS.debug mode will not enable it.
Remove `2> /dev/null` command from `cmds_linux['cuda_ver_dflt']` to enable
stderr.
It iterates through two types of version retrieval method:
1) Using `nvcc`: If `nvcc` is not available, then it uses next method.
2) Read version file (`version.txt`) found in CUDA install directory.
Returns:
String that is the default CUDA version.
e.g. '10.1'
"""
key = "cuda_ver_dflt"
out = ""
cmd_list = cmds_all[PLATFORM.lower()][key]
for i, cmd in enumerate(cmd_list):
try:
out, err = run_shell_cmd(cmd)
if not out:
raise Exception(err)
except Exception as e:
if FLAGS.debug:
print("\nWarning: Encountered issue while retrieving default CUDA "
"version. (%s) Trying a different method...\n" % e)
if i == len(cmd_list) - 1:
if FLAGS.debug:
print("Error: Cannot retrieve CUDA default version.\nStopping...")
else:
pass
return out.strip("\n")
def get_cuda_compute_capability(source_from_url=False):
"""Retrieves CUDA compute capability based on the detected GPU type.
This function uses the `cuda_compute_capability` module to retrieve the
corresponding CUDA compute capability for the given GPU type.
Args:
source_from_url: Boolean deciding whether to source compute capability
from NVIDIA website or from a local golden file.
Returns:
List of all supported CUDA compute capabilities for the given GPU type.
e.g. ['3.5', '3.7']
"""
if not GPU_TYPE:
if FLAGS.debug:
print("Warning: GPU_TYPE is empty. "
"Make sure to call `get_gpu_type()` first.")
elif GPU_TYPE == "unknown":
if FLAGS.debug:
print("Warning: Unknown GPU is detected. "
"Skipping CUDA compute capability retrieval.")
else:
if source_from_url:
cuda_compute_capa = cuda_compute_capability.retrieve_from_web()
else:
cuda_compute_capa = cuda_compute_capability.retrieve_from_golden()
return cuda_compute_capa[GPU_TYPE]
return
def get_cudnn_version():
"""Retrieves the version of cuDNN library detected.
Returns:
String that is the version of cuDNN library detected.
e.g. '7.5.0'
"""
key = "cudnn_ver"
cmds = cmds_all[PLATFORM.lower()][key]
out, err = run_shell_cmd(cmds[0])
if err and FLAGS.debug:
print("Error in finding `cudnn.h`:\n %s" % str(err))
if len(out.split(b" ")) > 1:
cmd = cmds[0] + " | " + cmds[1]
out_re, err_re = run_shell_cmd(cmd)
if err_re and FLAGS.debug:
print("Error in detecting cuDNN version:\n %s" % str(err_re))
return out_re.strip(b"\n")
else:
return
def get_gcc_version():
"""Retrieves version of GCC detected.
Returns:
String that is the version of GCC.
e.g. '7.3.0'
"""
key = "gcc_ver"
out, err = run_shell_cmd(cmds_all[PLATFORM.lower()][key])
if err and FLAGS.debug:
print("Error in detecting GCC version:\n %s" % str(err))
return out.strip(b"\n")
def get_glibc_version():
"""Retrieves version of GLIBC detected.
Returns:
String that is the version of GLIBC.
e.g. '2.24'
"""
key = "glibc_ver"
out, err = run_shell_cmd(cmds_all[PLATFORM.lower()][key])
if err and FLAGS.debug:
print("Error in detecting GCC version:\n %s" % str(err))
return out.strip(b"\n")
def get_libstdcpp_version():
"""Retrieves version of libstdc++ detected.
Returns:
String that is the version of libstdc++.
e.g. '3.4.25'
"""
key = "libstdcpp_ver"
out, err = run_shell_cmd(cmds_all[PLATFORM.lower()][key])
if err and FLAGS.debug:
print("Error in detecting libstdc++ version:\n %s" % str(err))
ver = out.split(b"_")[-1].replace(b"\n", b"")
return ver
def get_cpu_isa_version():
"""Retrieves all Instruction Set Architecture(ISA) available.
Required ISA(s): 'avx', 'avx2', 'avx512f', 'sse4', 'sse4_1'
Returns:
Tuple
(list of available ISA, list of missing ISA)
"""
key = "cpu_isa"
out, err = run_shell_cmd(cmds_all[PLATFORM.lower()][key])
if err and FLAGS.debug:
print("Error in detecting supported ISA:\n %s" % str(err))
ret_val = out
required_isa = ["avx", "avx2", "avx512f", "sse4", "sse4_1"]
found = []
missing = []
for isa in required_isa:
for sys_isa in ret_val.split(b" "):
if isa == sys_isa:
if isa not in found:
found.append(isa)
missing = list(set(required_isa) - set(found))
return found, missing
def get_python_version():
"""Retrieves default Python version.
Returns:
String that is the version of default Python.
e.g. '2.7.4'
"""
ver = str(sys.version_info)
mmm = re.search(r".*major=([\d]), minor=([\d]), micro=([\d]+),.*", ver)
return mmm.group(1) + "." + mmm.group(2) + "." + mmm.group(3)
def get_all_configs():
"""Runs all functions for detecting user machine configurations.
Returns:
Tuple
(List of all configurations found,
List of all missing configurations,
List of all configurations found with warnings,
Dict of all configurations)
"""
all_functions = collections.OrderedDict(
[("Platform", get_platform()),
("CPU", get_cpu_type()),
("CPU arch", get_cpu_arch()),
("Distribution", get_distrib()),
("Distribution version", get_distrib_version()),
("GPU", get_gpu_type()[1]),
("GPU count", get_gpu_count()),
("CUDA version (default)", get_cuda_version_default()),
("CUDA versions (all)", get_cuda_version_all()),
("CUDA compute capability",
get_cuda_compute_capability(get_gpu_type()[1])),
("cuDNN version", get_cudnn_version()),
("GCC version", get_gcc_version()),
("Python version (default)", get_python_version()),
("GNU C Lib (glibc) version", get_glibc_version()),
("libstdc++ version", get_libstdcpp_version()),
("CPU ISA (min requirement)", get_cpu_isa_version())]
)
configs_found = []
json_data = {}
missing = []
warning = []
for config, call_func in all_functions.items():
ret_val = call_func
if not ret_val:
configs_found.append([config, "\033[91m\033[1mMissing\033[0m"])
missing.append([config])
json_data[config] = ""
elif ret_val == "unknown":
configs_found.append([config, "\033[93m\033[1mUnknown type\033[0m"])
warning.append([config, ret_val])
json_data[config] = "unknown"
else:
if "ISA" in config:
if not ret_val[1]:
# Not missing any required ISA
configs_found.append([config, ret_val[0]])
json_data[config] = ret_val[0]
else:
configs_found.append([
config,
"\033[91m\033[1mMissing " + str(ret_val[1][1:-1]) + "\033[0m"
])
missing.append(
[config,
"\n\t=> Found %s but missing %s"
% (str(ret_val[0]), str(ret_val[1]))]
)
json_data[config] = ret_val[0]
else:
configs_found.append([config, ret_val])
json_data[config] = ret_val
return (configs_found, missing, warning, json_data)
def print_all_configs(configs, missing, warning):
"""Prints the status and info on all configurations in a table format.
Args:
configs: List of all configurations found.
missing: List of all configurations that are missing.
warning: List of all configurations found with warnings.
"""
print_text = ""
llen = 65 # line length
for i, row in enumerate(configs):
if i != 0:
print_text += "-" * llen + "\n"
if isinstance(row[1], list):
val = ", ".join(row[1])
else:
val = row[1]
print_text += " {: <28}".format(row[0]) + " {: <25}".format(val) + "\n"
print_text += "="*llen
print("\n\n {: ^32} {: ^25}".format("Configuration(s)",
"Detected value(s)"))
print("="*llen)
print(print_text)
if missing:
print("\n * ERROR: The following configurations are missing:")
for m in missing:
print(" ", *m)
if warning:
print("\n * WARNING: The following configurations could cause issues:")
for w in warning:
print(" ", *w)
if not missing and not warning:
print("\n * INFO: Successfully found all configurations.")
print("\n")
def save_to_file(json_data, filename):
"""Saves all detected configuration(s) into a JSON file.
Args:
json_data: Dict of all configurations found.
filename: String that is the name of the output JSON file.
"""
if filename[-5:] != ".json":
print("filename: %s" % filename)
filename += ".json"
with open(PATH_TO_DIR + "/" + filename, "w") as f:
json.dump(json_data, f, sort_keys=True, indent=4)
print(" Successfully wrote configs to file `%s`.\n" % (filename))
def manage_all_configs(save_results, filename):
"""Manages configuration detection and retrieval based on user input.
Args:
save_results: Boolean indicating whether to save the results to a file.
filename: String that is the name of the output JSON file.
"""
# Get all configs
all_configs = get_all_configs()
# Print all configs based on user input
print_all_configs(all_configs[0], all_configs[1], all_configs[2])
# Save all configs to a file based on user request
if save_results:
save_to_file(all_configs[3], filename)
def main(argv):
if len(argv) > 3:
raise app.UsageError("Too many command-line arguments.")
manage_all_configs(
save_results=FLAGS.save_output,
filename=FLAGS.filename,
)
if __name__ == "__main__":
app.run(main)
@@ -0,0 +1,238 @@
# Copyright 2019 The TensorFlow Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# ==============================================================================
"""Retrieves CUDA compute capability from NVIDIA webpage and creates a `.csv`.
This module is mainly written to supplement for `../config_detector.py`
which retrieves CUDA compute capability from existing golden file.
The golden file resides inside `./golden` directory.
Usage:
python cuda_compute_capability.py
Output:
Creates `compute_capability.csv` file in the same directory by default. If
the file already exists, then it overwrites the file.
In order to use the new `.csv` as the golden, then it should replace the
original golden file (`./golden/compute_capability_golden.csv`) with the
same file name and path.
"""
import collections
import difflib
import os
import re
import urllib.request as urllib
from absl import app
from absl import flags
FLAGS = flags.FLAGS
PATH_TO_DIR = "tensorflow/tools/tensorflow_builder/config_detector"
CUDA_CC_GOLDEN_DIR = PATH_TO_DIR + "/data/golden/compute_capability_golden.csv"
def retrieve_from_web(generate_csv=False):
"""Retrieves list of all CUDA compute capability from NVIDIA webpage.
Args:
generate_csv: Boolean for generating an output file containing
the results.
Returns:
OrderedDict that is a list of all CUDA compute capability listed on the
NVIDIA page. Order goes from top to bottom of the webpage content (.html).
"""
url = "https://developer.nvidia.com/cuda-gpus"
source = urllib.request.urlopen(url)
matches = []
while True:
line = source.readline()
if "</html>" in line:
break
else:
gpu = re.search(r"<a href=.*>([\w\S\s\d\[\]\,]+[^*])</a>(<a href=.*)?.*",
line)
capability = re.search(
r"([\d]+).([\d]+)(/)?([\d]+)?(.)?([\d]+)?.*</td>.*", line)
if gpu:
matches.append(gpu.group(1))
elif capability:
if capability.group(3):
capability_str = capability.group(4) + "." + capability.group(6)
else:
capability_str = capability.group(1) + "." + capability.group(2)
matches.append(capability_str)
return create_gpu_capa_map(matches, generate_csv)
def retrieve_from_golden():
"""Retrieves list of all CUDA compute capability from a golden file.
The following file is set as default:
`./golden/compute_capability_golden.csv`
Returns:
Dictionary that lists of all CUDA compute capability in the following
format:
{'<GPU name>': ['<version major>.<version minor>', ...], ...}
If there are multiple versions available for a given GPU, then it
appends all supported versions in the value list (in the key-value
pair.)
"""
out_dict = dict()
with open(CUDA_CC_GOLDEN_DIR) as g_file:
for line in g_file:
line_items = line.split(",")
val_list = []
for item in line_items[1:]:
val_list.append(item.strip("\n"))
out_dict[line_items[0]] = val_list
return out_dict
def create_gpu_capa_map(match_list,
generate_csv=False,
filename="compute_capability"):
"""Generates a map between GPU types and corresponding compute capability.
This method is used for retrieving CUDA compute capability from the web only.
Args:
match_list: List of all CUDA compute capability detected from the webpage.
generate_csv: Boolean for creating csv file to store results.
filename: String that is the name of the csv file (without `.csv` ending).
Returns:
OrderedDict that lists in the incoming order of all CUDA compute capability
provided as `match_list`.
"""
gpu_capa = collections.OrderedDict()
include = False
gpu = ""
cnt = 0
mismatch_cnt = 0
for match in match_list:
if "Products" in match:
if not include:
include = True
continue
elif "www" in match:
include = False
break
if include:
if gpu:
if gpu in gpu_capa:
gpu_capa[gpu].append(match)
else:
gpu_capa[gpu] = [match]
gpu = ""
cnt += 1
if len(list(gpu_capa.keys())) < cnt:
mismatch_cnt += 1
cnt = len(list(gpu_capa.keys()))
else:
gpu = match
if generate_csv:
f_name = filename + ".csv"
write_csv_from_dict(f_name, gpu_capa)
return gpu_capa
def write_csv_from_dict(filename, input_dict):
"""Writes out a `.csv` file from an input dictionary.
After writing out the file, it checks the new list against the golden
to make sure golden file is up-to-date.
Args:
filename: String that is the output file name.
input_dict: Dictionary that is to be written out to a `.csv` file.
"""
f = open(PATH_TO_DIR + "/data/" + filename, "w")
for k, v in input_dict.items():
line = k
for item in v:
line += "," + item
f.write(line + "\n")
f.flush()
print("Wrote to file %s" % filename)
check_with_golden(filename)
def check_with_golden(filename):
"""Checks the newly created CUDA compute capability file with the golden.
If differences are found, then it prints a list of all mismatches as
a `WARNING`.
Golden file must reside in `golden/` directory.
Args:
filename: String that is the name of the newly created file.
"""
path_to_file = PATH_TO_DIR + "/data/" + filename
if os.path.isfile(path_to_file) and os.path.isfile(CUDA_CC_GOLDEN_DIR):
with open(path_to_file, "r") as f_new:
with open(CUDA_CC_GOLDEN_DIR, "r") as f_golden:
diff = difflib.unified_diff(
f_new.readlines(),
f_golden.readlines(),
fromfile=path_to_file,
tofile=CUDA_CC_GOLDEN_DIR
)
diff_list = []
for line in diff:
diff_list.append(line)
if diff_list:
print("WARNING: difference(s) found between new csv and golden csv.")
print(diff_list)
else:
print("No difference found between new csv and golen csv.")
def print_dict(py_dict):
"""Prints dictionary with formatting (2 column table).
Args:
py_dict: Dictionary that is to be printed out in a table format.
"""
for gpu, cc in py_dict.items():
print("{:<25}{:<25}".format(gpu, cc))
def main(argv):
if len(argv) > 2:
raise app.UsageError("Too many command-line arguments.")
retrieve_from_web(generate_csv=True)
if __name__ == "__main__":
app.run(main)
@@ -0,0 +1,14 @@
# TODO(hyey): describe this package.
package(
# copybara:uncomment default_applicable_licenses = ["//tensorflow:license"],
default_visibility = [
"//tensorflow/tools/tensorflow_builder/config_detector:__subpackages__",
],
licenses = ["notice"],
)
filegroup(
name = "cuda_cc_golden",
srcs = ["compute_capability_golden.csv"],
)
@@ -0,0 +1,208 @@
Tesla K80,3.7,3.7
Tesla K40,3.5,3.5
Tesla K20,3.5,3.5
Tesla C2075,2.0
Tesla C2050/C2070,2.0
Tesla T4,7.5
Tesla V100,7.0
Tesla P100,6.0
Tesla P40,6.1
Tesla P4,6.1
Tesla M60,5.2
Tesla M40,5.2
Tesla K10,3.0
Quadro RTX 8000,7.5
Quadro RTX 6000,7.5
Quadro RTX 5000,7.5
Quadro RTX 4000,7.5
Quadro GV100,7.0
Quadro GP100,6.0
Quadro P6000,6.1
Quadro P5000,6.1,6.1
Quadro P4000,6.1,6.1
Quadro P2000,6.1
Quadro P1000,6.1
Quadro P600,6.1
Quadro P400,6.1
Quadro M6000 24GB,5.2
Quadro M6000,5.2
Quadro K6000,3.5
Quadro M5000,5.2
Quadro K5200,3.5
Quadro K5000,3.0
Quadro M4000,5.2
Quadro K4200,3.0
Quadro K4000,3.0
Quadro M2000,5.2
Quadro K2200,3.0
Quadro K2000,3.0
Quadro K2000D,3.0
Quadro K1200,5.0
Quadro K620,5.0
Quadro K600,3.0
Quadro K420,3.0
Quadro 410,3.0
Quadro Plex 7000,2.0
Quadro P5200,6.1
Quadro P4200,6.1
Quadro P3200,6.1
Quadro P3000,6.1
Quadro M5500M,5.2
Quadro M2200,5.2
Quadro M1200,5.0
Quadro M620,5.2
Quadro M520,5.0
Quadro K6000M,3.0
Quadro K5200M,3.0
Quadro K5100M,3.0
Quadro M5000M,5.0
Quadro K500M,3.0
Quadro K4200M,3.0
Quadro K4100M,3.0
Quadro M4000M,5.0
Quadro K3100M,3.0
Quadro M3000M,5.0
Quadro K2200M,3.0
Quadro K2100M,3.0
Quadro M2000M,5.0
Quadro K1100M,3.0
Quadro M1000M,5.0
Quadro K620M,5.0
Quadro K610M,3.5
Quadro M600M,5.0
Quadro K510M,3.5
Quadro M500M,5.0
NVIDIA NVS 810,5.0
NVIDIA NVS 510,3.0
NVIDIA NVS 315,2.1
NVIDIA NVS 310,2.1
NVS 5400M,2.1
NVS 5200M,2.1
NVS 4200M,2.1
NVIDIA TITAN RTX,7.5
Geforce RTX 2080 Ti,7.5
Geforce RTX 2080,7.5,7.5
Geforce RTX 2070,7.5,7.5
Geforce RTX 2060,7.5,7.5
NVIDIA TITAN V,7.0
NVIDIA TITAN Xp,6.1
NVIDIA TITAN X,6.1
GeForce GTX 1080 Ti,6.1
GeForce GTX 1080,6.1,6.1
GeForce GTX 1070,6.1,6.1
GeForce GTX 1060,6.1,6.1
GeForce GTX 1050,6.1
GeForce GTX TITAN X,5.2
GeForce GTX TITAN Z,3.5
GeForce GTX TITAN Black,3.5
GeForce GTX TITAN,3.5
GeForce GTX 980 Ti,5.2
GeForce GTX 980,5.2,5.2
GeForce GTX 970,5.2
GeForce GTX 960,5.2
GeForce GTX 950,5.2
GeForce GTX 780 Ti,3.5
GeForce GTX 780,3.5
GeForce GTX 770,3.0
GeForce GTX 760,3.0
GeForce GTX 750 Ti,5.0
GeForce GTX 750,5.0
GeForce GTX 690,3.0
GeForce GTX 680,3.0
GeForce GTX 670,3.0
GeForce GTX 660 Ti,3.0
GeForce GTX 660,3.0
GeForce GTX 650 Ti BOOST,3.0
GeForce GTX 650 Ti,3.0
GeForce GTX 650,3.0
GeForce GTX 560 Ti,2.1
GeForce GTX 550 Ti,2.1
GeForce GTX 460,2.1
GeForce GTS 450,2.1,2.1
GeForce GTX 590,2.0
GeForce GTX 580,2.0
GeForce GTX 570,2.0
GeForce GTX 480,2.0
GeForce GTX 470,2.0
GeForce GTX 465,2.0
GeForce GT 740,3.0
GeForce GT 730,3.5,2.1
GeForce GT 720,3.5
GeForce GT 705,3.5
GeForce GT 640 (GDDR5),3.5
GeForce GT 640,2.1
GeForce GT 630,2.1
GeForce GT 620,2.1
GeForce GT 610,2.1
GeForce GT 520,2.1
GeForce GT 440,2.1,2.1
GeForce GT 430,2.1,2.1
GeForce GTX 980M,5.2
GeForce GTX 970M,5.2
GeForce GTX 965M,5.2
GeForce GTX 960M,5.0
GeForce GTX 950M,5.0
GeForce 940M,5.0
GeForce 930M,5.0
GeForce 920M,3.5
GeForce 910M,5.2
GeForce GTX 880M,3.0
GeForce GTX 870M,3.0
GeForce GTX 860M,5.0
GeForce GTX 850M,5.0
GeForce 840M,5.0
GeForce 830M,5.0
GeForce 820M,2.1
GeForce 800M,2.1
GeForce GTX 780M,3.0
GeForce GTX 770M,3.0
GeForce GTX 765M,3.0
GeForce GTX 760M,3.0
GeForce GTX 680MX,3.0
GeForce GTX 680M,3.0
GeForce GTX 675MX,3.0
GeForce GTX 675M,2.1
GeForce GTX 670MX,3.0
GeForce GTX 670M,2.1
GeForce GTX 660M,3.0
GeForce GT 755M,3.0
GeForce GT 750M,3.0
GeForce GT 650M,3.0
GeForce GT 745M,3.0
GeForce GT 645M,3.0
GeForce GT 740M,3.0
GeForce GT 730M,3.0,3.0
GeForce GT 640M,3.0
GeForce GT 640M LE,3.0
GeForce GT 735M,3.0
GeForce GT 635M,2.1
GeForce GT 630M,2.1
GeForce GT 625M,2.1
GeForce GT 720M,2.1
GeForce GT 620M,2.1
GeForce 710M,2.1,2.1
GeForce 705M,2.1
GeForce 610M,2.1
GeForce GTX 580M,2.1
GeForce GTX 570M,2.1
GeForce GTX 560M,2.1
GeForce GT 555M,2.1
GeForce GT 550M,2.1
GeForce GT 540M,2.1
GeForce GT 525M,2.1
GeForce GT 520MX,2.1
GeForce GT 520M,2.1
GeForce GTX 485M,2.1
GeForce GTX 470M,2.1
GeForce GTX 460M,2.1
GeForce GT 445M,2.1
GeForce GT 435M,2.1
GeForce GT 420M,2.1
GeForce GT 415M,2.1
GeForce GTX 480M,2.0
GeForce 410M,2.1
Jetson TX2,6.2
Jetson TX1,5.3
Jetson TK1,3.2
Tegra X1,5.3
Tegra K1,3.2
1 Tesla K80,3.7,3.7
2 Tesla K40,3.5,3.5
3 Tesla K20,3.5,3.5
4 Tesla C2075,2.0
5 Tesla C2050/C2070,2.0
6 Tesla T4,7.5
7 Tesla V100,7.0
8 Tesla P100,6.0
9 Tesla P40,6.1
10 Tesla P4,6.1
11 Tesla M60,5.2
12 Tesla M40,5.2
13 Tesla K10,3.0
14 Quadro RTX 8000,7.5
15 Quadro RTX 6000,7.5
16 Quadro RTX 5000,7.5
17 Quadro RTX 4000,7.5
18 Quadro GV100,7.0
19 Quadro GP100,6.0
20 Quadro P6000,6.1
21 Quadro P5000,6.1,6.1
22 Quadro P4000,6.1,6.1
23 Quadro P2000,6.1
24 Quadro P1000,6.1
25 Quadro P600,6.1
26 Quadro P400,6.1
27 Quadro M6000 24GB,5.2
28 Quadro M6000,5.2
29 Quadro K6000,3.5
30 Quadro M5000,5.2
31 Quadro K5200,3.5
32 Quadro K5000,3.0
33 Quadro M4000,5.2
34 Quadro K4200,3.0
35 Quadro K4000,3.0
36 Quadro M2000,5.2
37 Quadro K2200,3.0
38 Quadro K2000,3.0
39 Quadro K2000D,3.0
40 Quadro K1200,5.0
41 Quadro K620,5.0
42 Quadro K600,3.0
43 Quadro K420,3.0
44 Quadro 410,3.0
45 Quadro Plex 7000,2.0
46 Quadro P5200,6.1
47 Quadro P4200,6.1
48 Quadro P3200,6.1
49 Quadro P3000,6.1
50 Quadro M5500M,5.2
51 Quadro M2200,5.2
52 Quadro M1200,5.0
53 Quadro M620,5.2
54 Quadro M520,5.0
55 Quadro K6000M,3.0
56 Quadro K5200M,3.0
57 Quadro K5100M,3.0
58 Quadro M5000M,5.0
59 Quadro K500M,3.0
60 Quadro K4200M,3.0
61 Quadro K4100M,3.0
62 Quadro M4000M,5.0
63 Quadro K3100M,3.0
64 Quadro M3000M,5.0
65 Quadro K2200M,3.0
66 Quadro K2100M,3.0
67 Quadro M2000M,5.0
68 Quadro K1100M,3.0
69 Quadro M1000M,5.0
70 Quadro K620M,5.0
71 Quadro K610M,3.5
72 Quadro M600M,5.0
73 Quadro K510M,3.5
74 Quadro M500M,5.0
75 NVIDIA NVS 810,5.0
76 NVIDIA NVS 510,3.0
77 NVIDIA NVS 315,2.1
78 NVIDIA NVS 310,2.1
79 NVS 5400M,2.1
80 NVS 5200M,2.1
81 NVS 4200M,2.1
82 NVIDIA TITAN RTX,7.5
83 Geforce RTX 2080 Ti,7.5
84 Geforce RTX 2080,7.5,7.5
85 Geforce RTX 2070,7.5,7.5
86 Geforce RTX 2060,7.5,7.5
87 NVIDIA TITAN V,7.0
88 NVIDIA TITAN Xp,6.1
89 NVIDIA TITAN X,6.1
90 GeForce GTX 1080 Ti,6.1
91 GeForce GTX 1080,6.1,6.1
92 GeForce GTX 1070,6.1,6.1
93 GeForce GTX 1060,6.1,6.1
94 GeForce GTX 1050,6.1
95 GeForce GTX TITAN X,5.2
96 GeForce GTX TITAN Z,3.5
97 GeForce GTX TITAN Black,3.5
98 GeForce GTX TITAN,3.5
99 GeForce GTX 980 Ti,5.2
100 GeForce GTX 980,5.2,5.2
101 GeForce GTX 970,5.2
102 GeForce GTX 960,5.2
103 GeForce GTX 950,5.2
104 GeForce GTX 780 Ti,3.5
105 GeForce GTX 780,3.5
106 GeForce GTX 770,3.0
107 GeForce GTX 760,3.0
108 GeForce GTX 750 Ti,5.0
109 GeForce GTX 750,5.0
110 GeForce GTX 690,3.0
111 GeForce GTX 680,3.0
112 GeForce GTX 670,3.0
113 GeForce GTX 660 Ti,3.0
114 GeForce GTX 660,3.0
115 GeForce GTX 650 Ti BOOST,3.0
116 GeForce GTX 650 Ti,3.0
117 GeForce GTX 650,3.0
118 GeForce GTX 560 Ti,2.1
119 GeForce GTX 550 Ti,2.1
120 GeForce GTX 460,2.1
121 GeForce GTS 450,2.1,2.1
122 GeForce GTX 590,2.0
123 GeForce GTX 580,2.0
124 GeForce GTX 570,2.0
125 GeForce GTX 480,2.0
126 GeForce GTX 470,2.0
127 GeForce GTX 465,2.0
128 GeForce GT 740,3.0
129 GeForce GT 730,3.5,2.1
130 GeForce GT 720,3.5
131 GeForce GT 705,3.5
132 GeForce GT 640 (GDDR5),3.5
133 GeForce GT 640,2.1
134 GeForce GT 630,2.1
135 GeForce GT 620,2.1
136 GeForce GT 610,2.1
137 GeForce GT 520,2.1
138 GeForce GT 440,2.1,2.1
139 GeForce GT 430,2.1,2.1
140 GeForce GTX 980M,5.2
141 GeForce GTX 970M,5.2
142 GeForce GTX 965M,5.2
143 GeForce GTX 960M,5.0
144 GeForce GTX 950M,5.0
145 GeForce 940M,5.0
146 GeForce 930M,5.0
147 GeForce 920M,3.5
148 GeForce 910M,5.2
149 GeForce GTX 880M,3.0
150 GeForce GTX 870M,3.0
151 GeForce GTX 860M,5.0
152 GeForce GTX 850M,5.0
153 GeForce 840M,5.0
154 GeForce 830M,5.0
155 GeForce 820M,2.1
156 GeForce 800M,2.1
157 GeForce GTX 780M,3.0
158 GeForce GTX 770M,3.0
159 GeForce GTX 765M,3.0
160 GeForce GTX 760M,3.0
161 GeForce GTX 680MX,3.0
162 GeForce GTX 680M,3.0
163 GeForce GTX 675MX,3.0
164 GeForce GTX 675M,2.1
165 GeForce GTX 670MX,3.0
166 GeForce GTX 670M,2.1
167 GeForce GTX 660M,3.0
168 GeForce GT 755M,3.0
169 GeForce GT 750M,3.0
170 GeForce GT 650M,3.0
171 GeForce GT 745M,3.0
172 GeForce GT 645M,3.0
173 GeForce GT 740M,3.0
174 GeForce GT 730M,3.0,3.0
175 GeForce GT 640M,3.0
176 GeForce GT 640M LE,3.0
177 GeForce GT 735M,3.0
178 GeForce GT 635M,2.1
179 GeForce GT 630M,2.1
180 GeForce GT 625M,2.1
181 GeForce GT 720M,2.1
182 GeForce GT 620M,2.1
183 GeForce 710M,2.1,2.1
184 GeForce 705M,2.1
185 GeForce 610M,2.1
186 GeForce GTX 580M,2.1
187 GeForce GTX 570M,2.1
188 GeForce GTX 560M,2.1
189 GeForce GT 555M,2.1
190 GeForce GT 550M,2.1
191 GeForce GT 540M,2.1
192 GeForce GT 525M,2.1
193 GeForce GT 520MX,2.1
194 GeForce GT 520M,2.1
195 GeForce GTX 485M,2.1
196 GeForce GTX 470M,2.1
197 GeForce GTX 460M,2.1
198 GeForce GT 445M,2.1
199 GeForce GT 435M,2.1
200 GeForce GT 420M,2.1
201 GeForce GT 415M,2.1
202 GeForce GTX 480M,2.0
203 GeForce 410M,2.1
204 Jetson TX2,6.2
205 Jetson TX1,5.3
206 Jetson TK1,3.2
207 Tegra X1,5.3
208 Tegra K1,3.2