내용을 다르게 쓰거나 다르게 컴파일하기 때문에 멀티 스레딩에서 함수형 프로그래밍이 더 빠릅니까?


63

기능 프로그래밍의 세계로 뛰어 들어가면서 기능 언어가 멀티 스레딩 / 멀티 코어 프로그램에 더 적합하다는 것을 어디에서나 계속 읽습니다. 나는 같은 다른 많은 것들을 어떻게 함수형 언어 이해 재귀 , 난수 등 만이 있기 때문에 나는 멀티 스레딩 빠르게 기능 언어 인 경우 알아낼 수 없습니다 컴파일 다르게 또는 내가 있기 때문에 쓰기 다르게.

예를 들어, 특정 프로토콜을 구현하는 프로그램을 Java로 작성했습니다. 이 프로토콜에서 두 당사자는 수천 개의 메시지를 서로주고받으며 해당 메시지를 암호화 한 후 다시 보내고받습니다. 예상대로 수천 개의 규모를 다룰 때는 멀티 스레딩이 중요합니다. 이 프로그램에는 잠금이 없습니다 .

Scala에서 동일한 프로그램을 작성하면 (JVM 사용)이 구현이 더 빨라 집니까? 그렇다면 왜 그렇습니까? 글쓰기 스타일 때문입니까? 이 경우 입니다 때문에 문체의 현재 자바는 람다 표현식을 포함, 나는 람다 자바를 사용하여 동일한 결과를 달성 할 수 있을까? 아니면 스칼라는 다른 방식으로 컴파일하기 때문에 더 빠릅니까?


64
Afaik 기능 프로그래밍은 멀티 스레딩을 더 빠르게하지 않습니다. 불변성과 같은 기능 프로그래밍의 기능과 부작용이없는 기능이 있기 때문에 멀티 스레딩을보다 쉽게 ​​구현하고 안전하게 만들 수 있습니다.
피터 B

7
1) 더 나은 것은 실제로 정의되지 않습니다. 2) 반드시 단순히 "더 빠른"것으로 정의 되지 않았습니다 . Y에 대한 0.1 % 성능 향상을 위해 코드 크기의 10 억 배가 필요한 언어 X는 더 나은 것을 합리적으로 정의하기 위해 Y보다 낫지 않습니다.
Bakuriu

2
"기능적 프로그래밍"또는 "기능적 스타일로 작성된 프로그램"에 대해 질문 했습니까? 더 빠른 프로그래밍으로 더 빠른 프로그램이 생성되지 않는 경우가 종종 있습니다.
벤 Voigt

1
항상 백그라운드에서 실행하고 할당 요구를
따라야

4
가장 간단한 대답은 다음과 같습니다. 함수형 프로그래밍은 경쟁 조건 문제를 고려하는 쓰기 프로그램을 허용 하지만 명령형 스타일로 작성된 프로그램이 느리다는 것을 의미하지는 않습니다.
Dawid Pura

답변:


97

사람들이 기능적 언어가 병렬 처리에 더 좋다고 말하는 이유는 일반적으로 변경 가능한 상태를 피하기 때문입니다 . 가변 상태는 병렬 처리의 맥락에서 "모든 악의 뿌리"입니다. 그들은 동시 프로세스간에 공유 될 때 경쟁 조건에 빠지기 쉽습니다. 그런 다음 경쟁 조건에 대한 솔루션에는 앞서 언급 한 것처럼 잠금 및 동기화 메커니즘이 포함되는데, 이러한 개념은 프로세스가 서로 공유 리소스를 사용하기를 기다리면서 런타임 오버 헤드가 발생하고 설계 개념이 복잡해지기 때문에 런타임 오버 헤드가 발생합니다. 이러한 응용 프로그램 내에 깊이 중첩되어 있습니다.

변경 가능한 상태를 피하면 동기화 및 잠금 메커니즘의 필요성이 사라집니다. 기능적 언어는 일반적으로 변경 가능한 상태를 피하기 때문에 병렬 처리에 자연스럽게 더 효율적이고 효과적입니다. 공유 리소스의 런타임 오버 헤드가 없으며 일반적으로 따르는 추가 디자인 복잡성이 없습니다.

