Re: JMP JSL의 Python 병렬 처리내용 편집기는 defs.py의 내용에서 캐리지 리턴을 제거했으며 다음과 같이 표시되어야 합니다.
def f(x):
return x*x
JMP JSL의 Python 병렬 처리휴일 방학 동안 나는 파이썬으로 땜질하는 시간을 가졌습니다. Python/JSL 인터페이스의 병렬 처리와 관련하여 직면한 문제는 Python이 일반적으로 Python이 아닌 JMP의 여러 복사본을 시작한다는 것입니다. 그 이유는 Python을 JMP에 포함하여 실행 파일이 Python이 아닌 JMP이기 때문입니다. 병렬 코드를 실행할 때 실행 파일을 실행하도록 설정하는 멀티프로세싱 패키지를 발견했습니다. 이것은 퍼즐에서 누락된 부분입니다.
아래 스크립트는 Python 3.9가 설치된 Mac 및 Windows 모두에서 JMP 15.2.1에서 테스트했습니다. 내 Windows 컴퓨터에서 Python Init()에 대한 Path() 인수를 사용해야 했습니다. 여러 버전의 Python이 있고 올바른 버전이 선택되었는지 확인하고 싶습니다. 내 Mac에서 인수가 없는 Python Init()은 내 구성에 대해 잘 작동합니다. Windows에서 한 가지 문제가 발생했습니다. pythonw.exe의 경로를 하드코딩해야 했습니다. Python 위치와 일치하도록 변경해야 합니다. FixMe 검색:
Python Init() 매개변수 및 Windows의 pythonw.exe 경로 외에는 JMP 15.2.1 이상에서 Mac 또는 Windows에서 문제 없이 실행되어야 합니다. Python을 지원하지만 테스트되지 않은 이전 버전의 JMP에서 실행할 수 있습니다.
이 스크립트와 동일한 디렉토리에 defs.py 파일을 생성해야 합니다. 다중 처리 패키지를 사용하려면 병렬로 실행할 코드가 스크립트에 로컬이 아닌 가져오기 가능한 코드에서 제공되어야 합니다. 따라서 이 스크립트에서 대부분의 작업은 sys.path가 올바르게 설정되어 defs.py 파일을 스크립트에서 간단히 가져올 수 있도록 하는 것이었습니다.
defs.py 파일의 전체 내용은 파일 주석에 있으므로 새 파일에 쉽게 붙여넣을 수 있습니다. 아래에 복제된 내용은 다음과 같습니다.
def f(x):
return x*x
스크립트는 숫자 목록 [1,2,3]을 사용하고 병렬로 f(x)를 호출하여 결과 [1,4,9]를 제공하는 값을 제곱합니다.
pyParallel.jsl
Names Default To Here( 1 );
/* Description: Python mulltiprocessing from JMP JSL
* File: pyParallel.jsl, and defs.py
* Author: Paul Nelson
* JMP Statistical Discovery, LLC
*
* Python multiprocessing from an embedded context adds additional challenges.
* Just attempting to run Python parallel processing modules, typically launches
* multiple copies of JMP, not executing the Python code in parallel.
*
* The multiprocessing module allows setting the executable to launch, since we
* are loading Python into JMP, it's an embedded case thus sys.executable => JMP, not Python.
*
*
* Note: Functions to be run in parallel must come from importable code, not local
* code definitions.
* A multiprocessing 'Feature', not bug, See: https://bugs.python.org/issue25053
*
* Known Bugs:
* On Windows within JMP, sys.exec_prefix is empty, so path to pythonw.exe has to be set explicitly
*
* Create a defs.py file with the contents below. Place in the same directory as this JSL file.
def f(x):
return x*x
*/
// On my windows machine since I have multiple versions of Python, I have to specify the
// full path to the python39.dll, on my Mac it just works.
If( Host Is("Windows"),
Python Init(Path("C:\\Users\\panels\\AppData\\Local\\Programs\\Python\\Python39\\python39.dll")),
Python Init()
);
// send the current working directory where we loaded this script, down to Python so we can find defs.py
script_path = Get Default Directory();
Python Send(script_path);
Python Submit("\[
import sys
import os
import multiprocessing
from multiprocessing import Pool
import platform
# The loaded Python does not have the current working directory initialized, so change it
# to the directory of the script, as sent in from JSL
print('script_path: ' + script_path)
hostPlatform = platform.system()
print(hostPlatform)
try:
if 'Windows' == hostPlatform:
# strip leading / off from path if it exists
if '/' == script_path[0]:
script_path = script_path[1:]
norm_path = os.path.normpath(script_path)
print(norm_path)
os.chdir(norm_path)
else:
os.chdir(script_path)
except:
print("Unable to change current working directory", sys.exc_info())
finally:
print(os.getcwd())
# if '.' is not at the beginning of sys.path, prepend so we can look for modules within CWD
if sys.path[0] != '.':
sys.path.insert(0, '.')
print( sys.path )
# here argv is [''] empty string, doesn't seem to bother us in this use case.
#print( 'argv: ', sys.argv )
# prints the path to JMP, not Python
print('sys.executable: ' + sys.executable)
# build up path prefix to python3 exectable
if 'Darwin' == hostPlatform:
pyPath = os.path.join(sys.exec_prefix, 'bin', 'python3')
elif 'Windows' == hostPlatform:
# Bug - on windows sys.exec_prefix is empty in Python within JMP!
pyPath = None
if sys.exec_prefix:
pyPath = os.path.join(sys.exec_prefix, 'pythonw.exe')
else:
# FixMe: hardcode for now
pyPath = 'C:\\Users\\panels\\AppData\\Local\\Programs\\Python\\Python39\\pythonw.exe'
print ('Python Executable: ' + pyPath)
# Finally down to running something in parallel.
#
# Import the function code that will be executed in parallel.
# JMP is not reporting ImportError or ModuleNotFound errors, the scripting just returns -1
# on an import failure, so wrap it within our own try: except: block
try:
import defs as d
# print( d.f(2) )
# Tell multiprocessing to use the Python executable, not JMP to spawn the workers.
multiprocessing.set_executable(pyPath)
# Run the tasks in parallel - (separate Python instances)
with Pool(5) as p:
print(p.map(d.f, [1,2,3])) # Results in [1,4,9] the square of the numbers
except ModuleNotFoundError as mnf:
print(mnf)
print('Check that the moudule is located on your sys.path:')
print( sys.path )
except ImportError as error:
print(error)
except Exception as ex:
print(ex)
]\");
타사 통합분석 애플리케이션 개발유용Re: JMP JSL의 Python 병렬 처리안녕하세요 @Paul_Nelson 님 , JMP 18에서 내장된 Python을 사용하여 동일한 결과(백그라운드에서 Python 스크립트 또는 함수 실행)를 어떻게 얻을 수 있습니까?
감사해요!
마테오
Re: JMP JSL의 Python 병렬 처리위와 동일한 defs.py를 사용하는 Parallel.py는 JMP 18 통합을 통해 훨씬 쉽게 수행됩니다. JMP는 Python 실행 파일의 위치를 알고 있으며 jmp 가져오기 패키지에 '상수'로 제공됩니다. 이제 JMP는 로컬 파일을 가져올 수 있도록 패키지 검색 경로에 스크립트 디렉터리를 추가하는 방법을 알고 있습니다.
# test_multiprocessing.py
# Author: Paul R. Nelson
# JMP Statistical Discovery LLC
#
# Description: Demonstrate using Python's parallel processing capability.
# The file defs.py contains the code that will execute in parallel.
#
# Contents of defs.py:
#def f(x):
# return x*x
#
import jmp
import multiprocessing
from multiprocessing import Pool
# jmp.PYTHON_EXE is the file path to JMP's Python executable.
print(jmp.PYTHON_EXE)
try:
# defs.py is the code to be run in parallel, located in same directory as this file.
import defs as d
# tell multiprocessing to use Python executable, not spawn JMP for the workers
multiprocessing.set_executable(jmp.PYTHON_EXE)
# square each number in parallel - separate Python instances
with Pool(5) as p:
print(p.map(d.f, [1,2,3])) # result is the square of the numbers
except ModuleNotFoundError as mnf:
print(mnf)
print('Check that the module is located on your sys.path:')
print( sys.path )
except ImportError as error:
print(error)
except Exception as ex:
print(ex)Re: JMP JSL의 Python 병렬 처리빠른 답변 감사합니다. 귀하의 코드는 있는 그대로 잘 작동하지만 관심 분야로 확장하는 데 문제가 있습니다. JMP에서 cmdstanpy 샘플 방법(Bayesian MCMC)을 실행하려고 합니다. 그냥 기본 형태로 사용하면 잘 작동하지만 MCMC 체인당 cmd.exe 터미널을 열고 알고리즘이 완료될 때까지(상당히 오랜 시간이 걸릴 수 있음) 열어두어 그 때까지 어떠한 상호 작용도 허용하지 않고 로그도 표시하지 않습니다. 끝.
기본 코드는 다음과 같습니다.
import cmdstanpy as cs
model = cs.CmdStanModel(stan_file=stan_file)
fit = model.sample(data=datafile, show_progress=True, show_console=True)
fit_table = fit.draws_pd(vars=genVariables+modelParams)
알고리즘이 실행되는 동안 백그라운드에서 로그 정보를 표시하고 싶습니다. 나는 귀하의 제안에 따라 다음과 같은 적응을 시도했습니다.
def sample_chain(chain_id):
return model.sample(data=datafile, chains=1, parallel_chains=1, show_progress=True, show_console=True)
try:
multiprocessing.set_executable(jmp.PYTHON_EXE)
with multiprocessing.Pool(4) as pool:
results = pool.map(sample_chain, range(4))4개의 cmd.exe 창을 열지만 완료되지 않으며 4개의 창이 열려 있는 동안 여전히 기본 프로그램에 대한 제어권을 다시 제공하지 않습니다. 추가 제안이 있으십니까?
감사해요
마테오
Re: JMP JSL의 Python 병렬 처리패키지에 익숙하지 않지만 show_progress=True 또는 show_console=True 매개변수가 문제를 일으킬 수 있는지 궁금합니다. 또한 병렬로 실행되는 코드에서 오류가 발생하면 종료되는 예외 처리기가 있는지 확인합니다. 창이 아직 열려 있어서 돌아오지 않는 것일 수도 있습니다. JMP 내의 스크립트가 아닌 병렬 스크립트의 끝에 종료를 추가해 보십시오.
JMP는 Python 공유 라이브러리를 프로세스 공간에 로드하므로 Python이 실행될 때 JMP 대신 이벤트 루프를 제어할 수 있습니다. 루프 내에서 jmp.run_jsl('Wait(0);')을 사용하여 주기적으로 JMP에 제어권을 다시 넘겨주는 것이 가능합니다. 그러면 JMP 이벤트 루프를 실행하기 위해 잠시 제어권이 JMP로 반환됩니다. JMP 실행 코드에서도 이를 실험할 수 있습니다. 루프가 아니라 병렬 실행이고 실행된 병렬 코드가 import jmp 기능에 액세스할 수 없기 때문에 정확히 어디인지는 알 수 없습니다. 나는 당신의 코드 비트를 가지고 놀면서 눈에 띄는 것이 있는지 확인합니다.
Re: JMP JSL의 Python 병렬 처리나는 작고 단순하게 시작하는 것이 무언가를 디버깅하는 가장 좋은 방법이라고 생각합니다. 멀티스레딩으로 뛰어들어 '실행'하기 전에 JMP 내에서 cmdstanpy를 실행하여 '걷기'하세요.
나는 다음을 시도했다
import os
from cmdstanpy import CmdStanModel
stan_file = os.path.join('/Users/_me_/Desktop', 'bernoulli.stan')
print(stan_file)
model = CmdStanModel(stan_file=stan_file)
print(model)
print(model.exe_info())
결과
/*:
//:*/
import os
from cmdstanpy import CmdStanModel
stan_file = os.path.join('/Users/_me_/Desktop', 'bernoulli.stan')
print(stan_file)
model = CmdStanModel(stan_file=stan_file)
print(model)
print(model.exe_info())
/*:
/Users/_me_/Desktop/bernoulli.stan
19:01:04 - cmdstanpy - INFO - No CmdStan installation found.
19:01:04 - cmdstanpy - INFO - Cannot determine whether version is before 2.27.
19:01:04 - cmdstanpy - INFO - No CmdStan installation found.
19:01:04 - cmdstanpy - INFO - Cannot determine whether version is before 2.27.
Traceback (most recent call last):
File "", 8번째 줄, 파일 "/Users/_me_/Library/Application Support/JMP/Python/3.11/lib/python/site-packages/cmdstanpy/model.py", 212행, __init__ model_info = self.src_info() ^^^^^ ^^^^^^^^^^ 파일 "/Users/_me_/Library/Application Support/JMP/Python/3.11/lib/python/site-packages/cmdstanpy/model.py", 318행, src_info 반환 컴파일 .src_info(str(self.stan_file), self._compiler_options) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 파일 "/Users/_me_/Library/Application Support/JMP/Python/3.11/lib/python /site-packages/cmdstanpy/compilation.py", 354행, src_info [os.path.join(cmdstan_path(), 'bin', 'stanc' + EXTENSION)] ^^^^^^^^^^^ ^^^ 파일 "/Users/_me_/Library/Application Support/JMP/Python/3.11/lib/python/site-packages/cmdstanpy/utils/cmdstan.py", 170행, cmdstan_path raise ValueError( ValueError: No CmdStan 설치를 찾았으면 "install_cmdstan" 명령을 실행하거나 conda 환경을 (재)활성화하세요!
이 예에서도 JMP에게 무엇을 요구하는지 이해하세요. CmdStan은 명령줄 프로그램이고 cmdstanpy 패키지는 Python 래퍼입니다. 따라서 OS를 호출하고, 명령줄 프로그램을 실행하고, 셸 출력을 모니터링하는 Python 하위 프로세스와 같은 작업을 수행하는 것으로 보입니다. JMP와 JMP jpip 래퍼로 설치된 패키지는 JMP의 비공개 사이트 패키지 디렉터리에 저장됩니다. Conda init는 Conda 배포판이 아니며 install_cmdstan 패키지가 JMP 가상 환경에서 실행하기에 충분한 환경을 구성할 수도 구성하지 않을 수도 있으므로 Conda init를 실행하지 않습니다. 일반적으로 Conda 또는 venv 활성화는 패키지 경로 및 디렉터리 경로에 대한 환경 변수를 설정합니다. 아마도 install_cmdstan도 같은 일을 할 것입니다. 그러나 이들 모두는 방금 구성된 동일한 셸 환경이 cmdstan을 가져올 Python 실행 파일을 실행하는 데 사용된다고 가정합니다. 이러한 가정은 성립하지 않습니다. JMP는 JMP 패키지 환경을 시스템의 다른 Python 인스턴스와 격리할 수 있도록 경로 자체를 설정해야 합니다. 따라서 JMP는 cmdstanpy 패키지를 가져와 실행할 수 있지만 패키지 자체는 cmdstan 명령줄 실행 파일을 찾고 있지만 찾지는 않습니다. 그런 다음 다중 처리를 추가하려고 시도하는 것은 실제로 JMP의 Python에 Python 스크립트를 실행하는 jmp.PYTHON_EXE 복사본을 시작하도록 요청한 다음 작업을 수행하는 명령줄 프로그램을 실행하기 위해 하위 프로세스를 시작하려고 시도하는 것입니다.
내 원래 예제에서는 여러 Python 인스턴스를 시작하여 병렬로 실행되는 순수 Python 함수를 생성하는 사용 사례를 제시했습니다. 실행하려는 작업은 명령줄 실행 파일에 Python 래퍼의 복잡성을 추가하고 중첩된 셸 환경에 문제가 있습니다.
귀하가 시도하는 사용 사례가 현재 형식에서 지원되지 않는다고 생각합니다. JMP에서 시도하기 전에 JMP 외부에서 동일한 다중 처리를 시도해야 합니다. 거기에 적용할 수 있다면 JMP 내에서 성공할 수도 있습니다. (jmp.PYTHON_EXE를 python.exe의 전체 경로로 바꾸고 jmp를 가져오지 마세요).
나는 기본적인 순수 Python 작업의 병렬 스크립트를 알고 있습니다. 나는 공유 라이브러리 가져오기에 의존하지 않는 가져온 Python 패키지도 병렬 스크립트에서 작동해야 한다고 믿습니다... (그러나 테스트하지는 않았습니다). JMP 환경의 site.USER_BASE를 병렬 jmp.PYTHON_EXE로 전달하고 해당 환경을 JMP 자체 설정과 동일한 디렉토리 환경과 일치하도록 적절하게 설정해야 할 수도 있습니다.
탐색하면서 발견한 내용을 커뮤니티에 다시 게시해 주세요.
JMP는 Python이 JMP의 기본 이벤트 루프에서 실행된다는 사실로 인해 제한됩니다. JMP 및 멀티스레딩 Python을 위한 가능한 솔루션 중 하나는 독립적인 Python 하위 인터프리터를 시작하는 것입니다. Python의 GIL(Global Interpreter Lock)을 제거하는 데 따른 모든 문제를 해결하려면 Python 3.13이 필요합니다. 이에 대해 조사 중이지만 Python 3.13은 JMP 18에 포함되지 않습니다. JMP 18은 수명 주기 내내 Python 3.11.x와 함께 유지됩니다. Python 3.13의 타임라인과 안정성에 따라 이러한 기능이 JMP 19 또는 20에 포함될 수도 있습니다.
Re: JMP JSL의 Python 병렬 처리설명해주셔서 감사합니다. 더 공부해서 새로운 결과가 나오면 다시 연락드리겠습니다.
마테오