Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CPU usage when use find the element #29

Open
szczepanR opened this issue Jan 12, 2022 · 12 comments
Open

CPU usage when use find the element #29

szczepanR opened this issue Jan 12, 2022 · 12 comments
Labels
enhancement New feature or request

Comments

@szczepanR
Copy link

Hi,
can you check what is the CPU consumption for the java application under test during execution i.e. find_element_by_name or find_element_by_xpath?
Task Manager on my VM (hyper-v ws2019) shows about 24%, a pretty high, isn't it?
thanks in advance

@szczepanR
Copy link
Author

Looks like it happens only when I am looking for an element that is not found. I am not getting jabexception as I am using find_element_by_xpath in a try ...except block. It seems that find_element_by_xpath is looking for my element in all nodes, which is expected.
However, I am wondering if there possibility to look for a specific element in the "direct" children of the parent i.e. I have parent element
desktop_pane = jabdriver.find_element_by_role("desktop pane")
I know that "desktop pane" can have a child with name "User Information":
user_window = desktop_pane.find_element_by_xpath( "//internal frame[@name=contains('User Information')]")
The "user_window" can be found only as a direct child for "desktop pane" so there is no need to search for "user_window" in nested nodes
I hope you get my point :)

@gaozhao1989
Copy link
Owner

Hi @szczepanR,
Thanks for your kindly remind, but unfortunately CPU cost optimize is blind area for me.
Let's have a quick walkthrough about strategy for pyjab find an element.
All find_by functions will invoke the generator function "_generate_all_childs", current implementation as below:

    def _generate_all_childs(
        self, jabelement: JABElement = None, visible: bool = False
    ) -> Generator[JABElement]:
        """generate all child jab elements from a jab element.

        Args:
            jabelement (JABElement, optional): The parent jab element to generate child jab elements.
            Defaults to None use current element.
            visible (bool, optional): The switch for find only visible child jab elements or not.
            Defaults to False to find all child elements.

        Yields:
            Generator: Generator of all child jab elements from this parent jab element.
        """
        jabelement = (
            jabelement
            if jabelement
            else JABElement(self.bridge, self.hwnd, self.vmid, self.accessible_context)
        )
        for jabelement in self._generate_childs_from_element(
            jabelement=jabelement, visible=visible
        ):
            if jabelement.children_count != 0:
                yield from self._generate_all_childs(
                    jabelement=jabelement, visible=visible
                )
            yield jabelement

So, the logic is pretty simple here:

  1. get all children from current tree level(object depth)
  2. if current jab element has child node, recursive the function to get next tree level of children.

The key point in here, the logic for find element will not loop all children, if find your specific jabelemnt, pyjab will stop iteration and return the jabelement which is found by pyjab.
Here is the code snippet:

for jabelement in self._generate_all_childs(visible=visible):
            is_matched = (
                (value is None)
                or (by == By.NAME and jabelement.name == value)
                or (by == By.ROLE and jabelement.role_en_us == value)
                or (by == By.STATES and set(jabelement.states_en_us) == set(value))
                or (by == By.OBJECT_DEPTH and jabelement.object_depth == int(value))
                or (by == By.CHILDREN_COUNT and jabelement.children_count == int(value))
                or (
                    by == By.INDEX_IN_PARENT
                    and jabelement.index_in_parent == int(value)
                )
            )
            if is_matched:
                located_element = jabelement
                break

Back to your question, pyjab does not known the jabelement you have specific in which level of node tree.
So I have to make find strategy follow the node level one by one.
Please advice if you have more efficient solution. Thanks!

@gaozhao1989
Copy link
Owner

gaozhao1989 commented Jan 13, 2022