그러나 이것은 모두 부수적입니다. Java 솔루션이 변경 가능한 상태 (특히 스레드간에 공유 됨)를 피할 경우 스칼라 또는 Clojure와 같은 기능적 언어로 변환하면 동시 효율성 측면에서 이점이 없습니다. 원래 솔루션에는 이미 발생하는 오버 헤드가 없기 때문입니다. 잠금 및 동기화 메커니즘.

TL; DR : 스칼라의 솔루션이 Java의 솔루션보다 병렬 처리에서 더 효율적인 경우, 코드가 JVM을 통해 컴파일 또는 실행되는 방식 때문이 아니라 Java 솔루션이 스레드간에 변경 가능한 상태를 공유하기 때문입니다. 경쟁 조건을 피하거나 동기화를 피하기 위해 동기화 오버 헤드를 추가하십시오.


2
하나의 스레드 만 데이터 조각을 수정하는 경우 특별한주의가 필요하지 않습니다. 여러 스레드가 특정 종류의 특별한주의 (동기화, 트랜잭션 메모리, 잠금 등)가 필요한 동일한 데이터를 수정할 수있는 경우에만 해당됩니다. 이것의 예는 스레드의 스택으로, 기능 코드에 의해 지속적으로 변경되지만 여러 스레드에 의해 수정되지는 않습니다.
Brendan

31
하나의 스레드가 데이터를 변경하고 다른 스레드가 데이터를 읽도록하면 "특별한주의"를 시작하기에 충분합니다.
Peter Green

10
@Brendan : 아니요, 한 스레드가 데이터를 수정하는 동안 다른 스레드가 동일한 데이터를 읽는 경우 경쟁 조건이 있습니다. 하나의 스레드 만 수정하더라도 특별한주의가 필요합니다.
Cornstalks

3
병렬 처리의 맥락에서 변하기 쉬운 상태는 "모든 악의 뿌리" => 아직 Rust를 보지 않았다면, 나는 당신이 그것을 들여다 보는 것이 좋습니다. 실제 문제는 변경 가능한 앨리어싱과 함께 변경 가능하다는 것을 인식함으로써 변경 가능성을 매우 효율적으로 관리 할 수 ​​있습니다.
Matthieu M.

2
@MatthieuM. 그래, 고마워! 내 대답에 더 명확하게 표현하도록 편집했습니다. 가변 상태는 동시 프로세스간에 공유 될 때 "모든 악의 근원"일뿐입니다. Rust는 소유권 제어 메커니즘으로 피할 수 있습니다.
MichelHenrich

8

둘 다. 더 빨리 컴파일하기 쉬운 방식으로 코드를 작성하기가 더 빠르기 때문에 더 빠릅니다. 언어를 전환하여 속도 차이를 반드시 얻을 필요는 없지만 기능적 언어로 시작한 경우 프로그래머가 적은 노력으로 멀티 스레딩을 수행했을 수 있습니다 . 같은 줄을 따라 프로그래머가 스레딩 실수를해서 명령형 언어로 속도가 빨라지고 실수를 알아 채기가 훨씬 더 어려워집니다.

그 이유는 프로그래머가 일반적으로 잠금이없는 스레드 코드를 가능한 한 작은 상자에 넣고 가능한 한 빨리 편안하고 변하기 쉬운 동기 세계로 탈출하려고 시도하기 때문입니다. 속도를 높이는 대부분의 실수는 해당 경계 인터페이스에서 이루어집니다. 함수형 프로그래밍 언어에서는 해당 경계에서 실수하는 것에 대해 크게 걱정할 필요가 없습니다. 대부분의 전화 코드는 "상자 안에"있습니다.


7

함수형 프로그래밍은 일반적으로 더 빠른 프로그램을 만들지 않습니다. 무엇을 만드는 것은입니다 쉽게 병렬 및 동시 프로그래밍. 여기에는 두 가지 주요 키가 있습니다.

  1. 변경 가능한 상태를 피하면 프로그램에서 잘못 될 수있는 일의 수가 줄어드는 경향이 있으며, 동시 프로그램에서는 더 많은 일이 발생합니다.
  2. 높은 수준의 개념을 선호하는 공유 메모리 및 잠금 기반 동기화 프리미티브를 피하면 코드 스레드 간의 동기화가 단순화됩니다.

