o
    iA>                     @  sN  d dl mZ d dlZd dlZd dlZd dlZd dlZd dlZd dlm	Z	m
Z
 d dlmZ d dlmZ d dlmZmZ d dlmZ d dlmZmZ d d	lmZmZmZ d dlZd dlZd d
lmZ erid dl m!Z! nedddZ!ed\Z"Z#g dZ$dd Z%dd Z&dd Z'G dd dZ(e
dg dZ)dd Z*G dd dZ+G dd  d Z,dS )!    )annotationsN)defaultdict
namedtuple)contextmanagerwraps)getframeinfostack)Empty)perf_counterperf_counter_ns)TYPE_CHECKINGAnycast)optional_import)Eventszignite.enginer   )namepandas)torch_profiler_fulltorch_profiler_time_cpu_gputorch_profiler_time_end_to_endPerfContextWorkflowProfilerProfileHandlerselect_transform_callc                      t   fdd}|S )z
    A decorator which will run the torch profiler for the decorated function,
    printing the results in full.
    Note: Enforces a gpu sync point which could slow down pipelines.
    c                    sN   t jjjdd} | i |}W d    n1 sw   Y  t|dd |S )NTuse_cudaflush)torchautogradprofilerprofileprint)argskwargsprofresultfunc W/home/dell461/cl/sdc2/last_ska_mid/HISourceFinder-master-l/src/monai/utils/profiling.pywrapper:   s
   z$torch_profiler_full.<locals>.wrapperr   r*   r-   r+   r)   r,   r   3   s   r   c                   r   )z
    A decorator which measures the execution time of both the CPU and GPU components
    of the decorated function, printing both results.
    Note: Enforces a gpu sync point which could slow down pipelines.
    c                    s   t jjjdd} | i |}W d    n1 sw   Y  |j}tdd |jD }t jj|}t jj|}td| d| dd |S )NTr   c                 s  s    | ]}|j V  qd S N)self_cuda_time_total).0evtr+   r+   r,   	<genexpr>S   s    z?torch_profiler_time_cpu_gpu.<locals>.wrapper.<locals>.<genexpr>z
cpu time: z, gpu time: r   )	r    r!   r"   r#   self_cpu_time_totalsumfunction_eventsformat_timer$   )r%   r&   r'   r(   cpu_timeZgpu_timer)   r+   r,   r-   M   s   z,torch_profiler_time_cpu_gpu.<locals>.wrapperr   r.   r+   r)   r,   r   F   s   r   c                   r   )z
    A decorator which measures the total execution time from when the decorated
    function is called to when the last cuda operation finishes, printing the result.
    Note: Enforces a gpu sync point which could slow down pipelines.
    c                    s^   t j  t } | i |}t j  t }|| d }t jj|}td| dd |S )Ng    .AzEnd-to-end time: Tr   )r    cudasynchronizer   r!   r"   r7   r$   )r%   r&   startr(   end
total_timeZtotal_time_strr)   r+   r,   r-   f   s   

z/torch_profiler_time_end_to_end.<locals>.wrapperr   r.   r+   r)   r,   r   _   s   r   c                   @  s(   e Zd ZdZdd Zdd Zdd ZdS )	r   a  
    Context manager for tracking how much time is spent within context blocks. This uses `time.perf_counter` to
    accumulate the total amount of time in seconds in the attribute `total_time` over however many context blocks
    the object is used in.
    c                 C  s   d| _ d | _d S )Nr   )r=   
start_timeselfr+   r+   r,   __init__   s   
zPerfContext.__init__c                 C  s   t  | _| S r/   )r   r>   r?   r+   r+   r,   	__enter__   s   zPerfContext.__enter__c                 C  s*   | j d ur|  jt | j  7  _d | _ d S r/   )r>   r=   r   )r@   exc_type	exc_valueexc_tracebackr+   r+   r,   __exit__   s   

