Ë
    g^(hJ4  ã                   ó¬   — d dl Z d dlmZmZ d dlmZ d dlmZmZm	Z	 d dl
Z
d dlmZ g d¢Z G d„ d«      Z G d„ d	e«      Z G d
„ de«      Z G d„ d«      Zy)é    N)ÚABCÚabstractmethod)ÚTracebackType)ÚAnyÚ
NamedTupleÚOptional)ÚJoinHookÚJoinableÚJoinc                   ó(   — e Zd ZdZdd„Zdeddfd„Zy)r	   aË  
    This defines a join hook, which provides two entry points in the join context manager.

    Entry points : a main hook, which is called repeatedly while there exists a non-joined
    process, and a post-hook, which is called once all processes have joined.

    To implement a join hook for the generic join context manager, define a
    class that inherits from :class:`JoinHook` and override ``main_hook()`` and
    ``post_hook()`` as appropriate.
    ÚreturnNc                  ó   — y)zÖCall this hook while there exists a non-joined process to shadow collective communications in a training iteration.

        Training iteration i.e., in one forward pass, backward pass, and optimizer step.
        N© ©Úselfs    ú_/var/www/skyplay_api_hub/venv/lib/python3.12/site-packages/torch/distributed/algorithms/join.pyÚ	main_hookzJoinHook.main_hook   ó   � ó    Úis_last_joinerc                  ó   — y)aK  
        Call hook after all processes have joined.

        It is passed an additional ``bool`` argument ``is_last_joiner``, which indicates if the rank is one of the last to join.

        Arguments:
            is_last_joiner (bool): ``True`` if the rank is one of the last to
                join; ``False`` otherwise.
        Nr   )r   r   s     r   Ú	post_hookzJoinHook.post_hook    r   r   ©r   N)Ú__name__Ú
__module__Ú__qualname__Ú__doc__r   Úboolr   r   r   r   r	   r	      s   „ ñ	óð	¨ð 	°ô 	r   r	   c                   ó˜   ‡ — e Zd ZdZedˆ fd„«       Zedefd„«       Zeede	j                  fd„«       «       Zeedefd„«       «       Zˆ xZS )r
   a_  
    This defines an abstract base class for joinable classes.

    A joinable class
    (inheriting from :class:`Joinable`) should implement :meth:`join_hook`,
    which returns a :class:`JoinHook` instance, in addition to
    :meth:`join_device` and :meth:`join_process_group` that return device and
    process group information, respectively.
    r   c                 óT   •— t         ‰| �  «        t        j                  «       | _        y ©N)ÚsuperÚ__init__Ú_JoinConfigÚconstruct_disabled_join_configÚ_join_config)r   Ú	__class__s    €r   r#   zJoinable.__init__7   s   ø€ ä‰ÑÔÜ'×FÑFÓHˆÕr   c                  ó   — y)aŽ  
        Return a :class:`JoinHook` instance for the given :class:`Joinable`.

        Arguments:
            kwargs (dict): a :class:`dict` containing any keyword arguments
                to modify the behavior of the join hook at run time; all
                :class:`Joinable` instances sharing the same join context
                manager are forwarded the same value for ``kwargs``.
        Nr   )r   Úkwargss     r   Ú	join_hookzJoinable.join_hook<   s   € ð 	r   c                  ó   — y)zeReturn the device from which to perform collective communications needed by the join context manager.Nr   r   s    r   Újoin_devicezJoinable.join_deviceI   ó   € ð 	r   c                  ó   — y)zfReturns the process group for the collective communications needed by the join context manager itself.Nr   r   s    r   Újoin_process_groupzJoinable.join_process_groupO   r-   r   r   )r   r   r   r   r   r#   r	   r*   ÚpropertyÚtorchÚdevicer,   r   r/   Ú__classcell__)r'   s   @r   r
   r
   ,   sƒ   ø„ ñð ôIó ðIð ð
 Xò 