포인트 # 2의 한 가지 훌륭한 예는 Haskell에서 결정 론적 병렬성 과 비결정론 적 동시성 사이에 분명한 차이점이 있다는 것입니다 . 하스켈에서 Simon Marlow의 훌륭한 책 Parallel and Concurrent Programming을 인용하는 것보다 더 나은 설명은 없습니다 (견적은 1 장 에서 왔습니다 ).

병렬 프로그램을 더 빨리 계산을 수행하는 컴퓨팅 하드웨어 (예를 들어, 여러 개의 프로세서 코어) 다수의 사용이다. 목표는 계산의 다른 부분을 동시에 실행되는 다른 프로세서로 위임하여 더 일찍 답에 도달하는 것입니다.

대조적으로, 동시성 은 다중 제어 스레드가있는 프로그램 구조화 기술입니다. 개념적으로, 제어 스레드는 "동시"실행됩니다. 즉, 사용자는 자신의 효과가 인터리브 된 것을 볼 수 있습니다. 이들이 실제로 동시에 실행되는지 여부는 구현 세부 사항입니다. 동시 프로그램은 인터리브 된 실행 또는 여러 물리적 프로세서를 통해 단일 프로세서에서 실행될 수 있습니다.

이 외에도 Marlow 언급은 결정론 의 차원을 제시합니다 .

결정 론적 프로그래밍 모델 과 비결정론 적 프로그래밍 모델 사이의 관련 차이점이 있습니다. 결정 론적 프로그래밍 모델은 각 프로그램이 하나의 결과 만 제공 할 수있는 모델이지만 비결정론 적 프로그래밍 모델은 실행의 일부 측면에 따라 다른 결과를 가질 수있는 프로그램을 허용합니다. 동시 프로그래밍 모델은 예측할 수없는 시간에 이벤트를 발생시키는 외부 에이전트와 상호 작용해야하므로 반드시 비 결정적입니다. 그러나 비결정론에는 다음과 같은 몇 가지 단점이 있습니다. 프로그램을 테스트하고 추론하기가 상당히 어려워집니다.

병렬 프로그래밍의 경우 가능한 경우 결정적 프로그래밍 모델을 사용하려고합니다. 목표는 응답에 더 빨리 도달하는 것이므로 프로세스에서 프로그램을 디버그하기 어렵게 만들지는 않습니다. 결정적 병렬 프로그래밍은 두 가지 측면에서 최고입니다. 테스트, 디버깅 및 추론은 순차적 프로그램에서 수행 될 수 있지만 프로세서를 추가하면 프로그램이 더 빨리 실행됩니다.

Haskell에서 병렬 처리 및 동시성 기능은 이러한 개념을 중심으로 설계되었습니다. 특히 다른 언어가 하나의 기능 세트로 그룹화되어있는 Haskell은 두 가지로 나뉩니다.

  • 병렬 처리를 위한 결정적 기능 및 라이브러리 .
  • 동시성을 위한 비 결정적 기능 및 라이브러리 .

순수하고 결정론적인 계산 속도를 높이려는 경우 결정적 병렬 처리를 사용하면 작업이 훨씬 쉬워집니다. 종종 다음과 같은 작업을 수행합니다.

  1. 답변 목록을 생성하는 함수를 작성하십시오. 각 목록은 계산 비용이 많이 들지만 서로 크게 의존하지는 않습니다. 이것은 Haskell이므로 목록은 게으르다 . 소비자가 요구할 때까지 요소의 값은 실제로 계산되지 않는다.
  2. 사용 전략 여러 코어에서 병렬로 함수의 결과 목록 '요소를 소비하는 라이브러리를.

실제로 몇 주 전에 내 장난감 프로젝트 프로그램 중 하나를 사용하여이 작업을 수행했습니다 . 프로그램을 병렬화하는 것은 사소한 일이었습니다. 실제로 내가해야 할 핵심은 "이 목록의 요소를 병렬로 계산"(라인 90)이라는 코드를 추가하는 것입니다. 더 비싼 테스트 사례 중 일부.

