setup.py 11.3 KB
Newer Older
aiss's avatar
aiss committed
1
2
# Copyright (c) Microsoft Corporation.
# SPDX-License-Identifier: Apache-2.0
Jeff Rasley's avatar
Jeff Rasley committed
3

aiss's avatar
aiss committed
4
5
# DeepSpeed Team
"""
Jeff Rasley's avatar
Jeff Rasley committed
6
7
DeepSpeed library

aiss's avatar
aiss committed
8
To build wheel on Windows:
aiss's avatar
aiss committed
9
10
11
12
13
1. Install pytorch, such as pytorch 1.12 + cuda 11.6.
2. Install visual cpp build tool.
3. Include cuda toolkit.
4. Launch cmd console with Administrator privilege for creating required symlink folders.

aiss's avatar
aiss committed
14
15

Create a new wheel via the following command:
aiss's avatar
aiss committed
16
build_win.bat
Jeff Rasley's avatar
Jeff Rasley committed
17
18
19
20

The wheel will be located at: dist/*.whl
"""

21
import os
aiss's avatar
aiss committed
22
import sys
23
import subprocess
aiss's avatar
aiss committed
24
from setuptools import setup, find_packages, find_namespace_packages
aiss's avatar
aiss committed
25
from setuptools.command import egg_info
Samyam Rajbhandari's avatar
Samyam Rajbhandari committed
26
import time
27

aiss's avatar
aiss committed
28
torch_available = True
29
30
31
try:
    import torch
except ImportError:
aiss's avatar
aiss committed
32
33
34
35
    torch_available = False
    print('[WARNING] Unable to import torch, pre-compiling ops will be disabled. ' \
        'Please visit https://pytorch.org/ to see how to properly install torch on your system.')

aiss's avatar
aiss committed
36
37
38
from op_builder import get_default_compute_capabilities, OpBuilder
from op_builder.all_ops import ALL_OPS
from op_builder.builder import installed_cuda_version
aiss's avatar
aiss committed
39

aiss's avatar
aiss committed
40
# Fetch rocm state.
aiss's avatar
aiss committed
41
42
43
44
45
46
47
is_rocm_pytorch = OpBuilder.is_rocm_pytorch()
rocm_version = OpBuilder.installed_rocm_version()

RED_START = '\033[31m'
RED_END = '\033[0m'
ERROR = f"{RED_START} [ERROR] {RED_END}"

48

aiss's avatar
aiss committed
49
50
51
def abort(msg):
    print(f"{ERROR} {msg}")
    assert False, msg
52
53
54
55
56
57
58
59


def fetch_requirements(path):
    with open(path, 'r') as fd:
        return [r.strip() for r in fd.readlines()]


install_requires = fetch_requirements('requirements/requirements.txt')
60
extras_require = {
aiss's avatar
aiss committed
61
    '1bit': [],  # add cupy based on cuda/rocm version
aiss's avatar
aiss committed
62
    '1bit_mpi': fetch_requirements('requirements/requirements-1bit-mpi.txt'),
63
64
    'readthedocs': fetch_requirements('requirements/requirements-readthedocs.txt'),
    'dev': fetch_requirements('requirements/requirements-dev.txt'),
aiss's avatar
aiss committed
65
66
    'autotuning': fetch_requirements('requirements/requirements-autotuning.txt'),
    'autotuning_ml': fetch_requirements('requirements/requirements-autotuning-ml.txt'),
aiss's avatar
aiss committed
67
68
69
    'sparse_attn': fetch_requirements('requirements/requirements-sparse_attn.txt'),
    'inf': fetch_requirements('requirements/requirements-inf.txt'),
    'sd': fetch_requirements('requirements/requirements-sd.txt')
70
}
71

aiss's avatar
aiss committed
72
# Add specific cupy version to both onebit extension variants.
aiss's avatar
aiss committed
73
74
75
76
if torch_available and torch.cuda.is_available():
    cupy = None
    if is_rocm_pytorch:
        rocm_major, rocm_minor = rocm_version
aiss's avatar
aiss committed
77
        # XXX cupy support for rocm 5 is not available yet.
aiss's avatar
aiss committed
78
79
80
        if rocm_major <= 4:
            cupy = f"cupy-rocm-{rocm_major}-{rocm_minor}"
    else:
