setup.py 11.3 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
To build wheel on Windows:
aiss's avatar
aiss committed
7
    1. Install pytorch, such as pytorch 1.12 + cuda 11.6
aiss's avatar
aiss committed
8
    2. Install visual cpp build tool
aiss's avatar
aiss committed
9
10
    3. Include cuda toolkit
    4. Launch cmd console with Administrator privilege for creating required symlink folders
aiss's avatar
aiss committed
11
12

Create a new wheel via the following command:
aiss's avatar
aiss committed
13
    build_win.bat
Jeff Rasley's avatar
Jeff Rasley committed
14
15
16
17

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

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

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

# fetch rocm state
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}"

45

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


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')
57
extras_require = {
aiss's avatar
aiss committed
58
59
    '1bit': [], # add cupy based on cuda/rocm version
    '1bit_mpi': fetch_requirements('requirements/requirements-1bit-mpi.txt'),
60
61
    'readthedocs': fetch_requirements('requirements/requirements-readthedocs.txt'),
    'dev': fetch_requirements('requirements/requirements-dev.txt'),
aiss's avatar
aiss committed
62
63
    'autotuning': fetch_requirements('requirements/requirements-autotuning.txt'),
    'autotuning_ml': fetch_requirements('requirements/requirements-autotuning-ml.txt'),
aiss's avatar
aiss committed
64
65
66
    'sparse_attn': fetch_requirements('requirements/requirements-sparse_attn.txt'),
    'inf': fetch_requirements('requirements/requirements-inf.txt'),
    'sd': fetch_requirements('requirements/requirements-sd.txt')
67
}
68

aiss's avatar
aiss committed
69
70
71
72
73
74
75
76
77
# 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:
aiss's avatar
aiss committed
78
        cupy = f"cupy-cuda{''.join(map(str,installed_cuda_version()))}"
aiss's avatar
aiss committed
79
80
81
    if cupy:
        extras_require['1bit'].append(cupy)
        extras_require['1bit_mpi'].append(cupy)
82
83
84
85
86
87
88

# 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
89
90

cmdclass = {}
91
92

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

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

aiss's avatar
aiss committed
105
if torch_available and not torch.cuda.is_available():
106
    # Fix to allow docker builds, similar to https://github.com/NVIDIA/apex/issues/486
107
    print(
108
        "[WARNING] Torch did not find cuda available, if cross-compiling or running with cpu only "
109
110
111
        "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
112
        os.environ["TORCH_CUDA_ARCH_LIST"] = get_default_compute_capabilities()
113

114
115
ext_modules = []

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

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

124
125

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


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

aiss's avatar
aiss committed
139

aiss's avatar
aiss committed
140
141
def op_enabled(op_name):
    env_var = op_envvar(op_name)
142
143
144
    return int(os.environ.get(env_var, BUILD_OP_DEFAULT))


aiss's avatar
aiss committed
145
compatible_ops = dict.fromkeys(ALL_OPS.keys(), False)
146
147
148
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
149
    compatible_ops[op_name] = op_compatible
aiss's avatar
aiss committed
150
151
152
    print("op_name: ", op_name)
    print("op_enabled: ", op_enabled(op_name))
    print("op_compatible: ", op_compatible)
aiss's avatar
aiss committed
153
154
155
156
157
158
    # 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}")
aiss's avatar
aiss committed
159

aiss's avatar
aiss committed
160
161
162
    # 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()
aiss's avatar
aiss committed
163

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
#add dtk version
aiss's avatar
aiss committed
182
183
184
185
        if os.getenv('DS_BUILD_VERSION'):
            version_dtk = os.getenv('DS_BUILD_VERSION', "")
            git_hash += "." + version_dtk

186
187
188
    except subprocess.CalledProcessError:
        git_hash = "unknown"
        git_branch = "unknown"
189
190
191
else:
    git_hash = "unknown"
    git_branch = "unknown"
192

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

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

210
211
212
213
# 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.
aiss's avatar
aiss committed
214
# example: DS_BUILD_STRING=".dev20201022" python setup.py sdist bdist_wheel
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230

# 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}'

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

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

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

270
271
272
273
274
# 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
275
276
start_time = time.time()

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

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