Javac와 같은 컴파일러는 자동으로 순수한 함수를 감지하고 병렬화합니까?


12

순수한 기능은 병렬화를 촉진하는 것으로 알려져 있습니다. 병렬 실행에 본질적으로 적용되는 기능적 프로그래밍에 대해 무엇입니까?

Javac와 같은 컴파일러는 메소드가 순수한 함수일 때 감지하기에 충분히 똑똑합니까? Function 과 같은 기능적 인터페이스를 구현 하지만 부작용이 있는 클래스를 항상 구현할 수 있습니다 .


7
문제는 컴파일러가 함수가 순수한지 여부를 알 수있을뿐만 아니라 순수한 함수의 병렬 실행을 지능적으로 예약 할 수 있는지 여부입니다. 각 스레드마다 새 스레드를 시작하는 것만으로는 충분하지 않습니다. 이는 비효율적입니다. GHC (Haskell)는 게으름과 "녹색 실"을 사용하여이를 처리합니다. 순수한 스레드가 주요 불순물 스레드와 관련하여 올바르게 예약되었는지 확인하는 데 추가 어려움이 있기 때문에 불순한 언어가 시도 되어도 솔직히 놀랐습니다.
Ryan Reich

@RyanReich, Java와 같은 불완전한 기능 언어에서 기능 프로그래밍을 사용하면 성능이 향상됩니까? 기능성 프로그래밍의 장점은 모듈 성과 같은 기능적인 것입니까?
Naveen

@RyanReich GHC는 프로그래머가 병렬 처리를 원할 때 주석을 달아 문제를 해결합니다. 순도는 이러한 주석이 의미를 변경하지 않고 성능 만 변경한다는 것을 의미합니다. (병행 성을 유발할 수있는 동시성 메커니즘도 있지만 이것은 다른 물고기 주전자입니다.)
Derek Elkins가 SE

@Naveen 더 큰 자유 재 배열 코드, 메모 및 공통 하위 표현 제거와 같은 병렬 처리 외에도 최적화와 관련하여 순수한 함수에 다른 이점이 있습니다. 나는 틀릴 수 있지만, 관용어 코드에서는 아마 드물고 가장 사소한 경우를 제외하고는 다소 어려울 수 있기 때문에 javac가 순도를 감지하려고 시도하지는 않습니다. 예를 들어, NullPointerExceptions 가 없다는 것을 알아야합니다 . 이를 기반으로 한 최적화의 이점은 일반적인 Java 응용 프로그램의 경우 상당히 적을 수 있습니다.
Derek Elkins가 SE

6
javac는 java 소스 코드를 사용하여 java 바이트 코드 클래스 파일을 생성하는 java 컴파일러입니다. 그것은 무엇을 할 수 있고 무엇을해야하는지에 대해 꽤 제한되어 있습니다. 바이트 코드 클래스 파일에 병렬 처리를 도입하기위한 자유 또는 필요한 기본 메커니즘이 없습니다.
Erik Eidt

답변:


33

메소드가 순수한 함수일 때 감지 할 수있을만큼 Javac 스마트와 같은 컴파일러입니다.

"충분히 똑똑하다"는 문제는 아닙니다. 이를 순도 분석 이라고 하며 일반적인 경우에는 불가능합니다. 중지 문제를 해결하는 것과 같습니다.

물론, 옵티마이 저는 항상 불가능한 일을합니다. "일반적으로 불가능할 것"은 결코 작동하지 않는다는 것을 의미하지 않으며, 모든 경우에 작동 할 수 없다는 것을 의미합니다. 사실, 함수가 순수한지 아닌지를 확인하는 알고리즘이 있습니다. 결과는 "알지 못합니다"라는 결과보다 더 자주 나타납니다. 즉, 안전과 정확성을 이유로 가정해야합니다. 이 특정 기능이 불완전 할 수 있습니다.

그리고 심지어는 경우 않는 일을, 알고리즘은 복잡하고 비싸다.

따라서 그것은 문제 # 1입니다 : 특별한 경우에만 작동합니다 .

문제 # 2 : 라이브러리 . 함수가 순수하기 위해서는 항상 순수 함수 만 호출 할 수 있습니다 (이러한 함수는 순수 함수 만 호출 할 수 있음 등). Javac은 분명히 Java에 대해서만 알고 있으며 볼 수있는 코드에 대해서만 알고 있습니다. 따라서 함수가 다른 컴파일 단위에서 함수를 호출하면 함수가 순수한지 여부를 알 수 없습니다. 다른 언어로 작성된 함수를 호출하면 알 수 없습니다. 아직 설치되지 않은 라이브러리에서 함수를 호출하면 알 수 없습니다. 등등.