zPerfContext.__exit__N)__name__
__module____qualname____doc__rA   rB   rF   r+   r+   r+   r,   r   y   s
    r   ProfileResult)r   timefilenamelinenopid	timestampc                 C  s0   ddl m} | jdd}| jjdkot||S )zMReturns True if `frame` is a call to a `Transform` object's `_call__` method.r   )	Transformr@   N__call__)monai.transformsrQ   f_localsgetf_codeco_name
isinstance)framerQ   self_objr+   r+   r,   r      s   r   c                   @  s   e Zd ZdZefddZdd Zdd Zdd	 Zd
d Z	dd Z
dd Zdd Zd(ddZdd Zed)ddZd)ddZdd  Zd*d"d#Zd*d$d%Zejfd&d'ZdS )+r   ae  
    Profiler for timing all aspects of a workflow. This includes using stack tracing to capture call times for
    all selected calls (by default calls to `Transform.__call__` methods), times within context blocks, times
    to generate items from iterables, and times to execute decorated functions.

    This profiler must be used only within its context because it uses an internal thread to read results from a
    multiprocessing queue. This allows the profiler to function across multiple threads and processes, though the
    multiprocess tracing is at times unreliable and not available in Windows at all.

    The profiler uses `sys.settrace` and `threading.settrace` to find all calls to profile, this will be set when
    the context enters and cleared when it exits so proper use of the context is essential to prevent excessive
    tracing. Note that tracing has a high overhead so times will not accurately reflect real world performance
    but give an idea of relative share of time spent.

    The tracing functionality uses a selector to choose which calls to trace, since tracing all calls induces
    infinite loops and would be terribly slow even if not. This selector is a callable accepting a `call` trace
    frame and returns True if the call should be traced. The default is `select_transform_call` which will return
    True for `Transform.__call__` calls only.

    Example showing use of all profiling functions:

    .. code-block:: python

        import monai.transform as mt
        from monai.utils import WorkflowProfiler
        import torch

        comp=mt.Compose([mt.ScaleIntensity(),mt.RandAxisFlip(0.5)])

        with WorkflowProfiler() as wp:
            for _ in wp.profile_iter("range",range(5)):
                with wp.profile_ctx("Loop"):
                    for i in range(10):
                        comp(torch.rand(1,16,16))

            @wp.profile_callable()
            def foo(): pass

            foo()
            foo()

        print(wp.get_times_summary_pd())  # print results

    Args:
        call_selector: selector to determine which calls to trace, use None to disable tracing
    c                 C  s>   t t| _t | _d | _t | _	t
 | _d| _|| _d S )Ng?)r   listresultsosgetpid
parent_pidread_thread	threadingRLocklockmultiprocessingSimpleQueuequeueZqueue_timeoutcall_selector)r@   rg   r+   r+   r,   rA      s   




zWorkflowProfiler.__init__c                 C  s   t  | jkS )z*Return True if this is the parent process.)r]   r^   r_   r?   r+   r+   r,   
_is_parent   s   zWorkflowProfiler._is_parentc                 C  s   | j dup
| j  S )z6Return True if the read thread should be still active.N)r`   rf   emptyr?   r+   r+   r,   _is_thread_active   s   z"WorkflowProfiler._is_thread_activec                 C  sv   |   r,|  r,z| j }|du rW n| | W n	 ty#   Y nw |   r,|  s|   r7| j s9tdS dS )zVRead results from the queue and add to self.results in a thread stared by `__enter__`.N)rh   rj   rf   rU   
add_resultr
   ri   AssertionErrorr@   r(   r+   r+   r,   _read_thread_func   s   
z"WorkflowProfiler._read_thread_funcc              	   C  s0   t tj }| jt||||t | dS )z(Add a ProfileResult object to the queue.N)strdatetimenowrf   putrK   r]   r^   )r@   r   	timedeltarM   rN   tsr+   r+   r,   _put_result   s   "zWorkflowProfiler._put_resultc                   s:   |dkr |r| t  fdd}|S dS jS )z
        Trace calls, when a call is encountered that is accepted by self.call_selector, create a new function to
        trace that call and measure the time from the call to a "return" frame.
        callc                   sd   |dkr0t   } j} jdd}|j}|dur$t|j d| }|||j|j	 dS dS )z6Defines a new inner trace function just for this call.returnr@   N.)
r   rV   rT   rU   rW   typerG   ru   co_filenameco_firstlineno)rY   whyargdiffrV   rZ   r   calling_framer@   r;   r+   r,   _call_profiler   s   
z4WorkflowProfiler._trace_call.<locals>._call_profilerN)rg   r   _trace_call)r@   rY   r|   r}   r   r+   r   r,   r      s   
zWorkflowProfiler._trace_callc                 C  s@   t j| jd| _| j  | jdurt | j t| j | S )zMEnter the context, creating the read thread and setting up tracing if needed.)targetN)	ra   Threadrn   r`   r;   rg   settracer   sysr?   r+   r+   r,   rB     s   

