setup.py 10.5 KB
Newer Older
Jeff Rasley's avatar
Jeff Rasley committed
1
2
3
4
5
"""
Copyright 2020 The Microsoft DeepSpeed Team

DeepSpeed library

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

Create a new wheel via the following command:
    python setup.py bdist_wheel
Jeff Rasley's avatar
Jeff Rasley committed
13
14
15
16

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

17
import os
aiss's avatar
aiss committed
18
import sys
19
import shutil
20
21
import subprocess
import warnings
Jeff Rasley's avatar
Jeff Rasley committed
22
from setuptools import setup, find_packages
aiss's avatar
aiss committed
23
from setuptools.command import egg_info
Samyam Rajbhandari's avatar
Samyam Rajbhandari committed
24
import time
25

aiss's avatar
aiss committed
26
torch_available = True
27
28
29
30
try:
    import torch
    from torch.utils.cpp_extension import BuildExtension
except ImportError:
aiss's avatar
aiss committed
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
    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.')

from op_builder import ALL_OPS, get_default_compute_capabilities, OpBuilder

# fetch rocm state
is_rocm_pytorch = OpBuilder.is_rocm_pytorch()
rocm_version = OpBuilder.installed_rocm_version()
#aiss
print("is_rocm_pytorch: ", is_rocm_pytorch)
print("rocm_version: ", 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
62
    '1bit': [], # add cupy based on cuda/rocm version
    '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
67
    'autotuning': fetch_requirements('requirements/requirements-autotuning.txt'),
    'autotuning_ml': fetch_requirements('requirements/requirements-autotuning-ml.txt'),
    'sparse_attn': fetch_requirements('requirements/requirements-sparse_attn.txt')
68
}
69

aiss's avatar
aiss committed
70
71
72
73
74
75
76
77
78
# Add specific cupy version to both onebit extension variants
if torch_available and torch.cuda.is_available():
    cupy = None
    if is_rocm_pytorch:
        rocm_major, rocm_minor = rocm_version
        # XXX cupy support for rocm 5 is not available yet
        if rocm_major <= 4:
            cupy = f"cupy-rocm-{rocm_major}-{rocm_minor}"
    else:
79
        cupy = f"cupy-cuda{torch.version.cuda.replace('.','')[:3]}"
aiss's avatar
aiss committed
80
81
82
    if cupy:
        extras_require['1bit'].append(cupy)
        extras_require['1bit_mpi'].append(cupy)
83
84
85
86
87
88
89

# Make an [all] extra that installs all needed dependencies
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
90
91

cmdclass = {}
92
93

# For any pre-installed ops force disable ninja
aiss's avatar
aiss committed
94
95
if torch_available:
    cmdclass['build_ext'] = BuildExtension.with_options(use_ninja=False)
Jeff Rasley's avatar
Jeff Rasley committed
96

aiss's avatar
aiss committed
97
98
99
100
101
102
if torch_available:
    TORCH_MAJOR = torch.__version__.split('.')[0]
    TORCH_MINOR = torch.__version__.split('.')[1]
else:
    TORCH_MAJOR = "0"
    TORCH_MINOR = "0"
103

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

113
114
ext_modules = []

aiss's avatar
aiss committed
115
116
117
# 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))
118
print(f"DS_BUILD_OPS={BUILD_OP_DEFAULT}")
119

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

123
124

def command_exists(cmd):
aiss's avatar
aiss committed
125
126
127
128
129
130
    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
131
132


aiss's avatar
aiss committed
133
def op_envvar(op_name):
134
135
    assert hasattr(ALL_OPS[op_name], 'BUILD_VAR'), \
        f"{op_name} is missing BUILD_VAR field"
aiss's avatar
aiss committed
136
137
138
139
140
    return ALL_OPS[op_name].BUILD_VAR

sparse_env='DS_BUILD_SPARSE_ATTN'
def op_enabled(op_name):
    env_var = op_envvar(op_name)
141
142
143
    return int(os.environ.get(env_var, BUILD_OP_DEFAULT))


aiss's avatar
aiss committed
144
compatible_ops = dict.fromkeys(ALL_OPS.keys(), False)
145
146
147
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
148
149
150
151
152
153
154
155
    compatible_ops[op_name] = op_compatible
    # If op is requested but not available, throw an error
    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}")
    print(f"op_name: {op_name}")
156
157
158
159
    # If op is compatible update install reqs so it can potentially build/run later
    if op_compatible:
        reqs = builder.python_requirements()
        install_requires += builder.python_requirements()
aiss's avatar
aiss committed
160
161
162
163
       
    # if op is compatible but install is not enabled (JIT mode)
    if is_rocm_pytorch and op_compatible and not op_enabled(op_name):
        builder.hipify_extension()
164
165
    # If op install enabled, add builder to extensions
    if op_enabled(op_name) and op_compatible:
aiss's avatar
aiss committed
166
        assert torch_available, f"Unable to pre-compile {op_name}, please first install torch"
167
168
169
170
        install_ops[op_name] = op_enabled(op_name)
        ext_modules.append(builder.builder())

print(f'Install Ops={install_ops}')
171
172
173
174

# Write out version/git info
git_hash_cmd = "git rev-parse --short HEAD"
git_branch_cmd = "git rev-parse --abbrev-ref HEAD"
175
176
177
178
179
180
181
182
183
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"
184
185
186
else:
    git_hash = "unknown"
    git_branch = "unknown"
187

aiss's avatar
aiss committed
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203

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')
    egg_info.manifest_maker.template = 'MANIFEST_win.in'

204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
# Parse the DeepSpeed version string from version.txt
version_str = open('version.txt', 'r').read().strip()

# Build specifiers like .devX can be added at install time. Otherwise, add the git hash.
# example: DS_BUILD_STR=".dev20201022" python setup.py sdist bdist_wheel

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

torch_version = ".".join([TORCH_MAJOR, TORCH_MINOR])
226
227
# Set cuda_version to 0.0 if cpu-only
cuda_version = "0.0"
aiss's avatar
aiss committed
228
229
230
# Set hip_version to 0.0 if cpu-only
hip_version = "0.0"
if torch_available and torch.version.cuda is not None:
231
    cuda_version = ".".join(torch.version.cuda.split('.')[:2])
aiss's avatar
aiss committed
232
233
234
235
236
237
238
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,
    "cuda_version": cuda_version,
    "hip_version": hip_version
}
239
240

print(f"version={version_str}, git_hash={git_hash}, git_branch={git_branch}")
241
with open('deepspeed/git_version_info_installed.py', 'w') as fd:
242
    fd.write(f"version='{version_str}'\n")
243
244
    fd.write(f"git_hash='{git_hash}'\n")
    fd.write(f"git_branch='{git_branch}'\n")
Jeff Rasley's avatar
Jeff Rasley committed
245
    fd.write(f"installed_ops={install_ops}\n")
246
247
    fd.write(f"compatible_ops={compatible_ops}\n")
    fd.write(f"torch_info={torch_info}\n")
248
249

print(f'install_requires={install_requires}')
250
251
print(f'compatible_ops={compatible_ops}')
print(f'ext_modules={ext_modules}')
Jeff Rasley's avatar
Jeff Rasley committed
252

253
254
255
256
257
# 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
258
259
start_time = time.time()

Jeff Rasley's avatar
Jeff Rasley committed
260
setup(name='deepspeed',
261
      version=version_str,
Jeff Rasley's avatar
Jeff Rasley committed
262
      description='DeepSpeed library',
263
264
      long_description=readme_text,
      long_description_content_type='text/markdown',
Jeff Rasley's avatar
Jeff Rasley committed
265
266
      author='DeepSpeed Team',
      author_email='deepspeed@microsoft.com',
267
      url='http://deepspeed.ai',
aiss's avatar
aiss committed
268
269
270
271
      project_urls={
          'Documentation': 'https://deepspeed.readthedocs.io',
          'Source': 'https://github.com/microsoft/DeepSpeed',
      },
272
      install_requires=install_requires,
273
      extras_require=extras_require,
Jeff Rasley's avatar
Jeff Rasley committed
274
      packages=find_packages(exclude=["docker",
aiss's avatar
aiss committed
275
276
277
                                      "third_party",
                                      "csrc",
                                      "op_builder"]),
278
279
280
281
282
283
      include_package_data=True,
      scripts=[
          'bin/deepspeed',
          'bin/deepspeed.pt',
          'bin/ds',
          'bin/ds_ssh',
Jeff Rasley's avatar
Jeff Rasley committed
284
285
          'bin/ds_report',
          'bin/ds_elastic'
286
      ],
287
288
289
      classifiers=[
          'Programming Language :: Python :: 3.6',
          'Programming Language :: Python :: 3.7',
aiss's avatar
aiss committed
290
291
          'Programming Language :: Python :: 3.8',
          'Programming Language :: Python :: 3.9'
292
293
      ],
      license='MIT',
Jeff Rasley's avatar
Jeff Rasley committed
294
295
      ext_modules=ext_modules,
      cmdclass=cmdclass)
Samyam Rajbhandari's avatar
Samyam Rajbhandari committed
296
297
298

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