upload cprofile results for reference

 2651 function calls (2619 primitive calls) in 0.092 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        2    0.000    0.000    0.000    0.000 __init__.py:1075(flush)
        2    0.000    0.000    0.001    0.000 __init__.py:1086(emit)
        2    0.000    0.000    0.000    0.000 __init__.py:119(getLevelName)
        2    0.000    0.000    0.000    0.000 __init__.py:1298(disable)
        7    0.000    0.000    0.000    0.000 __init__.py:1446(debug)
        2    0.000    0.000    0.001    0.001 __init__.py:1458(info)
        2    0.000    0.000    0.000    0.000 __init__.py:1540(findCaller)
        2    0.000    0.000    0.000    0.000 __init__.py:1576(makeRecord)
        2    0.000    0.000    0.001    0.001 __init__.py:1591(_log)
        2    0.000    0.000    0.000    0.000 __init__.py:160(<lambda>)
        2    0.000    0.000    0.001    0.000 __init__.py:1617(handle)
        2    0.000    0.000    0.001    0.000 __init__.py:1671(callHandlers)
        2    0.000    0.000    0.000    0.000 __init__.py:1701(getEffectiveLevel)
        9    0.000    0.000    0.000    0.000 __init__.py:1715(isEnabledFor)
        2    0.000    0.000    0.000    0.000 __init__.py:219(_acquireLock)
        2    0.000    0.000    0.000    0.000 __init__.py:228(_releaseLock)
        2    0.000    0.000    0.000    0.000 __init__.py:283(__init__)
        1    0.000    0.000    0.002    0.002 __init__.py:340(__init__)
        2    0.000    0.000    0.000    0.000 __init__.py:359(getMessage)
        1    0.000    0.000    0.000    0.000 __init__.py:368(_FuncPtr)
       55    0.000    0.000    0.001    0.000 __init__.py:384(__getattr__)
       55    0.000    0.000    0.000    0.000 __init__.py:391(__getitem__)
        2    0.000    0.000    0.000    0.000 __init__.py:423(usesTime)
        2    0.000    0.000    0.000    0.000 __init__.py:431(_format)
        2    0.000    0.000    0.000    0.000 __init__.py:438(format)
        1    0.000    0.000    0.002    0.002 __init__.py:451(LoadLibrary)
        2    0.000    0.000    0.000    0.000 __init__.py:597(formatTime)
        2    0.000    0.000    0.000    0.000 __init__.py:643(usesTime)
        2    0.000    0.000    0.000    0.000 __init__.py:649(formatMessage)
        2    0.000    0.000    0.000    0.000 __init__.py:665(format)
        4    0.000    0.000    0.000    0.000 __init__.py:806(filter)
        4    0.000    0.000    0.000    0.000 __init__.py:911(acquire)
        4    0.000    0.000    0.000    0.000 __init__.py:918(release)
        2    0.000    0.000    0.000    0.000 __init__.py:931(format)
        2    0.000    0.000    0.001    0.000 __init__.py:954(handle)
        1    0.000    0.000    0.000    0.000 actorscheduler.py:17(__init__)
        1    0.000    0.000    0.000    0.000 actorscheduler.py:22(new_actor)
        1    0.000    0.000    0.015    0.015 actorscheduler.py:39(run)
        1    0.000    0.000    0.000    0.000 cProfile.py:117(__exit__)
        1    0.000    0.000    0.000    0.000 codecs.py:260(__init__)
        1    0.000    0.000    0.000    0.000 cp1252.py:22(decode)
        2    0.000    0.000    0.000    0.000 genericpath.py:121(_splitext)
        4    0.000    0.000    0.008    0.002 genericpath.py:27(isfile)
        1    0.000    0.000    0.000    0.000 jabdriver.py:101(bridge)
        2    0.000    0.000    0.000    0.000 jabdriver.py:105(root_element)
        1    0.000    0.000    0.000    0.000 jabdriver.py:109(root_element)
        1    0.000    0.000    0.015    0.015 jabdriver.py:113(_run_actor_sched)
        1    0.024    0.024    0.058    0.058 jabdriver.py:119(init_jab)
        2    0.001    0.000    0.001    0.000 jabdriver.py:162(_is_java_window)
        1    0.000    0.000    0.000    0.000 jabdriver.py:173(_get_accessible_context_from_hwnd)
        1    0.000    0.000    0.004    0.004 jabdriver.py:209(get_java_window_hwnd)
        1    0.000    0.000    0.004    0.004 jabdriver.py:222(wait_java_window_by_title)
        1    0.000    0.000    0.031    0.031 jabdriver.py:253(find_element_by_name)
        1    0.000    0.000    0.061    0.061 jabdriver.py:29(__init__)
        1    0.000    0.000    0.000    0.000 jabdriver.py:65(title)
        5    0.000    0.000    0.000    0.000 jabdriver.py:73(hwnd)
        1    0.000    0.000    0.000    0.000 jabdriver.py:77(hwnd)
        2    0.000    0.000    0.000    0.000 jabdriver.py:81(vmid)
        1    0.000    0.000    0.000    0.000 jabdriver.py:85(vmid)
        1    0.000    0.000    0.000    0.000 jabdriver.py:89(accessible_context)
        1    0.000    0.000    0.000    0.000 jabdriver.py:93(accessible_context)
        5    0.000    0.000    0.000    0.000 jabdriver.py:97(bridge)
       22    0.000    0.000    0.000    0.000 jabelement.py:109(children_count)
        1    0.000    0.000    0.031    0.031 jabelement.py:1288(find_element)
    44/14    0.000    0.000    0.030    0.002 jabelement.py:188(_generate_all_childs)
       22    0.004    0.000    0.028    0.001 jabelement.py:216(_generate_childs_from_element)
        4    0.001    0.000    0.001    0.000 jabelement.py:258(release_jabelement)
       15    0.000    0.000    0.028    0.002 jabelement.py:30(__init__)
       15    0.028    0.002    0.028    0.002 jabelement.py:361(_get_accessible_context_info)
       46    0.000    0.000    0.000    0.000 jabelement.py:45(bridge)
       14    0.000    0.000    0.000    0.000 jabelement.py:53(hwnd)
       46    0.000    0.000    0.000    0.000 jabelement.py:61(vmid)
       33    0.000    0.000    0.000    0.000 jabelement.py:69(accessible_context)
        6    0.000    0.000    0.000    0.000 jabelement.py:77(name)
        1    0.000    0.000    0.031    0.031 jabelement.py:981(find_element_by_name)
        1    0.000    0.000    0.000    0.000 jabfixedfunc.py:30(__init__)
       55    0.000    0.000    0.001    0.000 jabfixedfunc.py:39(_fix_bridge_function)
        1    0.000    0.000    0.002    0.002 jabfixedfunc.py:52(_fix_bridge_functions)
        2    0.000    0.000    0.001    0.001 logger.py:18(info)
        7    0.000    0.000    0.000    0.000 logger.py:21(debug)
        2    0.000    0.000    0.000    0.000 ntpath.py:124(splitdrive)
        2    0.000    0.000    0.000    0.000 ntpath.py:180(split)
        2    0.000    0.000    0.000    0.000 ntpath.py:203(splitext)
        2    0.000    0.000    0.000    0.000 ntpath.py:214(basename)
        2    0.000    0.000    0.000    0.000 ntpath.py:34(_get_bothseps)
        2    0.000    0.000    0.000    0.000 ntpath.py:44(normcase)
        1    0.000    0.000    0.002    0.002 service.py:17(__init__)
        1    0.000    0.000    0.002    0.002 service.py:29(is_bridge_enable)
        1    0.000    0.000    0.002    0.002 service.py:40(init_bridge)
        1    0.000    0.000    0.010    0.010 service.py:45(load_library)
    22/20    0.000    0.000    0.002    0.000 singleton.py:7(wrapper)
        2    0.000    0.000    0.000    0.000 threading.py:1109(name)
        2    0.000    0.000    0.000    0.000 threading.py:1410(current_thread)
        1    0.000    0.000    0.015    0.015 win32utils.py:170(setup_msg_pump)
        1    0.000    0.000    0.004    0.004 win32utils.py:216(enum_windows)
      424    0.001    0.000    0.002    0.000 win32utils.py:219(get_all_hwnds)
        1    0.000    0.000    0.004    0.004 win32utils.py:237(get_hwnds_by_title)
        1    0.000    0.000    0.000    0.000 {built-in method _codecs.charmap_decode}
        1    0.002    0.002    0.002    0.002 {built-in method _ctypes.LoadLibrary}
       31    0.000    0.000    0.000    0.000 {built-in method _ctypes.POINTER}
       17    0.000    0.000    0.000    0.000 {built-in method _ctypes.byref}
        1    0.000    0.000    0.000    0.000 {built-in method _stat.S_ISREG}
        1    0.000    0.000    0.000    0.000 {built-in method _struct.calcsize}
        4    0.000    0.000    0.000    0.000 {built-in method _thread.get_ident}
        1    0.000    0.000    0.000    0.000 {built-in method builtins.__build_class__}
       55    0.000    0.000    0.001    0.000 {built-in method builtins.getattr}
        6    0.000    0.000    0.000    0.000 {built-in method builtins.hasattr}
       63    0.000    0.000    0.000    0.000 {built-in method builtins.isinstance}
        5    0.000    0.000    0.000    0.000 {built-in method builtins.len}
        2    0.000    0.000    0.000    0.000 {built-in method builtins.max}
       55    0.000    0.000    0.000    0.000 {built-in method builtins.setattr}
        1    0.002    0.002    0.002    0.002 {built-in method io.open}
        1    0.000    0.000    0.000    0.000 {built-in method nt._getfullpathname}
        8    0.000    0.000    0.000    0.000 {built-in method nt.fspath}
        2    0.000    0.000    0.000    0.000 {built-in method nt.getpid}
        4    0.008    0.002    0.008    0.002 {built-in method nt.stat}
        1    0.010    0.010    0.010    0.010 {built-in method pythoncom.PumpWaitingMessages}
        2    0.000    0.000    0.000    0.000 {built-in method sys._getframe}
        2    0.000    0.000    0.000    0.000 {built-in method time.localtime}
        2    0.000    0.000    0.000    0.000 {built-in method time.strftime}
        3    0.000    0.000    0.000    0.000 {built-in method time.time}
        1    0.004    0.004    0.004    0.004 {built-in method win32event.MsgWaitForMultipleObjects}
        1    0.001    0.001    0.003    0.003 {built-in method win32gui.EnumWindows}
       21    0.001    0.000    0.001    0.000 {built-in method win32gui.GetWindowText}
      424    0.000    0.000    0.000    0.000 {built-in method win32gui.IsWindowEnabled}
      292    0.000    0.000    0.000    0.000 {built-in method win32gui.IsWindowVisible}
      424    0.000    0.000    0.000    0.000 {built-in method win32gui.IsWindow}
        1    0.000    0.000    0.000    0.000 {method '__exit__' of '_io._IOBase' objects}
        6    0.000    0.000    0.000    0.000 {method 'acquire' of '_thread.RLock' objects}
        1    0.000    0.000    0.000    0.000 {method 'append' of 'collections.deque' objects}
        1    0.000    0.000    0.000    0.000 {method 'append' of 'list' objects}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
        2    0.000    0.000    0.000    0.000 {method 'find' of 'str' objects}
        2    0.000    0.000    0.000    0.000 {method 'flush' of '_io.TextIOWrapper' objects}
        6    0.000    0.000    0.000    0.000 {method 'format' of 'str' objects}
       59    0.000    0.000    0.000    0.000 {method 'get' of 'dict' objects}
        1    0.000    0.000    0.000    0.000 {method 'items' of 'dict' objects}
        2    0.000    0.000    0.000    0.000 {method 'lower' of 'str' objects}
        1    0.000    0.000    0.000    0.000 {method 'popleft' of 'collections.deque' objects}
        1    0.000    0.000    0.000    0.000 {method 'read' of '_io.TextIOWrapper' objects}
        6    0.000    0.000    0.000    0.000 {method 'release' of '_thread.RLock' objects}
        4    0.000    0.000    0.000    0.000 {method 'replace' of 'str' objects}
        6    0.000    0.000    0.000    0.000 {method 'rfind' of 'str' objects}
        2    0.000    0.000    0.000    0.000 {method 'rstrip' of 'str' objects}
        1    0.000    0.000    0.015    0.015 {method 'send' of 'generator' objects}
       56    0.000    0.000    0.000    0.000 {method 'startswith' of 'str' objects}
       21    0.000    0.000    0.000    0.000 {method 'update' of 'dict' objects}
        2    0.001    0.000    0.001    0.000 {method 'write' of '_io.TextIOWrapper' objects}