이것은 전체 프로그램 분석이 있고 전체 프로그램이 동일한 언어로 작성되고 모든 것이 한 번에 한 번에 컴파일 될 때만 작동합니다. 라이브러리를 사용할 수 없습니다.

문제 # 3 : 스케줄링 . 어느 부분이 순수한지를 알아 낸 후에도 여전히 스레드를 분리하도록 일정을 예약해야합니다. 아님 스레드 시작 및 중지는 매우 비쌉니다 (특히 Java의 경우). 스레드 풀을 유지하고 시작 또는 중지하지 않더라도 스레드 컨텍스트 전환 비용도 많이 듭니다. 당신은 그렇지 않으면 당신은 것입니다, 계산에 상당한 시간이 문맥 스위치를 예약하는 데 걸리는 시간보다 실행됩니다 있는지 확인해야 손실 을 얻지 성능을.

지금까지 짐작할 수 있듯이, 일반적인 경우에는 계산에 걸리는 시간을 알아내는 것이 불가능할 것입니다. 특별한 경우.

따로 : Javac 및 최적화 . javac의 대부분의 구현은 실제로 많은 최적화를 수행하지는 않습니다. 예를 들어, Oracle의 javac 구현은 기본 실행 엔진을 사용하여 최적화를 수행합니다 . javac는 특정 함수가 순수하고 충분히 비싸다고 판단하여 다른 스레드에서 실행되도록 컴파일합니다. 그런 다음 플랫폼의 최적화 프로그램 (예 : HotSpot C2 JIT 컴파일러)이 제공되어 전체 기능을 최적화합니다. 이제 아무것도하지 않는 빈 스레드가 있습니다. 또는 javac가 다른 스레드에서 함수를 예약하기로 결정하고 플랫폼 최적화 프로그램 스레드 경계에 걸쳐 인라인을 수행 할 수 없다는 점을 제외하고 완전히 최적화하십시오. 따라서 완전히 최적화 할 수있는 기능이 불필요하게 실행됩니다.

따라서 이와 같은 작업을 수행하면 대부분의 최적화를 한 번에 수행하는 단일 컴파일러가있는 경우 컴파일러가 서로 다른 수준에서 서로 다른 모든 최적화 및 상호 작용을 알고이를 활용할 수 있습니다.

그 참고, 예를 들어, 핫 스폿 C2 JIT 컴파일러는 실제로 수행 또한 자동 병렬화의 형태 일부 자동 벡터화를 수행한다.


"순수 함수"에 대한 정의에 따라 구현에 불순 함수를 사용하는 것이 허용 될 수 있습니다.
중복 제거기

@Deduplicator 글쎄,에 대한 귀하의 정의에 따라 definition, 이종 definition을 사용하는 purity것은 아마도 모호 할 것입니다.
cat

1
문제 # 2는 거의 모든 최적화가 JIT에 의해 실행된다는 사실에 의해 대부분 무효화됩니다 (확실히 알고 있지만 무시하십시오). 마찬가지로 JIT는 통역사가 수집 한 통계에 크게 의존하므로 문제 # 3이 부분적으로 무효화됩니다. 나는 구조에 대한 최적화가 해제되어 있기 때문에 특히 "라이브러리를 사용할 수 없습니다"에 동의하지 않습니다. 추가 된 복잡성이 문제가된다는 데 동의합니다.
maaartinus

2
@maaartinus : 게다가, 내 대답의 끝 부분은 javac에만 해당됩니다. 내가 특별히 "이것은에만 작동합니다, 당신은 전체 프로그램은 동일한 언어로 작성되며, 모두가 한 번에 한 번에 컴파일 전체 프로그램 분석을 때."고 예를 들어 언급을 이것은 C2의 경우에는 사실입니다. 하나의 언어 (JVM 바이트 코드) 만 처리하며 한 번에 전체 프로그램에 액세스 할 수 있습니다.
Jörg W Mittag