aiss's avatar
aiss committed
81
82
83
84
85
86
        cuda_major_ver, cuda_minor_ver = installed_cuda_version()
        if (cuda_major_ver < 11) or ((cuda_major_ver == 11) and (cuda_minor_ver < 3)):
            cupy = f"cupy-cuda{cuda_major_ver}{cuda_minor_ver}"
        else:
            cupy = f"cupy-cuda{cuda_major_ver}x"

aiss's avatar
aiss committed
87
88
89
    if cupy:
        extras_require['1bit'].append(cupy)
        extras_require['1bit_mpi'].append(cupy)
90

aiss's avatar
aiss committed
91
# Make an [all] extra that installs all needed dependencies.
92
93
94
95
96
all_extras = set()
for extra in extras_require.items():
    for req in extra[1]:
        all_extras.add(req)
extras_require['all'] = list(all_extras)
Jeff Rasley's avatar
Jeff Rasley committed
97
98

cmdclass = {}
99

aiss's avatar
aiss committed
100
# For any pre-installed ops force disable ninja.
aiss's avatar
aiss committed
101
if torch_available:
aiss's avatar
aiss committed
102
    from accelerator import get_accelerator
aiss's avatar
aiss committed
103
    cmdclass['build_ext'] = get_accelerator().build_extension().with_options(use_ninja=False)
Jeff Rasley's avatar
Jeff Rasley committed
104

aiss's avatar
aiss committed
105
106
107
108
109
110
if torch_available:
    TORCH_MAJOR = torch.__version__.split('.')[0]
    TORCH_MINOR = torch.__version__.split('.')[1]
else:
    TORCH_MAJOR = "0"
    TORCH_MINOR = "0"
111

aiss's avatar
aiss committed
112
if torch_available and not torch.cuda.is_available():
aiss's avatar
aiss committed
113
114
115
116
    # Fix to allow docker builds, similar to https://github.com/NVIDIA/apex/issues/486.
    print("[WARNING] Torch did not find cuda available, if cross-compiling or running with cpu only "
          "you can ignore this message. Adding compute capability for Pascal, Volta, and Turing "
          "(compute capabilities 6.0, 6.1, 6.2)")
117
    if os.environ.get("TORCH_CUDA_ARCH_LIST", None) is None:
aiss's avatar
aiss committed
118
        os.environ["TORCH_CUDA_ARCH_LIST"] = get_default_compute_capabilities()
119

120
121
ext_modules = []

aiss's avatar
aiss committed
122
123
124
# Default to pre-install kernels to false so we rely on JIT on Linux, opposite on Windows.
BUILD_OP_PLATFORM = 1 if sys.platform == "win32" else 0
BUILD_OP_DEFAULT = int(os.environ.get('DS_BUILD_OPS', BUILD_OP_PLATFORM))
125
print(f"DS_BUILD_OPS={BUILD_OP_DEFAULT}")
126

aiss's avatar
aiss committed
127
128
129
if BUILD_OP_DEFAULT:
    assert torch_available, "Unable to pre-compile ops without torch installed. Please install torch before attempting to pre-compile ops."

130
131

def command_exists(cmd):
aiss's avatar
aiss committed
132
133
134
135
136
137
    if sys.platform == "win32":
        result = subprocess.Popen(f'{cmd}', stdout=subprocess.PIPE, shell=True)
        return result.wait() == 1
    else:
        result = subprocess.Popen(f'type {cmd}', stdout=subprocess.PIPE, shell=True)
        return result.wait() == 0
138
139


aiss's avatar
aiss committed
140
def op_envvar(op_name):
141
142
    assert hasattr(ALL_OPS[op_name], 'BUILD_VAR'), \
        f"{op_name} is missing BUILD_VAR field"
aiss's avatar
aiss committed
143
144
    return ALL_OPS[op_name].BUILD_VAR

aiss's avatar
aiss committed
145

aiss's avatar
aiss committed
146
147
def op_enabled(op_name):
    env_var = op_envvar(op_name)
148
149
150
    return int(os.environ.get(env_var, BUILD_OP_DEFAULT))


aiss's avatar
aiss committed
151
compatible_ops = dict.fromkeys(ALL_OPS.keys(), False)
152
153
154
install_ops = dict.fromkeys(ALL_OPS.keys(), False)
for op_name, builder in ALL_OPS.items():
    op_compatible = builder.is_compatible()
aiss's avatar
aiss committed
155
    compatible_ops[op_name] = op_compatible
aiss's avatar
aiss committed
156
157
158
    print("op_name: ", op_name)
    print("op_enabled: ", op_enabled(op_name))
    print("op_compatible: ", op_compatible)
aiss's avatar
aiss committed
159
160

    # If op is requested but not available, throw an error.
