setup.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. # Copyright (c) Microsoft Corporation.
  2. # SPDX-License-Identifier: Apache-2.0
  3. # DeepSpeed Team
  4. """
  5. DeepSpeed library
  6. To build wheel on Windows:
  7. 1. Install pytorch, such as pytorch 1.12 + cuda 11.6.
  8. 2. Install visual cpp build tool.
  9. 3. Include cuda toolkit.
  10. 4. Launch cmd console with Administrator privilege for creating required symlink folders.
  11. Create a new wheel via the following command:
  12. build_win.bat
  13. The wheel will be located at: dist/*.whl
  14. """
  15. import pathlib
  16. import os
  17. import shutil
  18. import sys
  19. import subprocess
  20. from setuptools import setup, find_packages
  21. from setuptools.command import egg_info
  22. import time
  23. import typing
  24. import shlex
  25. torch_available = True
  26. try:
  27. import torch
  28. except ImportError:
  29. torch_available = False
  30. print('[WARNING] Unable to import torch, pre-compiling ops will be disabled. ' \
  31. 'Please visit https://pytorch.org/ to see how to properly install torch on your system.')
  32. from op_builder import get_default_compute_capabilities, OpBuilder
  33. from op_builder.all_ops import ALL_OPS, accelerator_name
  34. from op_builder.builder import installed_cuda_version
  35. from accelerator import get_accelerator
  36. # Fetch rocm state.
  37. is_rocm_pytorch = OpBuilder.is_rocm_pytorch()
  38. rocm_version = OpBuilder.installed_rocm_version()
  39. RED_START = '\033[31m'
  40. RED_END = '\033[0m'
  41. ERROR = f"{RED_START} [ERROR] {RED_END}"
  42. def abort(msg):
  43. print(f"{ERROR} {msg}")
  44. assert False, msg
  45. def fetch_requirements(path):
  46. with open(path, 'r') as fd:
  47. return [r.strip() for r in fd.readlines()]
  48. def is_env_set(key):
  49. """
  50. Checks if an environment variable is set and not "".
  51. """
  52. return bool(os.environ.get(key, None))
  53. def get_env_if_set(key, default: typing.Any = ""):
  54. """
  55. Returns an environment variable if it is set and not "",
  56. otherwise returns a default value. In contrast, the fallback
  57. parameter of os.environ.get() is skipped if the variable is set to "".
  58. """
  59. return os.environ.get(key, None) or default
  60. install_requires = fetch_requirements('requirements/requirements.txt')
  61. extras_require = {
  62. '1bit': [], # add cupy based on cuda/rocm version
  63. '1bit_mpi': fetch_requirements('requirements/requirements-1bit-mpi.txt'),
  64. 'readthedocs': fetch_requirements('requirements/requirements-readthedocs.txt'),
  65. 'dev': fetch_requirements('requirements/requirements-dev.txt'),
  66. 'autotuning': fetch_requirements('requirements/requirements-autotuning.txt'),
  67. 'autotuning_ml': fetch_requirements('requirements/requirements-autotuning-ml.txt'),
  68. 'sparse_attn': fetch_requirements('requirements/requirements-sparse_attn.txt'),
  69. 'sparse': fetch_requirements('requirements/requirements-sparse_pruning.txt'),
  70. 'inf': fetch_requirements('requirements/requirements-inf.txt'),
  71. 'sd': fetch_requirements('requirements/requirements-sd.txt'),
  72. 'triton': fetch_requirements('requirements/requirements-triton.txt'),
  73. }
  74. # Only install pynvml on nvidia gpus.
  75. if torch_available and get_accelerator().device_name() == 'cuda' and not is_rocm_pytorch:
  76. install_requires.append('nvidia-ml-py')
  77. # Add specific cupy version to both onebit extension variants.
  78. if torch_available and get_accelerator().device_name() == 'cuda':
  79. cupy = None
  80. if is_rocm_pytorch:
  81. rocm_major, rocm_minor = rocm_version
  82. # XXX cupy support for rocm 5 is not available yet.
  83. if rocm_major <= 4:
  84. cupy = f"cupy-rocm-{rocm_major}-{rocm_minor}"
  85. else:
  86. cuda_major_ver, cuda_minor_ver = installed_cuda_version()
  87. if (cuda_major_ver < 11) or ((cuda_major_ver == 11) and (cuda_minor_ver < 3)):
  88. cupy = f"cupy-cuda{cuda_major_ver}{cuda_minor_ver}"
  89. else:
  90. cupy = f"cupy-cuda{cuda_major_ver}x"
  91. if cupy:
  92. extras_require['1bit'].append(cupy)
  93. extras_require['1bit_mpi'].append(cupy)
  94. # Make an [all] extra that installs all needed dependencies.
  95. all_extras = set()
  96. for extra in extras_require.items():
  97. for req in extra[1]:
  98. all_extras.add(req)
  99. extras_require['all'] = list(all_extras)
  100. cmdclass = {}
  101. # For any pre-installed ops force disable ninja.
  102. if torch_available:
  103. use_ninja = is_env_set("DS_ENABLE_NINJA")
  104. cmdclass['build_ext'] = get_accelerator().build_extension().with_options(use_ninja=use_ninja)
  105. if torch_available:
  106. TORCH_MAJOR = torch.__version__.split('.')[0]
  107. TORCH_MINOR = torch.__version__.split('.')[1]
  108. else:
  109. TORCH_MAJOR = "0"
  110. TORCH_MINOR = "0"
  111. if torch_available and not get_accelerator().device_name() == 'cuda':
  112. # Fix to allow docker builds, similar to https://github.com/NVIDIA/apex/issues/486.
  113. print("[WARNING] Torch did not find cuda available, if cross-compiling or running with cpu only "
  114. "you can ignore this message. Adding compute capability for Pascal, Volta, and Turing "
  115. "(compute capabilities 6.0, 6.1, 6.2)")
  116. if not is_env_set("TORCH_CUDA_ARCH_LIST"):
  117. os.environ["TORCH_CUDA_ARCH_LIST"] = get_default_compute_capabilities()
  118. ext_modules = []
  119. # Default to pre-install kernels to false so we rely on JIT on Linux, opposite on Windows.
  120. BUILD_OP_PLATFORM = 1 if sys.platform == "win32" else 0
  121. BUILD_OP_DEFAULT = int(get_env_if_set('DS_BUILD_OPS', BUILD_OP_PLATFORM))
  122. print(f"DS_BUILD_OPS={BUILD_OP_DEFAULT}")
  123. if BUILD_OP_DEFAULT:
  124. assert torch_available, "Unable to pre-compile ops without torch installed. Please install torch before attempting to pre-compile ops."
  125. def command_exists(cmd):
  126. if sys.platform == "win32":
  127. safe_cmd = shlex.split(f'{cmd}')
  128. result = subprocess.Popen(safe_cmd, stdout=subprocess.PIPE)
  129. return result.wait() == 1
  130. else:
  131. safe_cmd = shlex.split(f"bash -c type {cmd}")
  132. result = subprocess.Popen(safe_cmd, stdout=subprocess.PIPE)
  133. return result.wait() == 0
  134. def op_envvar(op_name):
  135. assert hasattr(ALL_OPS[op_name], 'BUILD_VAR'), \
  136. f"{op_name} is missing BUILD_VAR field"
  137. return ALL_OPS[op_name].BUILD_VAR
  138. def op_enabled(op_name):
  139. env_var = op_envvar(op_name)
  140. return int(get_env_if_set(env_var, BUILD_OP_DEFAULT))
  141. install_ops = dict.fromkeys(ALL_OPS.keys(), False)
  142. for op_name, builder in ALL_OPS.items():
  143. op_compatible = builder.is_compatible()
  144. # If op is requested but not available, throw an error.
  145. if op_enabled(op_name) and not op_compatible:
  146. env_var = op_envvar(op_name)
  147. if not is_env_set(env_var):
  148. builder.warning(f"Skip pre-compile of incompatible {op_name}; One can disable {op_name} with {env_var}=0")
  149. continue
  150. # If op is compatible but install is not enabled (JIT mode).
  151. if is_rocm_pytorch and op_compatible and not op_enabled(op_name):
  152. builder.hipify_extension()
  153. # If op install enabled, add builder to extensions.
  154. if op_enabled(op_name) and op_compatible:
  155. assert torch_available, f"Unable to pre-compile {op_name}, please first install torch"
  156. install_ops[op_name] = op_enabled(op_name)
  157. ext_modules.append(builder.builder())
  158. print(f'Install Ops={install_ops}')
  159. # Write out version/git info.
  160. git_hash_cmd = shlex.split("bash -c git rev-parse --short HEAD")
  161. git_branch_cmd = shlex.split("bash -c git rev-parse --abbrev-ref HEAD")
  162. if command_exists('git') and not is_env_set('DS_BUILD_STRING'):
  163. try:
  164. result = subprocess.check_output(git_hash_cmd)
  165. git_hash = result.decode('utf-8').strip()
  166. result = subprocess.check_output(git_branch_cmd)
  167. git_branch = result.decode('utf-8').strip()
  168. except subprocess.CalledProcessError:
  169. git_hash = "unknown"
  170. git_branch = "unknown"
  171. else:
  172. git_hash = "unknown"
  173. git_branch = "unknown"
  174. if sys.platform == "win32":
  175. shutil.rmtree('.\\deepspeed\\ops\\csrc', ignore_errors=True)
  176. pathlib.Path('.\\deepspeed\\ops\\csrc').unlink(missing_ok=True)
  177. shutil.copytree('.\\csrc', '.\\deepspeed\\ops\\csrc', dirs_exist_ok=True)
  178. shutil.rmtree('.\\deepspeed\\ops\\op_builder', ignore_errors=True)
  179. pathlib.Path('.\\deepspeed\\ops\\op_builder').unlink(missing_ok=True)
  180. shutil.copytree('.\\op_builder', '.\\deepspeed\\ops\\op_builder', dirs_exist_ok=True)
  181. shutil.rmtree('.\\deepspeed\\accelerator', ignore_errors=True)
  182. pathlib.Path('.\\deepspeed\\accelerator').unlink(missing_ok=True)
  183. shutil.copytree('.\\accelerator', '.\\deepspeed\\accelerator', dirs_exist_ok=True)
  184. egg_info.manifest_maker.template = 'MANIFEST_win.in'
  185. # Parse the DeepSpeed version string from version.txt.
  186. version_str = open('version.txt', 'r').read().strip()
  187. # Build specifiers like .devX can be added at install time. Otherwise, add the git hash.
  188. # Example: DS_BUILD_STRING=".dev20201022" python setup.py sdist bdist_wheel.
  189. # Building wheel for distribution, update version file.
  190. if is_env_set('DS_BUILD_STRING'):
  191. # Build string env specified, probably building for distribution.
  192. with open('build.txt', 'w') as fd:
  193. fd.write(os.environ['DS_BUILD_STRING'])
  194. version_str += os.environ['DS_BUILD_STRING']
  195. elif os.path.isfile('build.txt'):
  196. # build.txt exists, probably installing from distribution.
  197. with open('build.txt', 'r') as fd:
  198. version_str += fd.read().strip()
  199. else:
  200. # None of the above, probably installing from source.
  201. version_str += f'+{git_hash}'
  202. torch_version = ".".join([TORCH_MAJOR, TORCH_MINOR])
  203. bf16_support = False
  204. # Set cuda_version to 0.0 if cpu-only.
  205. cuda_version = "0.0"
  206. nccl_version = "0.0"
  207. # Set hip_version to 0.0 if cpu-only.
  208. hip_version = "0.0"
  209. if torch_available and torch.version.cuda is not None:
  210. cuda_version = ".".join(torch.version.cuda.split('.')[:2])
  211. if sys.platform != "win32":
  212. if isinstance(torch.cuda.nccl.version(), int):
  213. # This will break if minor version > 9.
  214. nccl_version = ".".join(str(torch.cuda.nccl.version())[:2])
  215. else:
  216. nccl_version = ".".join(map(str, torch.cuda.nccl.version()[:2]))
  217. if hasattr(torch.cuda, 'is_bf16_supported') and torch.cuda.is_available():
  218. bf16_support = torch.cuda.is_bf16_supported()
  219. if torch_available and hasattr(torch.version, 'hip') and torch.version.hip is not None:
  220. hip_version = ".".join(torch.version.hip.split('.')[:2])
  221. torch_info = {
  222. "version": torch_version,
  223. "bf16_support": bf16_support,
  224. "cuda_version": cuda_version,
  225. "nccl_version": nccl_version,
  226. "hip_version": hip_version
  227. }
  228. print(f"version={version_str}, git_hash={git_hash}, git_branch={git_branch}")
  229. with open('deepspeed/git_version_info_installed.py', 'w') as fd:
  230. fd.write(f"version='{version_str}'\n")
  231. fd.write(f"git_hash='{git_hash}'\n")
  232. fd.write(f"git_branch='{git_branch}'\n")
  233. fd.write(f"installed_ops={install_ops}\n")
  234. fd.write(f"accelerator_name='{accelerator_name}'\n")
  235. fd.write(f"torch_info={torch_info}\n")
  236. print(f'install_requires={install_requires}')
  237. print(f'ext_modules={ext_modules}')
  238. # Parse README.md to make long_description for PyPI page.
  239. thisdir = os.path.abspath(os.path.dirname(__file__))
  240. with open(os.path.join(thisdir, 'README.md'), encoding='utf-8') as fin:
  241. readme_text = fin.read()
  242. if sys.platform == "win32":
  243. scripts = ['bin/deepspeed.bat', 'bin/ds', 'bin/ds_report.bat', 'bin/ds_report']
  244. else:
  245. scripts = [
  246. 'bin/deepspeed', 'bin/deepspeed.pt', 'bin/ds', 'bin/ds_ssh', 'bin/ds_report', 'bin/ds_bench', 'bin/dsr',
  247. 'bin/ds_elastic', 'bin/ds_nvme_tune', 'bin/ds_io'
  248. ]
  249. start_time = time.time()
  250. setup(name='deepspeed',
  251. version=version_str,
  252. description='DeepSpeed library',
  253. long_description=readme_text,
  254. long_description_content_type='text/markdown',
  255. author='DeepSpeed Team',
  256. author_email='deepspeed-info@microsoft.com',
  257. url='http://deepspeed.ai',
  258. project_urls={
  259. 'Documentation': 'https://deepspeed.readthedocs.io',
  260. 'Source': 'https://github.com/microsoft/DeepSpeed',
  261. },
  262. install_requires=install_requires,
  263. extras_require=extras_require,
  264. packages=find_packages(include=['deepspeed', 'deepspeed.*']),
  265. include_package_data=True,
  266. scripts=scripts,
  267. classifiers=[
  268. 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7',
  269. 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9',
  270. 'Programming Language :: Python :: 3.10'
  271. ],
  272. license='Apache Software License 2.0',
  273. ext_modules=ext_modules,
  274. cmdclass=cmdclass)
  275. end_time = time.time()
  276. print(f'deepspeed build time = {end_time - start_time} secs')