ó ð
ð Øð˜UŸ\™\ò ó ó ðð Øð Cò ó ó ôr   r
   c                   ó@   — e Zd ZU dZeed<   eed<   eed<   ed„ «       Zy)r$   zdThis includes all fields needed from a :class:`Joinable` instance for the join context manager side.ÚenableÚthrow_on_early_terminationÚis_first_joinablec                  ó   — t        ddd¬«      S )z¤Return a :class:`_JoinConfig` instance indicating that join-related logic should be disabled.

        e.g. if the caller is not in a join context manager.
        F©r5   r6   r7   )r$   r   r   r   r%   z*_JoinConfig.construct_disabled_join_config]   s   € ô Ø°UÈeô
ð 	
r   N)r   r   r   r   r   Ú__annotations__Ústaticmethodr%   r   r   r   r$   r$   V   s(   … ÙoàƒLØ $Ó$ØÓàñ
ó ñ
r   r$   c                   ó”   — e Zd ZdZ	 	 ddee   dedefd„Zdd„Zdd„Z	d	„ Z
d
eee      dee   dee   fd„Zd„ Zd„ Zedefd„«       Zy)r   aë
  
    This class defines the generic join context manager, which allows custom hooks to be called after a process joins.

    These hooks should shadow the
    collective communications of non-joined processes to prevent hanging and
    erroring and to ensure algorithmic correctness. Refer to :class:`JoinHook`
    for details about the hook definition.

    .. warning::
        The context manager requires each participating :class:`Joinable` to
        call the method :meth:`notify_join_context()` before its own per-
        iteration collective communications to ensure correctness.

    .. warning::
        The context manager requires that all ``process_group`` attributes in
        the :class:`JoinHook` objects are the same. If there are multiple
        :class:`JoinHook` objects, then the ``device`` of the first is used.
        The process group and device information is used for checking for non-
        joined processes and for notifying processes to throw an exception if
        ``throw_on_early_termination`` is enabled, both of which using an all-
        reduce.

    Arguments:
        joinables (List[Joinable]): a list of the participating
            :class:`Joinable` s; their hooks are iterated over in the given
            order.

        enable (bool): a flag enabling uneven input detection; setting to
            ``False`` disables the context manager's functionality and should
            only be set when the user knows the inputs will not be uneven
            (default: ``True``).

        throw_on_early_termination (bool): a flag controlling whether to throw an
            exception upon detecting uneven inputs (default: ``False``).

    Example::

        >>> import os
        >>> import torch
        >>> import torch.distributed as dist
        >>> import torch.multiprocessing as mp
        >>> # xdoctest: +SKIP
        >>> import torch.nn.parallel.DistributedDataParallel as DDP
        >>> import torch.distributed.optim.ZeroRedundancyOptimizer as ZeRO
        >>> from torch.distributed.algorithms.join import Join
        >>>
        >>> # On each spawned worker
        >>> def worker(rank):
        >>>     dist.init_process_group("nccl", rank=rank, world_size=2)
        >>>     model = DDP(torch.nn.Linear(1, 1).to(rank), device_ids=[rank])
        >>>     optim = ZeRO(model.parameters(), torch.optim.Adam, lr=0.01)
        >>>     # Rank 1 gets one more input than rank 0
        >>>     inputs = [torch.tensor([1.]).to(rank) for _ in range(10 + rank)]
        >>>     with Join([model, optim]):
        >>>         for input in inputs:
        >>>             loss = model(input).sum()
        >>>             loss.backward()
        >>>             optim.step()
        >>>     # All ranks reach here without hanging/erroring
    Ú	joinablesr5   r6   c                 ó  — t        |«      dk(  rt        d«      ‚|| _        | j                  D �cg c]  } |j                  di |¤Ž‘Œ c}| _        || _        || _        | j                  «        | j                  «        y c c}w )Nr   z7The join context manager requires at least one joinabler   )	ÚlenÚ
