Работаю с CMS Bitrix, в нем есть папка /bitrix/components/bitrix/, и в этой папке лежит около 400 папок, без файлов. Дома на компьютере с SSD раскрытие папки около 170-300 мс (измеряю время выполнения функции action_rerfresh()), чем больше файлов/папок тем медленнее. На работе компонентов больше, и диск медленнее, там это заметнее.
Попытался исправить сам. Первым делом вставил if i % 10 == 0: app_idle(), так интерфейс не замораживается, но время перед открытием папки увеличивается.
Потом добавил TREE_LOCK, по коду ниже это есть, задержка стала 90-190 мс, но все равно ощущается, в SublimeText ее нету, достичь бы такого же поведения...
# /py/cuda_project_man/__init__.py
# line: 492
def action_refresh(self, parent=None):
unfold = parent is None
if parent is None:
# clear tree
tree_proc(self.tree, TREE_ITEM_DELETE, 0)
if self.project_file_path is None:
project_name = PROJECT_UNSAVED_NAME
else:
project_name = self.project_file_path.stem
parent = tree_proc(
self.tree,
TREE_ITEM_ADD,
0,
-1,
project_name,
self.ICON_PROJ,
)
#select 1st node
items_root = tree_proc(self.tree, TREE_ITEM_ENUM, 0)
tree_proc(self.tree, TREE_ITEM_SELECT, items_root[0][0])
nodes = self.project["nodes"]
self.top_nodes = {}
else:
fn = self.get_location_by_index(parent)
if not fn: return
#print('Reading dir:', fn)
try:
nodes = sorted(Path(fn).iterdir(), key=Command.node_ordering)
except:
tree_proc(self.tree, TREE_ITEM_SET_ICON, parent, image_index=self.ICON_BAD)
return
new_nodes = []
for path in map(Path, nodes):
spath = str(path)
sname = path.name
if is_win_root(spath):
sname = spath
elif self.options.get("no_hidden", True) and is_hidden(spath):
continue
elif self.is_filename_ignored(spath):
continue
if is_locked(spath):
imageindex = self.ICON_BAD
elif path.is_dir():
imageindex = self.ICON_DIR
elif is_simple_listed(path.name, MASKS_IMAGES):
imageindex = self.ICON_IMG
elif is_simple_listed(path.name, MASKS_ZIP):
imageindex = self.ICON_ZIP
elif is_simple_listed(path.name, MASKS_BINARY):
imageindex = self.ICON_BIN
else:
lexname = lexer_proc(LEXER_DETECT, path.name)
if lexname:
imageindex = self.icon_get(lexname)
else:
imageindex = self.ICON_ALL
new_nodes.append({
'sname': sname,
'imageindex': imageindex,
'spath': spath
})
###################################################
tree_proc(self.tree, TREE_LOCK)
for new_node in new_nodes:
index = tree_proc(
self.tree,
TREE_ITEM_ADD,
parent,
-1,
new_node['sname'],
new_node['imageindex'],
data=new_node['spath'],
)
if nodes is self.project['nodes']:
self.top_nodes[index] = path
# dummy nested node for folders
if new_node['imageindex'] == self.ICON_DIR:
tree_proc(
self.tree,
TREE_ITEM_ADD,
index,
-1,
'dummy',
-1
)
tree_proc(self.tree, TREE_UNLOCK)
###################################################
if unfold:
tree_proc(self.tree, TREE_ITEM_UNFOLD, parent)
Кажется мы уже упираемся в скорость АПИ (вряд ли скорость чтения папки питоном такая медленная). может можно было бы добавитть АПИ для "добавить много узлов сразу", не знаю.
Не пойму куда вы вставили LOCK/UNLOCK, можете дать весь файл или pullreq?
Не пойму куда вы вставили LOCK/UNLOCK, можете дать весь файл или pullreq?

Кажется мы уже упираемся в скорость АПИ (вряд ли скорость чтения папки питоном такая медленная). может можно было бы добавитть АПИ для "добавить много узлов сразу", не знаю.
Вроде бы по замерам основное время как раз на добавление элементов в дерево и уходит, попробую позже тогда сделать нативную функцию принимающую несколько элементов, если других вариантов пока не видно.
Добавил LOCK/UNLOCK - в новую обертку, т.к. добавление в action_refresh трудно, там внутри парочка return (они бы вышли без делания UNLOCK).
попробую позже тогда сделать нативную функцию принимающую несколько элементов, если других вариантов пока не видно.
я думал сделать передачу туда - это просто новый акшен TREE_ITEM_ADD_MANY- словарика. он бы держал в себе все детали про Н пунктов дерева.
Вопрос насколько это надо! у вас время около 200мс- надо ли ради того городить чтото?
У меня самую бОльшую часть времени занимают вызовы к is_file и is_dir, а заполнение дерева очень быстрое.
И еще при повторном открытии - удаление детей очень много времени занимает. Тут тоже нужен бы TREE_LOCK
Так нужно сначала точно знать что тормозит- чтение папки или АПИ tree_proc?
Тормозят системные вызовы is_file и is_dir, не tree_proc.
Если возможно (не уверен) переделать это Path(fn).iterdir() на os.scandir - должно быть значительно быстрее. У меня Path.iterdir + is_dir на большой папке занимает 140 мс, а os.scandir + is_dir - 18 мс.
А, ясно, значит надо переделать на scandir. patch welcome.
У меня самую бОльшую часть времени занимают вызовы к
is_fileиis_dir, а заполнение дерева очень быстрое.
У меня is_file, is_dir, итд где то 10-20% времени съедает от общего выполнения функции, Windows 10.
https://github.com/Alexey-T/CudaText/pull/3607
Создал 1600 папок, при открытии файловые операции начали действительно отъедать основное время.
Я потестил плагин со scandir(), вроде все окей, но возможно я что то сломал. В чем кстати суть двух одинаковых .py файлов в разных местах?
@memstream is_filename_ignored() проверяет os.path.isdir для каждого файла - у меня 25% времени где-то занимает (4000 папок)
двух одинаковых .py файлов в разных местах?
второе место исп-тся МакОС пакетом. его сборкой. генерировать автоматом- не поулчилось.
не закрываю, т.к. надо фиксить что @halfbrained выше сказал.
оптимизировал- тепеорь is_dir() не дергается лишние разы в is_filename_ignored(). проверьте плиз.
оптимизировал- тепеорь is_dir() не дергается лишние разы в is_filename_ignored(). проверьте плиз.
У меня функция action_refresh_int на тестовой папке отрабатывает 0.03с, хороший результат
Most helpful comment
Тормозят системные вызовы
is_fileиis_dir, неtree_proc.Если возможно (не уверен) переделать это
Path(fn).iterdir()наos.scandir- должно быть значительно быстрее. У меняPath.iterdir+is_dirна большой папке занимает 140 мс, аos.scandir+is_dir- 18 мс.