컴퓨팅이 발전함에 따라 내 워크스테이션에서 1코어에서 2코어, 4코어로... 64스레드로 이동했습니다. 한편 .jsl은 효과적으로 단일 스레드로 유지되었습니다. 일부 플랫폼이 모든 스레드를 활용한다는 것을 알고 있지만 간단한 jsl 작업을 원하는 대로 여러 스레드로 확장할 수 있기를 바랍니다. 예를 들어, 10,000,000개의 데이터 행에 대해 행 단위로 작동하는 스크립트가 있고 for 루프에서 행별로 반복하고 각 행의 결과가 다른 행과 독립적이라는 것을 알고 있는 경우 이를 분할하고 싶습니다. 64개의 비동기 for 루프로 작업을 수행하고 64개의 루프가 병렬로 완료되는 동안 다른 실행을 일시 중지한 다음 .py에서 Pool() 및 poolObject.join을 사용하는 것처럼 모든 작업이 완료된 후 단일 스레드 동기 스크립트로 다시 이동합니다.
헤이 제프,
멀티 스레딩을 활용하는 플랫폼/내장 작업은 어디에서 찾을 수 있습니까?
위에서 직접 언급했듯이 이것은 우리가 JSL에서 하려는 범위를 벗어난 것입니다. 멀티스레딩의 이점이 있다고 생각하는 특정 작업이 있는 경우 세부 정보와 함께 다른 희망 사항을 여십시오.
이 희망은 많은 표를 가지고 있지만 불행히도 Parallel Assign()이 제공하는 것보다 더 많은 것은 전달하기 어려울 것입니다. 사용자 인터페이스와의 긴밀한 통합 및 JMP의 대화식 특성으로 인해 가능한 JSL을 본질적으로 병렬 할당이 허용하는 범위로 제한하지 않고는 스레드로부터 안전한 환경을 제공할 수 없습니다.
멀티스레딩을 활용하는 가장 좋은 방법은 내장 플랫폼을 최대한 많이 사용하는 것입니다. 이러한 플랫폼은 멀티스레드 솔루션을 직접 제공할 수 있는 곳입니다.멀티스레딩이 유용할 것이라고 생각되는 내장 작업에 대한 다른 희망 사항을 여는 것이 더 많은 멀티스레딩을 보고 싶은 곳을 알려주는 가장 좋은 방법입니다.
간단한 JSL 작업을 여러 스레드로 확장하는 한 이를 위해 Parallel Assign()이라는 함수가 있습니다. 각 청크에 대한 코드가 단일 숫자를 생성하는 청크로 작업을 나누고 전체 연산의 결과는 다음과 같습니다. 숫자의 행렬.
Parallel Assign()에 대한 스크립팅 인덱스에 장난감 예제가 있으며 Frame Size()에 대한 두 번째 예제는 Mandelbrot 집합 데이터를 계산하는 더 정교한 예제를 보여줍니다. 또 다른 작은 예는 @Craige_Hales '에스 루프에 대한 JSL 요리 책 게시물 그리고 거기 만델봇에 대한 추가 정보 그의 블로그에서.
멀티 스레딩을 사용하여 파일 목록에서 가장 큰 파일을 찾는 또 다른 예를 아래에 포함했습니다. 이 스크립트는 단일 스레드 버전을 먼저 실행하고 이 작업 관리자 스크린샷에서 두 가지 활동을 모두 볼 수 있습니다. 단일 스레드 버전은 긴 메사이고 병렬 버전은 높은 피크입니다. 8코어의 17,000개 파일의 경우 시간이 13초에서 4.5초로 3배 빨라졌습니다. 이상적이지는 않지만 종종 그렇듯이 스레드는 이 경우 디스크 액세스와 같은 일부 리소스를 공유합니다.
병렬로 실행되는 JSL은 공유 전역 상태를 수정하지 않도록 단순해야 합니다. 이것이 일부 전역이 첫 번째 단계로 복사되는 이유입니다. 또한 병렬 코드가 데이터 테이블을 생성하거나 디스플레이와 상호 작용하는 것과 같은 작업을 수행할 수 없음을 의미합니다.
더 풍부한 인터페이스가 필요할 수 있는 다른 간단한 작업의 종류를 알고 싶습니다. 내가 아는 것은 각 작업자가 해당 번호 이외의 항목을 반환하는 것입니다. 본질적으로 이는 행렬 대신 결과에 대한 목록을 사용하는 것을 의미합니다.
----
Names Default To Here( 1 );
// Example for Parallel Assign().
// get a list of a lot of files
dir = If(Host is("Mac"), "$SAMPLE_DATA", "$JMP_HOME");
all files = Files In Directory( dir, "recursive" );
naf = N Items( all files );
// convert relative paths to full paths
For( i = 1, i <= naf, i++,
all files[i] = dir || "/" || all files[i]
);
// Function that looks at a range of a given file list and returns
// the index of the largest file.
// Importantly, this function uses no global variables.
largest file = Function( {files, first, last},
{i, i large = -1, bytes large = -1, bytes},
For( i = first, i <= last, i++,
bytes = File Size( files[i] );
If( bytes > bytes large,
bytes large = bytes;
i large = i;
);
);
i large;
);
// Single-threaded approach: see all the files to the function.
t0 = Tick Seconds();
lf = largest file( all files, 1, naf );
t1 = Tick Seconds();
Show( naf, lf, all files[lf], t1 - t0 );
// about 13sec on my machine (17k files on Win)
wait(1); // so we can see a break in a CPU activity monitor
// Multi-threaded approach: break the list up into 8 ranges that
// can be handled by separate threads.
t0 = Tick Seconds();
num chunks = 8; // JMP may create this many threads, or fewer
// initialize to -1, so we can detect failure if desired
results = J( num chunks, 1, -1 );
Parallel Assign(
// copy these globals into the thread's local variables
{tf = Name Expr( largest file ), tfiles = all files, tn = num chunks},
// make this assignment for each chunk:
results[i] = Local( {n = N Items( tfiles )},
tf( tfiles, 1 + Floor( (i - 1) * n / tn ), Floor( i * n / tn ) )
)
);
// We need to call the function once more to see which of the top
// files is the overall largest.
top files = all files[results];
toplf = largest file( top files, 1, num chunks );
lf = results[toplf];
t1 = Tick Seconds();
Show( naf, lf, all files[lf], t1 - t0 );
// about 4.5sec