zWorkflowProfiler.__enter__c                 C  sZ   |   st| jd ttj| j}d| _|  | j	dur+t
d t
d dS dS )z>Terminate the read thread cleanly and reset tracing if needed.N)rh   rl   rf   rr   r   ra   r   r`   joinrg   r   r   )r@   rC   rD   	tracebackr`   r+   r+   r,   rF     s   

zWorkflowProfiler.__exit__r(   rK   rw   Nonec                 C  s>   | j  | j|j | W d   dS 1 sw   Y  dS )zHAdd a result in a thread-safe manner to the internal results dictionary.N)rc   r\   r   appendrm   r+   r+   r,   rk   )  s   "zWorkflowProfiler.add_resultc                 C  sN   |   std| j dd | j D W  d   S 1 s w   Y  dS )zPGet a fresh results dictionary containing fresh tuples of ProfileResult objects.z'Only parent process can collect resultsc                 S  s   i | ]	\}}|t |qS r+   )tuple)r1   kvr+   r+   r,   
<dictcomp>4  s    z0WorkflowProfiler.get_results.<locals>.<dictcomp>N)rh   RuntimeErrorrc   r\   itemsr?   r+   r+   r,   get_results.  s
   $zWorkflowProfiler.get_resultsNc                 c  sn    |du rt t d d }t }zdV  W t | }| |||j|j dS t | }| |||j|j w )zSCreates a context to profile, placing a timing result onto the queue when it exits.N   r   )r   r	   r   ru   rM   rN   )r@   r   callerr;   r~   r+   r+   r,   profile_ctx6  s   

zWorkflowProfiler.profile_ctxc                   s    fdd}|S )z
        Decorator which can be applied to a function which profiles any calls to it. All calls to decorated
        callables must be done within the context of the profiler.
        c                   s     d u r| j n }|| S r/   )rG   r   )r*   _namer   r@   r+   r,   _outerI  s   z1WorkflowProfiler.profile_callable.<locals>._outerr+   )r@   r   r   r+   r   r,   profile_callableC  s   z!WorkflowProfiler.profile_callablec                   s   G  fddd}| S )zPWrapper around anything iterable to profile how long it takes to generate items.c                      s   e Zd Z fddZdS )z0WorkflowProfiler.profile_iter.<locals>._Iterablec                 3  s~    d}t  }tt d d }|r=zt }t|}t | }||j|j |V  W n ty8   d}Y nw |sd S d S )NT   r   F)	iterr   r	   r   nextru   rM   rN   StopIteration)_selfZdo_iterZ	orig_iterr   r;   itemr~   iterabler   r@   r+   r,   __iter__T  s   

z9WorkflowProfiler.profile_iter.<locals>._Iterable.__iter__N)rG   rH   rI   r   r+   r   r+   r,   	_IterableR  s    r   r+   )r@   r   r   r   r+   r   r,   profile_iterO  s   zWorkflowProfiler.profile_iterTc                   s   i }|    D ]6\}}|rdnd  fdd|D }t|}|t| }t|}t|}	t|}
t|||||	|
f||< q|S )z
        Returns a dictionary mapping results entries to tuples containing the number of items, time sum, time average,
        time std dev, time min, and time max.
        g&.>g      ?c                   s   g | ]}|j   qS r+   )rL   )r1   resZtimemultr+   r,   
<listcomp>n  s    z6WorkflowProfiler.get_times_summary.<locals>.<listcomp>)r   r   r5   lennpstdminmax)r@   
times_in_sr(   r   r   Z	all_timesZtimesumZtimeavgZtimestdZtimeminZtimemaxr+   r   r,   get_times_summaryf  s   
z"WorkflowProfiler.get_times_summaryc                 C  s^   ddl }| |}|rdnd}dd| ddd	d
dg}|jj|d|d}|j|d dd}|S )zNReturns the same information as `get_times_summary` but in a Pandas DataFrame.r   NsnsZCountzTotal Time ()ZAvgZStdMinMaxindex)orientcolumnsr   F)	ascending)r   r   	DataFrame	from_dictsort_values)r@   r   pdZsummsuffixr   dfr+   r+   r,   get_times_summary_pdz  s   
z%WorkflowProfiler.get_times_summary_pdc                 C  s\   t |   }tj||d d   d}|  |D ]}|D ]	}||  q!qdS )zSave all results to a csv file.r   )
fieldnamesN)	r[   r   valuescsv
DictWriter_asdictkeyswriteheaderwriterow)r@   streamZall_resultswriterrlistrr+   r+   r,   dump_csv  s   zWorkflowProfiler.dump_csv)r(   rK   rw   r   r/   )T)rG   rH   rI   rJ   r   rA   rh   rj   rn   ru   r   rB   rF   rk   r   r   r   r   r   r   r   r   stdoutr   r+   r+   r+   r,   r      s&    /	



r   c                   @  s2   e Zd ZdZdd	d
Zdd Zdd Zdd ZdS )r   ar  
    Handler for Ignite Engine classes which measures the time from a start event ton an end event. This can be used to
    profile epoch, iteration, and other events as defined in `ignite.engine.Events`. This class should be used only
    within the context of a profiler object.

    Args:
        name: name of event to profile
        profiler: instance of WorkflowProfiler used by the handler, should be within the context of this object
        start_event: item in `ignite.engine.Events` stating event at which to start timing
        end_event: item in `ignite.engine.Events` stating event at which to stop timing
    r   ro   r"   r   start_eventr   	end_eventc                 C  s"   || _ || _|| _|| _d | _d S r/   )r   r"   r   r   ctx)r@   r   r"   r   r   r+   r+   r,   rA     s
   
zProfileHandler.__init__c                 C  s$   | | j| j | | j| j | S r/   )add_event_handlerr   r;   r   r<   r@   enginer+   r+   r,   attach  s   zProfileHandler.attachc                 C  s   | j | j| _| j  d S r/   )r"   r   r   r   rB   r   r+   r+   r,   r;     s   zProfileHandler.startc                 C  s   | j d d d  d | _ d S r/   )r   rF   r   r+   r+   r,   r<     s   
zProfileHandler.endN)r   ro   r"   r   r   r   r   r   )rG   rH   rI   rJ   rA   r   r;   r<   r+   r+   r+   r,   r     s    
r   )-
__future__r   r   rp   rd   r]   r   ra   collectionsr   r   
contextlibr   	functoolsr   inspectr   r	   rf   r
   rL   r   r   typingr   r   r   numpyr   r    monai.utilsr   ignite.enginer   r   Z
has_pandas__all__r   r   r   r   rK   r   r   r   r+   r+   r+   r,   <module>   s>    x