ValueErrorÚ
_joinablesr*   Ú_join_hooksÚ_enableÚ_throw_on_early_terminationÚ_set_joinable_configsÚ_extract_dist_info)r   r=   r5   r6   r)   Újoinables         r   r#   zJoin.__init__¦   s~   € ô ˆy‹>˜QÒÜÐVÓWÐWØ#ˆŒà9=¿¹ö
Ø-5ÐˆH×ÑÑ( Ó(ò
ˆÔð ˆŒØ+EˆÔ(Ø×"Ñ"Ô$Ø×ÑÕ!ùò
s   ¯A?Nc                 ó°   — t        | j                  «      dkD  sJ ‚d}| j                  D ]+  }t        | j                  | j                  |¬«      |_        d}Œ- y)zESet the :class:`_JoinConfig` of each participating :class:`Joinable`.r   Tr9   FN)r?   rA   r$   rC   rD   r&   )r   r7   rG   s      r   rE   zJoin._set_joinable_configs¸   sZ   € ä�4—?‘?Ó# aÒ'Ð'Ð'Ø ÐØŸ™ò 	&ˆHÜ$/Ø—|‘|Ø+/×+KÑ+KØ"3ô%ˆHÔ!ð
 !&Ññ	&r   c                 ó  — d}d}| j                   D ]:  }|€|j                  }n||j                  k7  rt        d«      ‚|�Œ/|j                  }Œ< || _        t        j                  | j                  «      | _        || _        y)aÃ  
        Extract the process group and device information from the joinables.

        If there are multiple joinables, then the context manager uses the
        first specified device.

        Preconditions:
            ``self._joinables`` is not ``None`` and is non-empty.

        Raises:
            ValueError
                If there are multiple conflicting ``process_group`` attributes
                among the ``Joinable`` objects.
        Nz7Using join context manager with multiple process groups)	rA   r/   r@   r,   Ú_process_groupÚdistÚget_rankÚ_rankÚ_device)r   Úprocess_groupr2   rG   s       r   rF   zJoin._extract_dist_infoÄ   s‰   € ð ˆØˆØŸ™ò 	.ˆHØÐ$Ø (× ;Ñ ;‘Ø (×"=Ñ"=Ò=Ü ØMóð ð ‰~Ø!×-Ñ-‘ð	.ð ,ˆÔÜ—]‘] 4×#6Ñ#6Ó7ˆŒ
Øˆ�r   c                  ó   — y r!   r   r   s    r   Ú	__enter__zJoin.__enter__â   r   r   ÚtypeÚvalueÚ	tracebackc           	      ó¾  — | j                   r|ryd}d}d}d}t        j                  d«       |s�||kD  r)t        j                  d|› d| j                  › d	|› d
�«       | j                  «       }|dk(  rd}nD| j                  r| j                  «        | j                  D ]  }	|	j                  «        Œ d}|dz  }|sŒ�| j                  D ]  }	|	j                  |«       Œ y)zÇ
        Repeatedly runs the main hooks until all processes join; then, runs the post-hooks.

        Raises:
            RuntimeError
                If ``throw_on_early_termination=True``.
        NFTr   iè  Úoncez+Detected uneven input skew of greater than z. This means that rank z has at least zz fewer inputs than other currently-active ranks. This level of skew could lead to performance degradation during training.é   )rC   ÚwarningsÚsimplefilterÚwarnrM   Ú_get_num_nonjoined_procsrD   Ú_notify_procs_to_terminaterB   r   r   )
r   rR   rS   rT   Úall_procs_joinedr   ÚiÚWARN_THRESHOLDÚnum_nonjoined_procsr*   s
             r   Ú__exit__zJoin.__exit__ä   s  € ð �|Š|™tØà ÐØˆàˆØˆÜ×Ñ˜fÔ%á"Ø�>Ò!Ü—‘ØAØ%Ð&Ð&=Ø—z‘z�l .°Ð0@ð A3ð3ôð #'×"?Ñ"?Ó"AÐØ" aÒ'Ø#'Ñ à×3Ò3Ø×3Ñ3Ô5ð "&×!1Ñ!1ò *�IØ×'Ñ'Õ)ð*ð "'�Ø�Q‘�ò/ #ð4 ×)Ñ)ò 	0ˆIØ×Ñ Õ/ñ	0r   c                 ó¦   — t        j                  d| j                  ¬«      }t        j                  || j
                  ¬«       |j                  «       S )zaReturn the number of non-joined processes by shadowing an all-reduce in the non-joined processes.rW   ©r2   ©Úgroup)r1   ÚzerosrN   rK   Ú