@szczepanR
Copy link
Author

szczepanR commented Jan 13, 2022

Thanks (again :)) for your answer. Of course, I will try to find the solution, but my experience in python is very small (as you probably noticed :)).

@szczepanR
Copy link
Author

I played with the function _generate_childs_from_element

 for index in range(jabelement.children_count): 
                child_acc = jabelement.bridge.getAccessibleChildFromContext(
                    jabelement.vmid, jabelement.accessible_context, index
                )
                child_element = JABElement(
                    jabelement.bridge, jabelement.hwnd, jabelement.vmid, child_acc
                )
                yield child_element
                #stop search at specific level
                if index == 0:
                    break

It seems that break after the specific value of index gives expected results.
I see that CPU usage is at 1%-3%, which is good.
A searched direct child element was found as well as siblings of the searched element.
Also checked that grandchildren were not found, which is expected

Can you check whether it might be the correct solution?
Thanks in advance

@gaozhao1989
Copy link
Owner

Hi @szczepanR
if you want get current level of children from "parent" jabelement, why not directly use

parent.find_element_by_index_in_parent(xxx)

@szczepanR
Copy link
Author

From "Access bridge explorer", "index in parent" is unique for every child. So it does not describe level of children,

@gaozhao1989
Copy link
Owner

That's why suggest you have to find "parent" jabelement first, then find your specific child jabelement by index_in_parent.

Also, you can combine you locator such as "@ObjectDepth=2 and @indexinparent=3" with xpath.

@szczepanR
Copy link
Author

I don't know "index_in parent" as this child element can appear in a specific situation. I know only the parent of this element. That is why I need to "scan" first-level children just to get info that this element appears. All built-in functions don't allow to "scan" specific level of children. They go to grandchildren and that is why CPU consumption is high.
I can't use "object depth" as I am getting all children's elements with specific "object depth". So again CPU consumption is high.

@szczepanR
Copy link
Author

I think the issue is also visible in NVDA, I installed it and tried to detect some elements. CPU usage is also high :(

@gaozhao1989
Copy link
Owner

Hi @szczepanR,

Thanks for your investigation. Is that possible the JAB API makes high CPU consumption.

@szczepanR
Copy link
Author

szczepanR commented Apr 8, 2022 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants