2015-06-10 2 views
0

В cygwin py.test запускается очень медленно. Это не похоже на проблему с коллекцией из-за двух причин: тот же тест быстро запускается в Linux. И иногда, если повторять один и тот же тест достаточно быстро в cygwin, он запускается менее чем за 1 секунду. Выполнение команды времени говорит, что она запускается через 0,4 секунды или 11,7 секунды, когда поставляется с опцией --collection-only, чтобы избежать выполнения фактических тестов. Я также добавил печать на крючки pytest_configure() и pytest_ignore_collect(), чтобы убедиться, что это действительно до начала сбора.pytest (py.test) очень медленный запуск в cygwin

Были другие вопросы, linke how to speed up py.test и т. Д. Но я не видел, почему под cygwin идет медленно, и как решить проблему.

Обновление: Профилирование путем профилирования python -m cProfile -s cumulat ~/.../py.test conftest.py. Ниже приведены 20 лучших результатов. Я склонен думать, что это проблема с posix.stat в пакете cygwin или cygwin python или exists и isfile в разных местах.

104699 function calls (102659 primitive calls) in 12.223 CPU seconds 
    Ordered by: cumulative time 
ncalls tottime percall cumtime percall filename:lineno(function) 
     1 0.016 0.016 12.223 12.223 {execfile} 
     1 0.000 0.000 12.223 12.223 <string>:1(<module>) 
     1 0.000 0.000 12.207 12.207 py.test:4(<module>) 
     1 0.000 0.000 12.051 12.051 config.py:23(main) 
    48/22 0.000 0.000 12.051 0.548 core.py:526(_docall) 
    48/22 0.000 0.000 12.051 0.548 core.py:520(__call__) 
129/82 0.000 0.000 12.051 0.147 core.py:387(execute) 
     1 0.000 0.000 11.926 11.926 config.py:634(pytest_cmdline_parse) 
     1 0.000 0.000 11.926 11.926 config.py:70(_prepareconfig) 
     1 0.000 0.000 11.926 11.926 config.py:741(parse) 
    4/3 0.000 0.000 11.926 3.975 core.py:97(wrapped_call) 
    4/3 0.000 0.000 11.926 3.975 core.py:121(__init__) 
     1 0.000 0.000 11.911 11.911 config.py:706(_preparse) 
    70 0.000 0.000 11.817 0.169 local.py:363(check) 
    260 11.817 0.045 11.817 0.045 {posix.stat} <<<<this one??? 
     1 0.000 0.000 9.302 9.302 config.py:698(_initini) 
     1 0.000 0.000 9.286 9.286 config.py:896(determine_setup) 
    188 0.000 0.000 9.286 0.049 genericpath.py:15(exists) <<<<this one??? 
    18 0.000 0.000 6.861 0.381 config.py:845(exists) <<<<this one??? 
     1 0.000 0.000 6.861 6.861 config.py:851(getcfg) <<<<this one??? 
     1 0.000 0.000 2.531 2.531 config.py:694(pytest_load_initial_conftests) 
     1 0.000 0.000 2.531 2.531 config.py:477(setinitial) 
     1 0.000 0.000 2.531 2.531 config.py:503(_try_load_conftest) 
    13 0.000 0.000 2.531 0.195 config.py:511(getconftestmodules) 
    32 0.000 0.000 2.531 0.079 genericpath.py:26(isfile) <<<<this one??? 
     8 0.000 0.000 2.425 0.303 common.py:261(exists) 
     1 0.000 0.000 0.156 0.156 pytest.py:4(<module>) 
     1 0.000 0.000 0.125 0.125 main.py:73(wrap_session) 
     1 0.000 0.000 0.125 0.125 config.py:615(do_configure) 
     1 0.000 0.000 0.125 0.125 main.py:115(pytest_cmdline_main) 

Заметка о сортировке в профилировании: трудно определить, какое ключевое слово использовать. Добавление строки для распечатки sort_arg_defs в pstats.py может дать представление:

$ cat -n /usr/lib/python2.x.x/pstats.py 
214   sort_arg_defs = self.get_sort_arg_defs() 
215   sort_tuple =() 
216   self.sort_type = "" 
217   connector = "" 
218   for word in field: 
219    sort_tuple = sort_tuple + sort_arg_defs[word][0] 
220    self.sort_type += connector + sort_arg_defs[word][1] 
221    connector = ", " 

Резюме Заключение: искаженный путь к файлу, как //setup.py (который должен быть /setup.py без двойной слэш в начале), показывает проблема в одной из четырех частей программного обеспечения: pytest config.determine_setup(), py.path, который используется конфигурацией pytest, библиотекой python cygwin или реализацией posix cygwin.

ответ

3

Проблема заключается в том, что pytest ищет //pytest.ini, //tox.ini, //setup.cfg и //setup.py. Каждый из них вызывал либо genericpath.exists(), либо genericpath.isfile(), чтобы потреблять около 2,5 секунд.

Чтобы исправить это, добавьте строки ниже genericpath.exists() и genericpath.isfile(), чтобы пропустить эти четыре конкретных пути.

if path.startswith(r'//'): 
    return False 

Альтернативное исправление было бы изменить _pytest/config.py поэтому не образует те двойной слэш в пути поиска.

Код, используемый для определения точной проблемы, приведен ниже. Установите myshow = True, чтобы он показывал, сколько времени потребляется для каждого файла, который он ищет.

$ diff -u /usr/lib/python2.6/genericpath.py genericpath.py 
--- /usr/lib/python2.6/genericpath.py 2012-06-09 08:33:12.000000000 -0700 
+++ genericpath.py  2015-06-11 11:46:33.674285900 -0700 
@@ -9,14 +9,29 @@ 
__all__ = ['commonprefix', 'exists', 'getatime', 'getctime', 'getmtime', 
      'getsize', 'isdir', 'isfile'] 

+myshow = False 
+import time as mytime 
+mybasetime = mytime.time() 
+def myshowtime(): 
+ currenttime = mytime.time() 
+ tmdiff = currenttime - mybasetime 
+ global mybasetime 
+ mybasetime = currenttime 
+ return tmdiff 

# Does a path exist? 
# This is false for dangling symbolic links on systems that support them. 
def exists(path): 
    """Test whether a path exists. Returns False for broken symbolic links""" 
+ pretime = myshowtime() 
+ if path.startswith(r'//'): 
+  if myshow: print "\n genericpath exists %8.3f %8.3f False " % (pretime, myshowtime()), " ", path, "\n" 
+  return False 
    try: 
     st = os.stat(path) 
+  if myshow: print "\n genericpath exists %8.3f %8.3f True " % (pretime, myshowtime()), " ", path, "\n" 
    except os.error: 
+  if myshow: print "\n genericpath exists %8.3f %8.3f False " % (pretime, myshowtime()), " ", path, "\n" 
     return False 
    return True 

@@ -25,9 +40,15 @@ 
# for the same path ono systems that support symlinks 
def isfile(path): 
    """Test whether a path is a regular file""" 
+ pretime = myshowtime() 
+ if path.startswith(r'//'): 
+  if myshow: print "\n genericpath isfile %8.3f %8.3f False " % (pretime, myshowtime()), " ", path, "\n" 
+  return False 
    try: 
     st = os.stat(path) 
+  if myshow: print "\n genericpath isfile %8.3f %8.3f True " % (pretime, myshowtime()), " ", path, "\n" 
    except os.error: 
+  if myshow: print "\n genericpath isfile %8.3f %8.3f False " % (pretime, myshowtime()), " ", path, "\n" 
     return False 
    return stat.S_ISREG(st.st_mode) 
+2

Другой способ не изменять/взломать установку заключается в том, чтобы cygwin вызывал родной python Windows вместо питона cygwin. То есть, замените команду 'py.test' на' c: /python/path/Scripts/python.exe c:/ython/path/Scripts/py.test-script.py', если ваш python для Windows установлен на 'C:/Python/path'. – minghua

+0

В сумме вы узнали, что абсолютные пути Unix-стиля на Python cygwin медленны. Относительные пути для сокращения пути или использования не-cygwin Python не страдают теми же проблемами. – rocky

+0

Рокки, этот комментарий слишком широк. Конкретная проблема заключается в том, что неправильный путь к файлу, например '// setup.py' (который должен быть'/setup.py'), обнаруживает проблему в одной из четырех частей программного обеспечения: pytest config.determine_setup(), py .path, python cygwin или реализация cygwin posix. – minghua

Смежные вопросы