aiss's avatar
aiss committed
161
162
163
164
165
    if op_enabled(op_name) and not op_compatible:
        env_var = op_envvar(op_name)
        if env_var not in os.environ:
            builder.warning(f"One can disable {op_name} with {env_var}=0")
        abort(f"Unable to pre-compile {op_name}")
aiss's avatar
aiss committed
166

aiss's avatar
aiss committed
167
    # If op is compatible but install is not enabled (JIT mode).
aiss's avatar
aiss committed
168
169
    if is_rocm_pytorch and op_compatible and not op_enabled(op_name):
        builder.hipify_extension()
aiss's avatar
aiss committed
170

aiss's avatar
aiss committed
171
    # If op install enabled, add builder to extensions.
172
    if op_enabled(op_name) and op_compatible:
aiss's avatar
aiss committed
173
        assert torch_available, f"Unable to pre-compile {op_name}, please first install torch"
174
175
176
177
        install_ops[op_name] = op_enabled(op_name)
        ext_modules.append(builder.builder())

print(f'Install Ops={install_ops}')
178

aiss's avatar
aiss committed
179
# Write out version/git info.
180
181
git_hash_cmd = "git rev-parse --short HEAD"
git_branch_cmd = "git rev-parse --abbrev-ref HEAD"
182
183
184
185
186
187
188
189
190
if command_exists('git') and 'DS_BUILD_STRING' not in os.environ:
    try:
        result = subprocess.check_output(git_hash_cmd, shell=True)
        git_hash = result.decode('utf-8').strip()
        result = subprocess.check_output(git_branch_cmd, shell=True)
        git_branch = result.decode('utf-8').strip()
    except subprocess.CalledProcessError:
        git_hash = "unknown"
        git_branch = "unknown"
191
192
193
else:
    git_hash = "unknown"
    git_branch = "unknown"
194

aiss's avatar
aiss committed
195
196
197
198
199
200
201
202
203
204
205
206
207
208

def create_dir_symlink(src, dest):
    if not os.path.islink(dest):
        if os.path.exists(dest):
            os.remove(dest)
        assert not os.path.exists(dest)
        os.symlink(src, dest)


if sys.platform == "win32":
    # This creates a symbolic links on Windows.
    # It needs Administrator privilege to create symlinks on Windows.
    create_dir_symlink('..\\..\\csrc', '.\\deepspeed\\ops\\csrc')
    create_dir_symlink('..\\..\\op_builder', '.\\deepspeed\\ops\\op_builder')
aiss's avatar
aiss committed
209
    create_dir_symlink('..\\accelerator', '.\\deepspeed\\accelerator')
aiss's avatar
aiss committed
210
211
    egg_info.manifest_maker.template = 'MANIFEST_win.in'

aiss's avatar
aiss committed
212
# Parse the DeepSpeed version string from version.txt.
213
214
215
version_str = open('version.txt', 'r').read().strip()

# Build specifiers like .devX can be added at install time. Otherwise, add the git hash.
aiss's avatar
aiss committed
216
# Example: DS_BUILD_STRING=".dev20201022" python setup.py sdist bdist_wheel.
217

aiss's avatar
aiss committed
218
# Building wheel for distribution, update version file.
219
if 'DS_BUILD_STRING' in os.environ:
aiss's avatar
aiss committed
220
    # Build string env specified, probably building for distribution.
221
222
223
224
    with open('build.txt', 'w') as fd:
        fd.write(os.environ.get('DS_BUILD_STRING'))
    version_str += os.environ.get('DS_BUILD_STRING')
elif os.path.isfile('build.txt'):
aiss's avatar
aiss committed
225
    # build.txt exists, probably installing from distribution.
226
227
228
    with open('build.txt', 'r') as fd:
        version_str += fd.read().strip()
else:
aiss's avatar
aiss committed
229
    # None of the above, probably installing from source.
230
231
232
    version_str += f'+{git_hash}'

torch_version = ".".join([TORCH_MAJOR, TORCH_MINOR])
aiss's avatar
aiss committed
233
bf16_support = False
aiss's avatar
aiss committed
234
# Set cuda_version to 0.0 if cpu-only.
235
cuda_version = "0.0"
aiss's avatar
aiss committed
236
nccl_version = "0.0"
aiss's avatar
aiss committed
237
# Set hip_version to 0.0 if cpu-only.
aiss's avatar
aiss committed
238
239
hip_version = "0.0"
if torch_available and torch.version.cuda is not None:
240
    cuda_version = ".".join(torch.version.cuda.split('.')[:2])