all_reducerJ   Úitem)r   r`   s     r   r[   zJoin._get_num_nonjoined_procs  s;   € ä#Ÿk™k¨!°D·L±LÔAÐÜ�‰Ð+°4×3FÑ3FÕGØ"×'Ñ'Ó)Ð)r   c                 ó¸   — t        j                  d| j                  ¬«      }t        j                  || j
                  ¬«       t        d| j                  › d�«      ‚)z±Schedule an all-reduce to notify non-joined processes to terminate.

        Also raise a ``RuntimeError`` indicating that the current process has exhausted its inputs.
        rW   rc   rd   zRank z exhausted all inputs.)r1   ÚonesrN   rK   rg   rJ   ÚRuntimeErrorrM   )r   rj   s     r   r\   zJoin._notify_procs_to_terminate  sE   € ô
 �z‰z˜! D§L¡LÔ1ˆÜ�‰˜ D×$7Ñ$7Õ8Ü˜U 4§:¡: ,Ð.DÐEÓFÐFr   rG   c                 óÌ  — t        | d«      sJ dt        | «      › d�«       ‚| j                  }|j                  r|j                  sy| j
                  }| j                  }t        j                  d|¬«      }t        j                  ||d¬«      }|j                  rKt        j                  d|¬«      }t        j                  ||¬	«       |j                  «       }|rt        d
«      ‚|S )aH  
        Notifies the join context manager that the calling process has not yet joined.

        Then, if ``throw_on_early_termination=True``, checks if uneven inputs have been detected
        (i.e. if one process has already joined) and throws an exception if so.

        This method should be called from a :class:`Joinable` object before
        its per-iteration collective communications. For example, this should
        be called at the beginning of the forward pass in
        :class:`DistributedDataParallel`.

        Only the first :class:`Joinable` object passed into the context
        manager performs the collective communications in this method, and
        for the others, this method is vacuous.

        Arguments:
            joinable (Joinable): the :class:`Joinable` object calling this
                method.

        Returns:
            An async work handle for the all-reduce meant to notify the context
            manager that the process has not yet joined if ``joinable`` is the
            first one passed into the context manager; ``None`` otherwise.
        r&   zCheck that the z/ constructor calls the ``Joinable`` constructorNrW   rc   T)re   Úasync_oprd   zLDetected at least one rank that exhausted inputs. Throwing across all ranks.)ÚhasattrrR   r&   r7   r5   r,   r/   r1   rj   rK   rg   r6   rf   rh   rk   )rG   Újoin_configr2   rO   rj   Úworkrf   Úshould_throws           r   Únotify_join_contextzJoin.notify_join_context'  sÚ   € ô4 �x Ô0ð 	
Øœd 8›nÐ-ð .'ð 'ó	
Ð0ð
 ×+Ñ+ˆà×,Ò,°K×4FÒ4FØà×%Ñ%ˆØ ×3Ñ3ˆô �z‰z˜! FÔ+ˆÜ�‰˜t¨=À4ÔHˆà×1Ò1ä—K‘K ¨&Ô1ˆEÜ�O‰O˜E¨Õ7Ø Ÿ:™:›<ˆLÙÜ"ð1óð ð ˆr   )TFr   )r   r   r   r   Úlistr
   r   r#   rE   rF   rQ   r   rR   ÚBaseExceptionr   ra   r[   r\   r;   rr   r   r   r   r   r   h   s    „ ñ;ð@ Ø+0ñ	"à˜‘>ð"ð ð"ð %)ó	"ó$
&óò< ð20à�t˜MÑ*Ñ+ð20ð ˜Ñ&ð20ð ˜MÑ*ó	20òh*òGð ð4 hò 4ó ñ4r   r   )rX   Úabcr   r   Útypesr   Útypingr   r   r   r1   Útorch.distributedÚdistributedrK   Ú__all__r	   r
   r$   r   r   r   r   ú<module>r{      sP   ðã ß #Ý ß ,Ñ ,ã Ý  ò +€÷ñ ô<'ˆsô 'ôT
�*ô 
÷$tò tr   