1
@ JörgWMittag OP가 javac에 대해 묻는다는 것을 알고 있지만 javac가 최적화를 담당하는 것으로 가정합니다. 그리고 그들은 C2가 있다는 것을 거의 알지 못합니다. 나는 당신의 대답이 나쁘다는 것을 말하고 있지 않습니다. 그것은 할 javac의시키는 단지의 모든 (사용 같은 하찮은를 제외하고 최적화하는 것은 StringBuilder내가 그것을 취소하고 단순히 가정 것 때문에) 이외의 감각이다, 영업 이익은 javac의 그러나 수단 핫스팟 씁니다. 당신의 문제 # 2는 javac에서 아무것도 최적화하지 못하는 꽤 좋은 이유 입니다.
maaartinus

5

공감 된 답변이 한 가지 언급하지 못했습니다. 스레드 간 동기 통신은 매우 비쌉니다. 함수가 초당 수백만 번의 호출 속도로 실행될 수 있으면 실제로 그대로 두는 대신 병렬화하는 데 더 많은 피해를줍니다.

원자 변수와 함께 사용중인 루프를 사용하는 가장 빠른 형태의 동기 스레드 간 통신은 불행히도 에너지 비효율적입니다. 에너지를 절약하기 위해 조건 변수를 사용해야하는 경우 스레드 간 통신 성능이 저하됩니다.

따라서 컴파일러는 함수가 순수한지 여부를 결정할 필요가있을뿐만 아니라 병렬화가 순 승리인지 여부를 확인하기 위해 함수의 실행 시간을 추정해야합니다. 또한 원자 변수 또는 조건 변수를 사용하는 사용 중 루프 중에서 선택해야합니다. 그리고 등 뒤에 실을 만들어야합니다.

스레드를 동적으로 작성하면 조건 변수를 사용하는 것보다 느립니다. 따라서 컴파일러는 이미 실행중인 특정 수의 스레드를 설정해야합니다.

따라서 귀하의 질문에 대한 대답은 아니오입니다 . 컴파일러는 특히 Java 세계에서 순수한 함수를 자동 병렬화 할만 큼 "스마트"하지 않습니다. 그것들은 그것들을 자동 병렬화하지 않음으로써 똑똑합니다!


5
" 자동으로 병렬화하지 않으면 영리합니다! " : 너무 멀어요. 가능한 한 모든 지점에서 병렬 처리하는 것이 일반적으로 비효율적이라는 것이 사실이지만, 스마트 컴파일러는 실제 병렬화 전략을 식별합니다. 저는 대부분의 사람들이 이것을 이해한다고 생각합니다. 그래서 우리가 자동 병렬화에 대해 이야기 할 때, 우리는 자동 실용 병렬화를 의미합니다.
Nat

@Nat : 엄청나게 너무 어렵다. 이를 위해서는 런타임 스케일에서 수백 밀리 초의 순수한 함수를 식별해야하며, 컴파일러가 반복에 상수가없는 루프 런타임 (및 원하지 않는 경우)에 대한 아이디어를 얻는 것은 어리 석습니다.
여호수아

@Nat의 의견은 병렬화가 반드시 여러 스레드를 의미하는 것은 아니라는 것을 의미합니다. 예를 들어, JIT는 순수한 함수에 대한 여러 호출을 인라인하고 특정 경우 CPU 명령어를 인터리브 할 수 있습니다. 예를 들어, 두 메소드 호출이 상수를 페치하는 경우 한 번 페치하여 사용할 수있는 메소드의 두 인스턴스 모두에 대해 CPU 레지스터에 유지할 수 있습니다. 최신 CPU는 코드를 최적화 할 때 매우 유용한 수많은 범용 레지스터와 특수 명령이있는 야수입니다.

1
@Joshua : JIT 컴파일러의 경우 훨씬 더 쉽습니다. JIT 컴파일러는 함수가 순수하지 않을 수도 있지만 지금까지 어떤 호출도 부당한 행동을 유발하지 않았다는 것을 알 수 있습니다.
gnasher729

@Joshua에 동의합니다. 직장에서 병렬화하기 어려운 알고리즘이 있습니다. 나는 몇 가지 단순화 근사를 수행하고 (따라서 알고리즘을 수정하여) 수동으로 병렬화하려고 시도했으며 매번 잘못 실패했습니다. 실제로 병렬화하는 것보다 훨씬 간단하지만 무언가를 병렬화하는 것이 가능한지 알려주는 프로그램조차 매우 어렵다. 우리는 Turing-complete 프로그래밍 언어에 대해 이야기하고 있음을 기억하십시오.
juhist
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.