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
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
181
        git_hash +="dtk22.04"
182
183
184
    except subprocess.CalledProcessError:
        git_hash = "unknown"
        git_branch = "unknown"
185
186
187
else:
    git_hash = "unknown"
    git_branch = "unknown"
188

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

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'

205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
# 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
224
    print("version_str: ",version_str)
225
226

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

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

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

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

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

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