mappings.py 4.21 KB
Newer Older
1
# coding=utf-8
Mohammad's avatar
Mohammad committed
2
# Copyright (c) 2020, NVIDIA CORPORATION.  All rights reserved.
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import torch

18
from .initialize import get_model_parallel_group, get_model_parallel_world_size, get_model_parallel_rank
19
20
21
22
23
24
25
from .utils import split_tensor_along_last_dim


def _reduce(input_):
    """All-reduce the the input tensor across model parallel group."""

    # Bypass the function if we are using only 1 GPU.
26
    if get_model_parallel_world_size()==1:
27
28
29
        return input_

    # All-reduce.
30
    torch.distributed.all_reduce(input_, group=get_model_parallel_group())
31
32
33
34
35
36
37
38

    return input_


def _split(input_):
    """Split the tensor along its last dimension and keep the
    corresponding slice."""

39
    world_size = get_model_parallel_world_size()
40
    # Bypass the function if we are using only 1 GPU.
41
    if world_size==1:
42
43
44
45
46
47
        return input_

    # Split along last dimension.
    input_list = split_tensor_along_last_dim(input_, world_size)

    # Note: torch.split does not create contiguous tensors by default.
48
    rank = get_model_parallel_rank()
49
50
51
52
53
54
55
56
    output = input_list[rank].contiguous()

    return output


def _gather(input_):
    """Gather tensors and concatinate along the last dimension."""

57
    world_size = get_model_parallel_world_size()
58
    # Bypass the function if we are using only 1 GPU.
59
    if world_size==1:
60
61
62
63
        return input_

    # Size and dimension.
    last_dim = input_.dim() - 1
64
    rank = get_model_parallel_rank()
65
66
67

    tensor_list = [torch.empty_like(input_) for _ in range(world_size)]
    tensor_list[rank] = input_
68
    torch.distributed.all_gather(tensor_list, input_, group=get_model_parallel_group())
69
70
71
72
73
74
75
76
77
78

    # Note: torch.cat already creates a contiguous tensor.
    output = torch.cat(tensor_list, dim=last_dim).contiguous()

    return output


class _CopyToModelParallelRegion(torch.autograd.Function):
    """Pass the input to the model parallel region."""

79
80
81
82
    @staticmethod
    def symbolic(graph, input_):
        return input_
    
83
84
85
86
87
88
89
90
91
92
    @staticmethod
    def forward(ctx, input_):
        return input_

    @staticmethod
    def backward(ctx, grad_output):
        return _reduce(grad_output)


class _ReduceFromModelParallelRegion(torch.autograd.Function):
Nako Sung's avatar
Nako Sung committed
93
    """All-reduce the input from the model parallel region."""
94

95
96
97
98
    @staticmethod
    def symbolic(graph, input_):
        return _reduce(input_)
    
99
100
101
102
103
104
105
106
107
108
109
110
    @staticmethod
    def forward(ctx, input_):
        return _reduce(input_)

    @staticmethod
    def backward(ctx, grad_output):
        return grad_output


class _ScatterToModelParallelRegion(torch.autograd.Function):
    """Split the input and keep only the corresponding chuck to the rank."""

111
112
113
114
    @staticmethod
    def symbolic(graph, input_):
        return _split(input_)

115
116
117
118
119
120
121
122
123
124
125
126
    @staticmethod
    def forward(ctx, input_):
        return _split(input_)

    @staticmethod
    def backward(ctx, grad_output):
        return _gather(grad_output)


class _GatherFromModelParallelRegion(torch.autograd.Function):
    """Gather the input from model parallel region and concatinate."""

127
128
129
130
    @staticmethod
    def symbolic(graph, input_):
        return _gather(input_)
    
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
    @staticmethod
    def forward(ctx, input_):
        return _gather(input_)

    @staticmethod
    def backward(ctx, grad_output):
        return _split(grad_output)


# -----------------
# Helper functions.
# -----------------

def copy_to_model_parallel_region(input_):
    return _CopyToModelParallelRegion.apply(input_)

Neel Kant's avatar
Neel Kant committed
147

148
149
150
def reduce_from_model_parallel_region(input_):
    return _ReduceFromModelParallelRegion.apply(input_)

Neel Kant's avatar
Neel Kant committed
151

152
153
154
def scatter_to_model_parallel_region(input_):
    return _ScatterToModelParallelRegion.apply(input_)

Neel Kant's avatar
Neel Kant committed
155

156
157
def gather_from_model_parallel_region(input_):
    return _GatherFromModelParallelRegion.apply(input_)