setup.py 11.5 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
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()
aiss's avatar
aiss committed
188
189
190
191
192
        #add dtk version
        if os.getenv('DS_BUILD_VERSION'):
            version_dtk = os.getenv('DS_BUILD_VERSION', "")
            git_hash += "." + version_dtk

193
194
195
    except subprocess.CalledProcessError:
        git_hash = "unknown"
        git_branch = "unknown"
196
197
198
else:
    git_hash = "unknown"
    git_branch = "unknown"
199

aiss's avatar
aiss committed
200
201
202
203
204
205
206
207
208
209
210
211
212
213

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
214
    create_dir_symlink('..\\accelerator', '.\\deepspeed\\accelerator')
aiss's avatar
aiss committed
215
216
    egg_info.manifest_maker.template = 'MANIFEST_win.in'

aiss's avatar
aiss committed
217
# Parse the DeepSpeed version string from version.txt.
218
219
220
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
221
# Example: DS_BUILD_STRING=".dev20201022" python setup.py sdist bdist_wheel.
222

aiss's avatar
aiss committed
223
# Building wheel for distribution, update version file.
224
if 'DS_BUILD_STRING' in os.environ:
aiss's avatar
aiss committed
225
    # Build string env specified, probably building for distribution.
226
227
228
229
    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
230
    # build.txt exists, probably installing from distribution.
231
232
233
    with open('build.txt', 'r') as fd:
        version_str += fd.read().strip()
else:
aiss's avatar
aiss committed
234
    # None of the above, probably installing from source.
235
236
237
    version_str += f'+{git_hash}'

torch_version = ".".join([TORCH_MAJOR, TORCH_MINOR])
aiss's avatar
aiss committed
238
bf16_support = False
aiss's avatar
aiss committed
239
# Set cuda_version to 0.0 if cpu-only.
240
cuda_version = "0.0"
aiss's avatar
aiss committed
241
nccl_version = "0.0"
aiss's avatar
aiss committed
242
# Set hip_version to 0.0 if cpu-only.
aiss's avatar
aiss committed
243
244
hip_version = "0.0"
if torch_available and torch.version.cuda is not None:
245
    cuda_version = ".".join(torch.version.cuda.split('.')[:2])
aiss's avatar
aiss committed
246
247
    if sys.platform != "win32":
        if isinstance(torch.cuda.nccl.version(), int):
aiss's avatar
aiss committed
248
            # This will break if minor version > 9.
aiss's avatar
aiss committed
249
250
251
252
253
            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
254
255
256
257
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
258
    "bf16_support": bf16_support,
aiss's avatar
aiss committed
259
    "cuda_version": cuda_version,
aiss's avatar
aiss committed
260
    "nccl_version": nccl_version,
aiss's avatar
aiss committed
261
262
    "hip_version": hip_version
}
263
264

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

print(f'install_requires={install_requires}')
274
275
print(f'compatible_ops={compatible_ops}')
print(f'ext_modules={ext_modules}')
Jeff Rasley's avatar
Jeff Rasley committed
276

277
278
279
280
281
# 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
282
283
start_time = time.time()

Jeff Rasley's avatar
Jeff Rasley committed
284
setup(name='deepspeed',
285
      version=version_str,
Jeff Rasley's avatar
Jeff Rasley committed
286
      description='DeepSpeed library',
287
288
      long_description=readme_text,
      long_description_content_type='text/markdown',
Jeff Rasley's avatar
Jeff Rasley committed
289
      author='DeepSpeed Team',
aiss's avatar
aiss committed
290
      author_email='deepspeed-info@microsoft.com',
291
      url='http://deepspeed.ai',
aiss's avatar
aiss committed
292
293
294
295
      project_urls={
          'Documentation': 'https://deepspeed.readthedocs.io',
          'Source': 'https://github.com/microsoft/DeepSpeed',
      },
296
      install_requires=install_requires,
297
      extras_require=extras_require,
aiss's avatar
aiss committed
298
      #packages=find_packages(include=['deepspeed', 'deepspeed.*']),
aiss's avatar
aiss committed
299
300
      packages=find_namespace_packages(include=['deepspeed',
                                      'deepspeed.*']),
301
302
      include_package_data=True,
      scripts=[
aiss's avatar
aiss committed
303
          '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
304
          'bin/ds_elastic'
305
      ],
306
      classifiers=[
aiss's avatar
aiss committed
307
308
          '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
309
          'Programming Language :: Python :: 3.10'
310
311
      ],
      license='MIT',
Jeff Rasley's avatar
Jeff Rasley committed
312
313
      ext_modules=ext_modules,
      cmdclass=cmdclass)
Samyam Rajbhandari's avatar
Samyam Rajbhandari committed
314
315
316

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