aiss's avatar
aiss committed
241
242
    if sys.platform != "win32":
        if isinstance(torch.cuda.nccl.version(), int):
aiss's avatar
aiss committed
243
            # This will break if minor version > 9.
aiss's avatar
aiss committed
244
245
246
247
248
            nccl_version = ".".join(str(torch.cuda.nccl.version())[:2])
        else:
            nccl_version = ".".join(map(str, torch.cuda.nccl.version()[:2]))
    if hasattr(torch.cuda, 'is_bf16_supported') and torch.cuda.is_available():
        bf16_support = torch.cuda.is_bf16_supported()
aiss's avatar
aiss committed
249
250
251
252
if torch_available and hasattr(torch.version, 'hip') and torch.version.hip is not None:
    hip_version = ".".join(torch.version.hip.split('.')[:2])
torch_info = {
    "version": torch_version,
aiss's avatar
aiss committed
253
    "bf16_support": bf16_support,
aiss's avatar
aiss committed
254
    "cuda_version": cuda_version,
aiss's avatar
aiss committed
255
    "nccl_version": nccl_version,
aiss's avatar
aiss committed
256
257
    "hip_version": hip_version
}
258
259

print(f"version={version_str}, git_hash={git_hash}, git_branch={git_branch}")
260
with open('deepspeed/git_version_info_installed.py', 'w') as fd:
261
    fd.write(f"version='{version_str}'\n")
262
263
    fd.write(f"git_hash='{git_hash}'\n")
    fd.write(f"git_branch='{git_branch}'\n")
Jeff Rasley's avatar
Jeff Rasley committed
264
    fd.write(f"installed_ops={install_ops}\n")
265
266
    fd.write(f"compatible_ops={compatible_ops}\n")
    fd.write(f"torch_info={torch_info}\n")
267
268

print(f'install_requires={install_requires}')
269
270
print(f'compatible_ops={compatible_ops}')
print(f'ext_modules={ext_modules}')
Jeff Rasley's avatar
Jeff Rasley committed
271

272
273
274
275
276
# Parse README.md to make long_description for PyPI page.
thisdir = os.path.abspath(os.path.dirname(__file__))
with open(os.path.join(thisdir, 'README.md'), encoding='utf-8') as fin:
    readme_text = fin.read()

Samyam Rajbhandari's avatar
Samyam Rajbhandari committed
277
278
start_time = time.time()

Jeff Rasley's avatar
Jeff Rasley committed
279
setup(name='deepspeed',
280
      version=version_str,
Jeff Rasley's avatar
Jeff Rasley committed
281
      description='DeepSpeed library',
282
283
      long_description=readme_text,
      long_description_content_type='text/markdown',
Jeff Rasley's avatar
Jeff Rasley committed
284
      author='DeepSpeed Team',
aiss's avatar
aiss committed
285
      author_email='deepspeed-info@microsoft.com',
286
      url='http://deepspeed.ai',
aiss's avatar
aiss committed
287
288
289
290
      project_urls={
          'Documentation': 'https://deepspeed.readthedocs.io',
          'Source': 'https://github.com/microsoft/DeepSpeed',
      },
291
      install_requires=install_requires,
292
      extras_require=extras_require,
aiss's avatar
aiss committed
293
      #packages=find_packages(include=['deepspeed', 'deepspeed.*']),
aiss's avatar
aiss committed
294
295
      packages=find_namespace_packages(include=['deepspeed',
                                      'deepspeed.*']),
296
297
      include_package_data=True,
      scripts=[
aiss's avatar
aiss committed
298
          'bin/deepspeed', 'bin/deepspeed.pt', 'bin/ds', 'bin/ds_ssh', 'bin/ds_report', 'bin/ds_bench', 'bin/dsr',
Jeff Rasley's avatar
Jeff Rasley committed
299
          'bin/ds_elastic'
300
      ],
301
      classifiers=[
aiss's avatar
aiss committed
302
303
          'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7',
          'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9',
aiss's avatar
aiss committed
304
          'Programming Language :: Python :: 3.10'
305
306
      ],
      license='MIT',
Jeff Rasley's avatar
Jeff Rasley committed
307
308
      ext_modules=ext_modules,
      cmdclass=cmdclass)
Samyam Rajbhandari's avatar
Samyam Rajbhandari committed
309
310
311

end_time = time.time()
print(f'deepspeed build time = {end_time - start_time} secs')