기존의 잠금 기반 멀티 스레딩 유틸리티를 사용했을 때보 다 프로그램이 더 빠릅니까? 나는 너무 의심합니다. 필자의 경우 깔끔한 점은 너무 적은 비용으로 너무 많이 나가는 것이 었습니다. 내 코드는 아마도 차선책 일 것입니다.하지만 병렬화가 너무 쉬워서 적절히 프로파일 링하고 최적화하는 것보다 훨씬 적은 노력으로 큰 속도를 얻었습니다. 경쟁 조건의 위험이 없습니다. 그리고 기능 프로그래밍이 "더 빠른"프로그램을 작성하는 주된 방법이라고 주장합니다.


2

Haskell에서는 수정 라이브러리를 통해 특별한 수정 가능한 변수를 얻지 않고서는 수정이 사실상 불가능합니다. 대신 함수는 값 (게으르게 계산 됨)과 동시에 필요한 변수를 만들고 더 이상 필요하지 않은 경우 가비지 수집합니다.

수정 변수가 필요한 경우에도 대체로 수정 불가능한 변수와 함께 사용하면됩니다. (하스켈의 또 다른 좋은 점은 잠금을 원자 연산으로 대체하는 STM이지만 기능 프로그래밍에만 해당되는지 확실하지 않습니다.) 일반적으로 프로그램의 한 부분 만 병렬로 만들어야합니다. 성능 측면에서.

이로 인해 Haskell의 병렬 처리가 훨씬 쉬워졌으며 실제로 자동으로 만들기위한 노력이 진행되고 있습니다. 간단한 코드를 위해 병렬 처리와 논리를 분리 할 수도 있습니다.

또한 Haskell에서는 평가 순서가 중요하지 않기 때문에 컴파일러는 평가가 필요한 대기열 항목을 생성하고 사용 가능한 코어로 전송하기 때문에 중요하지 않은 "스레드"를 만들 수 있습니다 실제로 필요할 때까지 스레드가됩니다. 중요하지 않은 평가 순서는 순도의 특성이며 일반적으로 기능적 프로그래밍이 필요합니다.


하스켈 (HaskellWiki)의
동시 읽기 병렬화 "실제 하스켈"의 동시 및 멀티 코어 프로그래밍
Simon Marlow의 하스켈의 병렬 및 동시 프로그래밍


7
grep java this_post. grep scala this_post그리고 grep jvm this_post어떤 결과 : 반환하지
안드레스 F.

4
문제는 모호하다. 제목과 첫 번째 단락에서는 일반적으로 함수형 프로그래밍 대해 , 두 번째와 세 번째 단락에서는 특히 Java와 스칼라 대해 묻습니다 . 특히 스칼라의 핵심 강점 중 하나 는 기능 언어 가 아니라는 사실이기 때문에 불행한 일 입니다. Martin Odersky는 이것을 "포스트 기능"이라고하고, 다른 사람들은 "오브젝트 기능"이라고 부릅니다. "함수 프로그래밍"이라는 용어에는 두 가지 정의가 있습니다. 하나는 "일류 절차 프로그래밍"(LISP에 적용되는 기존의 정의), 다른 ...이다
르그 W MITTAG

2
"참조 적으로 투명하고 순수하며 부작용이없는 기능과 불변의 영구 데이터로 프로그래밍"(매우 엄격하고 새로운 해석). 이 답변은 두 번째 해석에 대해 설명합니다 .a) 첫 번째 해석은 병렬성과 동시성과 전혀 관련이 없습니다. 오늘 (자바 포함) 일류 절차를 가지고 있으며, 다) 영업 이익은 자바와 스칼라의 차이에 대해 물어,하지만 ... 없다
요 르그 W MITTAG

2
정의 # 1에 관한 두 정의 사이에만 정의 # 2.
Jörg W Mittag

평가 내용은 여기에 쓰여진 것처럼 사실이 아닙니다. 기본적으로 런타임은 멀티 스레딩을 전혀 사용하지 않으며 IIRC는 멀티 스레딩을 활성화하더라도 런타임에 병렬로 평가해야 할 사항을 알려 주어야합니다.
큐빅
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.