Я попытался реализовать поддержку Tree Sitter в Python с использованием многопроцессорности, но процесс Python дает сбой. Далее ниже приведен минимально воспроизводимый пример.
Мне нужно иметь возможность отправлять языковые данные через очередь в другой процесс. Я не могу просто импортировать его с другой стороны, поскольку первый процесс — это API, с которым взаимодействует пользователь, а второй процесс предназначен для выполнения более ресурсоемких в вычислительном отношении элементов, чтобы не засорять основной поток. Из-за этого пользователь должен иметь возможность использовать любую грамматику сидящего на дереве, которую он найдет, и я должен иметь возможность с ней работать.
Поскольку tree_sitter.Language
не сериализуемо, мне нужно сделать это через адрес указателя c, полученный из some_tree_sitter_language.language()
(выведите его, и вы получите целое число, соответствующее адресу памяти (см. здесь). Разумно, или я так думал, я решил отправить адрес памяти другому процессу через общую очередь, как это уже делает моя программа. Именно в этот момент я узнал, что проблема с простой отправкой адреса заключается в том, что адрес памяти не совпадает, поскольку это вы. догадалась, отдельный процесс.
Я слышу ваши крики, которые пытаются напомнить мне о multiprocessing.shared_memory.SharedMemory(), но, к сожалению, для этого требуется указать необходимый размер в байтах, и, как мне любезно сказал Python:
>>> from tree_sitter_python import language
>>> from tree_sitter import Language
>>> from ctypes import sizeof
>>> sizeof(Language(language()))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: this type has no size
Если у кого-нибудь есть идеи или решения, позволяющие разделить адрес памяти между двумя процессами (это также работает в Linux, Mac и Windows), дайте мне знать. Спасибо за ваше время!
МРЭ:
from multiprocessing import Queue, Process
from tree_sitter_python import language
from tree_sitter import Language
from time import sleep
def test(q: Queue):
x= q.get()
print("BEFORE")
other_side_lang: Language = Language(x)
print("AFTER", other_side_lang)
if __name__ == "__main__":
q = Queue()
q.put(language())
x = Process(
target=test,
args=(q,),
daemon=True
)
x.start()
sleep(3)
Прикреплена частичная копия одного из журналов сбоев:
-------------------------------------
Translated Report (Full Report Below)
-------------------------------------
Process: Python [639]
Path: /usr/local/Cellar/[email protected]/3.12.4/Frameworks/Python.framework/Versions/3.12/Resources/Python.app/Contents/MacOS/Python
Identifier: org.python.python
Version: 3.12.4 (3.12.4)
Code Type: X86-64 (Native)
Parent Process: Python [633]
Responsible: Terminal [441]
User ID: 501
Date/Time: 2024-07-11 02:37:55.4483 -0600
OS Version: macOS 13.6.1 (22G313)
Report Version: 12
Bridge OS Version: 8.1 (21P1069)
Anonymous UUID: 428C86B5-033D-A2E7-748C-6F0ECD68C1FD
Time Awake Since Boot: 26 seconds
System Integrity Protection: enabled
Crashed Thread: 0 Dispatch queue: com.apple.main-thread
Exception Type: EXC_BAD_ACCESS (SIGSEGV)
Exception Codes: KERN_INVALID_ADDRESS at 0x0000000106e06040
Exception Codes: 0x0000000000000001, 0x0000000106e06040
Termination Reason: Namespace SIGNAL, Code 11 Segmentation fault: 11
Terminating Process: exc handler [639]
VM Region Info: 0x106e06040 is not in any region. Bytes before following region: 132186048
REGION TYPE START - END [ VSIZE] PRT/MAX SHRMOD REGION DETAIL
UNUSED SPACE AT START
--->
__TEXT 10ec16000-10ec1a000 [ 16K] r-x/r-x SM=COW .../MacOS/Python
Thread 0 Crashed:: Dispatch queue: com.apple.main-thread
0 _binding.cpython-312-darwin.so 0x10f1cb004 ts_language_version + 4
1 _binding.cpython-312-darwin.so 0x10f1c258c language_init + 92
2 Python 0x10f2ed918 type_call + 135
3 Python 0x10f283e62 _PyObject_MakeTpCall + 140
4 Python 0x10f37a005 _PyEval_EvalFrameDefault + 51128
5 Python 0x10f36d691 PyEval_EvalCode + 197
6 Python 0x10f3d3e48 run_eval_code_obj.llvm.14544499641581592508 + 83
7 Python 0x10f3d20e7 run_mod.llvm.14544499641581592508 + 107
8 Python 0x10f3d1888 PyRun_StringFlags + 113
9 Python 0x10f3d17d0 PyRun_SimpleStringFlags + 68
10 Python 0x10f3f4361 Py_RunMain + 714
11 Python 0x10f3f49b0 pymain_main + 378
12 Python 0x10f3f4a63 Py_BytesMain + 42
13 dyld 0x7ff80164c41f start + 1903
Thread 0 crashed with X86 Thread State (64-bit):
rax: 0x0000000106e06040 rbx: 0x00000000ffffffff rcx: 0x0000000106e06040 rdx: 0x0000000000000004
rdi: 0x0000000106e06040 rsi: 0x0000000106e06040 rbp: 0x00007ff7b12e8b20 rsp: 0x00007ff7b12e8b20
r8: 0x000000010f4c4500 r9: 0x00007ff7b12e8910 r10: 0x00007ff7b12e87f0 r11: 0x0000000000000100
r12: 0x0000000000000000 r13: 0x000000010f7221c8 r14: 0x000000010ef5bb30 r15: 0x000000010f0ce710
rip: 0x000000010f1cb004 rfl: 0x0000000000010202 cr2: 0x0000000106e06040
Logical CPU: 2
Error Code: 0x00000004 (no mapping for user data read)
Trap Number: 14
Binary Images:
0x10ec16000 - 0x10ec19fff org.python.python (3.12.4) <3079272b-e686-3efd-8d24-c01f62fdb7c2> /usr/local/Cellar/[email protected]/3.12.4/Frameworks/Python.framework/Versions/3.12/Resources/Python.app/Contents/MacOS/Python
0x10f224000 - 0x10f54ffff org.python.python (3.12.4, (c) 2001-2023 Python Software Foundation.) <17e72ebd-32f4-3ba9-b4db-906f26230882> /usr/local/Cellar/[email protected]/3.12.4/Frameworks/Python.framework/Versions/3.12/Python
0x10ef9b000 - 0x10efa2fff _struct.cpython-312-darwin.so (*) <4a0e6449-166d-3ce3-a72c-f3808ef391d4> /usr/local/Cellar/[email protected]/3.12.4/Frameworks/Python.framework/Versions/3.12/lib/python3.12/lib-dynload/_struct.cpython-312-darwin.so
0x10efd4000 - 0x10efebfff _pickle.cpython-312-darwin.so (*) <c6d9e37d-5eb6-3db6-89d7-cd82ed1a467d> /usr/local/Cellar/[email protected]/3.12.4/Frameworks/Python.framework/Versions/3.12/lib/python3.12/lib-dynload/_pickle.cpython-312-darwin.so
0x10effc000 - 0x10f00bfff _socket.cpython-312-darwin.so (*) <432b133c-3887-3890-b371-cac14ab461df> /usr/local/Cellar/[email protected]/3.12.4/Frameworks/Python.framework/Versions/3.12/lib/python3.12/lib-dynload/_socket.cpython-312-darwin.so
0x10f018000 - 0x10f023fff math.cpython-312-darwin.so (*) <5ebc3b1b-4a5f-3fc5-8078-6760fbd4d083> /usr/local/Cellar/[email protected]/3.12.4/Frameworks/Python.framework/Versions/3.12/lib/python3.12/lib-dynload/math.cpython-312-darwin.so
0x10efaf000 - 0x10efb6fff select.cpython-312-darwin.so (*) <8b0d9d36-b536-33b7-a864-ee2ecce437a9> /usr/local/Cellar/[email protected]/3.12.4/Frameworks/Python.framework/Versions/3.12/lib/python3.12/lib-dynload/select.cpython-312-darwin.so
0x10f044000 - 0x10f04bfff array.cpython-312-darwin.so (*) <83c7e987-75d3-3e37-bf34-c7648fbe9788> /usr/local/Cellar/[email protected]/3.12.4/Frameworks/Python.framework/Versions/3.12/lib/python3.12/lib-dynload/array.cpython-312-darwin.so
0x10efc3000 - 0x10efc6fff fcntl.cpython-312-darwin.so (*) <4cea1d2a-2394-31c6-93db-2c96d9d471b3> /usr/local/Cellar/[email protected]/3.12.4/Frameworks/Python.framework/Versions/3.12/lib/python3.12/lib-dynload/fcntl.cpython-312-darwin.so
0x10f030000 - 0x10f033fff _posixsubprocess.cpython-312-darwin.so (*) <a5e7a342-0997-3c1e-9cbf-dc756a36ebb0> /usr/local/Cellar/[email protected]/3.12.4/Frameworks/Python.framework/Versions/3.12/lib/python3.12/lib-dynload/_posixsubprocess.cpython-312-darwin.so
0x10f058000 - 0x10f05bfff _multiprocessing.cpython-312-darwin.so (*) <a940b46c-f9f3-3147-8994-33b0ba07f09e> /usr/local/Cellar/[email protected]/3.12.4/Frameworks/Python.framework/Versions/3.12/lib/python3.12/lib-dynload/_multiprocessing.cpython-312-darwin.so
0x10f068000 - 0x10f06bfff _posixshmem.cpython-312-darwin.so (*) <13aad305-b26b-3ad8-b21f-48a89ea8358a> /usr/local/Cellar/[email protected]/3.12.4/Frameworks/Python.framework/Versions/3.12/lib/python3.12/lib-dynload/_posixshmem.cpython-312-darwin.so
0x10f7e8000 - 0x10f85ffff _binding.abi3.so (*) <0e713bee-dfd3-37e7-8812-a066a65b0f33> /usr/local/lib/python3.12/site-packages/tree_sitter_python/_binding.abi3.so
0x10f1c0000 - 0x10f1f3fff _binding.cpython-312-darwin.so (*) <dae6b362-3e05-3cdc-b2eb-aaef397ef712> /usr/local/lib/python3.12/site-packages/tree_sitter/_binding.cpython-312-darwin.so
0x10f178000 - 0x10f17bfff _heapq.cpython-312-darwin.so (*) <682fe7fb-3ffb-3cb3-82a9-fdd75c511b40> /usr/local/Cellar/[email protected]/3.12.4/Frameworks/Python.framework/Versions/3.12/lib/python3.12/lib-dynload/_heapq.cpython-312-darwin.so
0x10f188000 - 0x10f18bfff _queue.cpython-312-darwin.so (*) <b569f10c-ca95-3473-b27e-3a2429d1c4dd> /usr/local/Cellar/[email protected]/3.12.4/Frameworks/Python.framework/Versions/3.12/lib/python3.12/lib-dynload/_queue.cpython-312-darwin.so
0x10f198000 - 0x10f19ffff zlib.cpython-312-darwin.so (*) <43492c33-fe03-34d7-b369-8fc8adaabf29> /usr/local/Cellar/[email protected]/3.12.4/Frameworks/Python.framework/Versions/3.12/lib/python3.12/lib-dynload/zlib.cpython-312-darwin.so
0x10f1ac000 - 0x10f1affff _bz2.cpython-312-darwin.so (*) <a93b8231-452d-3dcb-8a41-25fc609a5022> /usr/local/Cellar/[email protected]/3.12.4/Frameworks/Python.framework/Versions/3.12/lib/python3.12/lib-dynload/_bz2.cpython-312-darwin.so
0x10f208000 - 0x10f20ffff _lzma.cpython-312-darwin.so (*) <36ffa772-1c73-310c-84e5-c18e71179618> /usr/local/Cellar/[email protected]/3.12.4/Frameworks/Python.framework/Versions/3.12/lib/python3.12/lib-dynload/_lzma.cpython-312-darwin.so
0x10f891000 - 0x10f8b0fff liblzma.5.dylib (*) <74cc93b3-d104-3692-ab7a-7348e6fc3cdc> /usr/local/Cellar/xz/5.4.6/lib/liblzma.5.dylib
0x10f868000 - 0x10f86bfff _bisect.cpython-312-darwin.so (*) <cb668a54-c2d6-3b89-aebb-90eb973e11bc> /usr/local/Cellar/[email protected]/3.12.4/Frameworks/Python.framework/Versions/3.12/lib/python3.12/lib-dynload/_bisect.cpython-312-darwin.so
0x10f878000 - 0x10f87bfff _random.cpython-312-darwin.so (*) <f1e00bab-9951-3ed7-b9d8-64fc054ffa41> /usr/local/Cellar/[email protected]/3.12.4/Frameworks/Python.framework/Versions/3.12/lib/python3.12/lib-dynload/_random.cpython-312-darwin.so
0x10f8d3000 - 0x10f8defff _sha2.cpython-312-darwin.so (*) <ddfdfed3-159b-34b5-84a5-95ba92ca83ae> /usr/local/Cellar/[email protected]/3.12.4/Frameworks/Python.framework/Versions/3.12/lib/python3.12/lib-dynload/_sha2.cpython-312-darwin.so
0x7ff801646000 - 0x7ff8016de5ef dyld (*) <3df96f32-b9c9-3566-a6b7-4daebc6d6563> /usr/lib/dyld
0x0 - 0xffffffffffffffff ??? (*) <00000000-0000-0000-0000-000000000000> ???
Дополнительно прилагается фото:
@Аарон Да. Я собираюсь опубликовать ответ для себя, поскольку узнал, что функции сериализуются в Python, поэтому вместо отправки указателя c из языка() я могу просто отправить саму функцию.
Вместо SharedMemory
вы можете использовать MemoryManager
. Для этого вам не обязательно знать размер объекта. Вы разделяете менеджер между процессами, и он заботится о потокобезопасном чтении и записи. Но не ждите молниеносной скорости.
@Раджа, сможешь ли ты сделать MRE, отображающий это?
Поскольку память специфична для процесса, я не могу отправить адрес, но функции, очевидно, сериализуются в Python, поэтому вместо отправки указателя c из языка() я могу просто отправить саму функцию:
from multiprocessing import Queue, Process
from tree_sitter_python import language
from tree_sitter import Language
from time import sleep
def test(q: Queue):
x= q.get()
print("BEFORE")
other_side_lang: Language = Language(x())
print("AFTER", other_side_lang)
if __name__ == "__main__":
q = Queue()
q.put(language)
x = Process(
target=test,
args=(q,),
daemon=True
)
x.start()
sleep(3)
просто небольшое примечание: если вы не используете что-то вроде dill
для сериализации (что используется в сторонней библиотеке multiprocess
), сама функция на самом деле не сериализуется. Вместо этого ссылка на функцию сериализуется. Ярким примером того, чему это может помешать, является невозможность сериализации import
функций, поскольку они не существуют в импортируемом виде. Возможно, это не вызовет проблем с вашим текущим решением, но об этом следует помнить.
Очень приятно это знать. Означает ли это что-то вроде «language_function» в , это не будет сериализоваться, или мне нужно просто попросить пользователя отправить файл .so?
Я не знаю точно, но рискну предположить, что вы добьетесь большего успеха, отправив path
и name
и вызвав lang_from_so
в дочернем процессе.
Звучит отлично. Спасибо за разъяснения, это действительно полезно знать
Ну вот:
from multiprocessing import Process, Manager
from tree_sitter_python import language
from tree_sitter import Language
from time import sleep
def test(name_space):
x = name_space.x
print("BEFORE")
other_side_lang: Language = Language(x)
print("AFTER", other_side_lang)
if __name__ == "__main__":
manager = Manager()
name_space = manager.Namespace()
name_space.x = language()
x = Process(
target=test,
args=(name_space,), # share the namespace with your process, not the manager
daemon=True
)
x.start()
sleep(3)
Мой результат:
BEFORE
AFTER <Language id=140705832066240, version=14>
Иногда это меня смущает, иногда нет. Фото прилагается. Все запуски test.py имеют предоставленный код в test.py (первый сбой, второй сработал, третий сбой, и после повторного запуска после фотографии также снова сбой) Img: ibb.co/b6czHCR
Я запускал его пару раз, и ни разу не было сбоя. Я не думаю, что это связано с самим сценарием.
В какой системе вы находитесь? Может быть, это что-то с этим связано?
Windows 10 с процессором Intel и Python 3.10.
macOS 13.6.1, Python 3.12.4, процессор Intel (Macbook Pro 2017 г.)
Указатели принадлежат процессу и являются не реальными адресами памяти, а виртуальными адресами, недоступными из других процессов. Попытка доступа к недоступной ячейке памяти является определением SEGFAULT. «Общая память» — это не просто произвольная ячейка памяти, к которой могут получить доступ оба процесса, а скорее файл, отображенный в памяти, созданный таким образом, что он будет существовать только в оперативной памяти. Таким образом, разделив имя файла или дескриптор, ОС может предоставить обоим процессам доступ к ячейке памяти. Я не думаю, что из-за этой проблемы можно поделиться объектом
Language()
.