여기에 좋은 예가 있지만 불변성이 많은 도움이되는 개인적인 것들로 뛰어 들기를 원했습니다. 필자의 경우 필자는 중복 된 읽기 및 쓰기와 함께 병렬로 코드를 자신있게 실행할 수 있고 경쟁 조건에 대해 걱정할 필요가 없다는 희망을 가지고 불변의 동시 데이터 구조를 설계하기 시작했습니다. John Carmack이 그런 아이디어에 대해 이야기 할 때 영감을 준 이야기가있었습니다. 다음과 같이 구현하는 것은 매우 기본적인 구조이며 매우 사소합니다.
물론 일정 시간 내에 요소를 제거하고 회수 가능한 구멍을 남기고 주어진 불변 인스턴스에 대해 비어 있고 잠재적으로 해제되면 블록이 무시되도록하는 것처럼 종과 휘파람이 몇 개 더 있습니다. 그러나 기본적으로 구조를 수정하려면 "일시적인"버전을 수정하고 변경 사항을 원자 적으로 적용하여 이전 버전에 닿지 않는 새로운 불변 사본을 얻습니다. 새로운 버전은 블록의 새 사본 만 만듭니다. 다른 사람을 얕게 복사하고 참조하는 동안 고유해야합니다.
그러나, 나는 그것을 찾지 못했습니다 그멀티 스레딩 목적에 유용합니다. 결국, 물리 시스템이 플레이어가 세계에서 요소를 움직이려고 시도하는 동안 물리를 동시에 적용하는 개념적 문제가 여전히 있습니다. 플레이어가 변형 한 것과 물리 시스템이 변형 한 변형 된 데이터의 어떤 불변 사본이 있습니까? 그래서 나는 똑똑한 방식으로 잠그고 버퍼의 동일한 섹션에 겹치는 읽기 및 쓰기를 금지하여 변경 스레드를 피하는 것을 제외 하고는이 기본 개념 문제에 대한 훌륭하고 간단한 해결책을 찾지 못했습니다. John Carmack이 그의 게임에서 어떻게 해결할 수 있을지 알아 낸 것 같습니다. 적어도 그는 벌레 차를 열지 않고도 해결책을 거의 볼 수있는 것처럼 그것에 대해 이야기합니다. 나는 그런 점에서 그에게까지 도달하지 못했습니다. 불변 주변의 모든 것을 병렬화하려고하면 끝없는 디자인 질문이 있습니다. 나는 그의 노력의 대부분이 그가 튀어 나온 아이디어로 시작했기 때문에 그의 두뇌를 따기 위해 하루를 보낼 수 있기를 바랍니다.
그럼에도 불구하고, 나는 다른 영역 에서이 불변의 데이터 구조의 엄청난 가치를 발견했습니다 . 나는 심지어 그것을 정말로 이상한 이미지를 저장하기 위해 지금 사용하고 무작위 액세스는 더 많은 지시 ( and
포인터 간접 레이어와 함께 오른쪽 시프트와 비트 단위 )를 필요로하지만, 아래의 이점을 다룰 것입니다.
시스템 취소
내가이 혜택을 얻는 가장 즉각적인 장소 중 하나는 실행 취소 시스템이었습니다. 실행 취소 시스템 코드는 내가 작업했던 제품뿐만 아니라 경쟁 제품 (실행 취소 시스템도 결함이 있음)에서 가장 오류가 발생하기 쉬운 영역 중 하나 (비주얼 FX 산업) 중 하나였습니다. 실행 취소 및 재실행에 대해 걱정해야 할 데이터 유형 (속성 시스템, 메시 데이터 변경, 서로 교환하는 것과 같이 속성 기반이 아닌 셰이더 변경, 자식의 부모 변경, 이미지 / 텍스처 변경, 등 등).
따라서 실행 취소 코드의 양은 엄청 났으며, 실행 취소 시스템이 상태 변경을 기록해야하는 시스템을 구현하는 코드의 양과 비교할 때 종종 많았습니다. 이 데이터 구조에 기대어 실행 취소 시스템을 다음과 같이 만들 수있었습니다.
on user operation:
copy entire application state to undo entry
perform operation
on undo/redo:
swap application state with undo entry
일반적으로 위의 코드는 장면 데이터가 기가 바이트에 걸쳐 전체를 복사 할 때 엄청나게 비효율적입니다. 그러나이 데이터 구조는 변경되지 않은 내용 만 얕게 복사하기 때문에 실제로 전체 응용 프로그램 상태의 변경 불가능한 복사본을 저장할 수있을 정도로 저렴했습니다. 이제 위 코드처럼 쉽게 실행 취소 시스템을 구현하고 변경 불가능한 데이터 구조를 사용하여 애플리케이션 상태의 변경되지 않은 부분을보다 저렴하고 저렴하게 복사 할 수 있습니다. 이 데이터 구조를 사용하기 시작한 이후, 모든 개인 프로젝트는이 간단한 패턴을 사용하여 시스템을 취소합니다.
이제 여기에는 여전히 약간의 오버 헤드가 있습니다. 마지막으로 측정 한 내용을 변경하지 않고 전체 응용 프로그램 상태를 얕게 복사하기 위해 약 10KB가 측정되었습니다. 장면이 계층 구조로 정렬되어 있기 때문에 장면 복잡성과 무관합니다. 따라서 루트 아래에 아무것도 없으면 루트 만 아래로 내려 가지 않고 얕게 복사됩니다). 델타 만 저장하는 실행 취소 시스템에 필요한 0 바이트와는 거리가 멀습니다. 그러나 작업 당 10 킬로바이트의 실행 취소 오버 헤드는 여전히 100 개의 사용자 작업 당 메가 바이트에 불과합니다. 또한 필요할 경우 앞으로도 계속해서 더 이상 스쿼시 할 수 있습니다.
예외 안전
복잡한 응용 프로그램의 예외 안전은 사소한 문제가 아닙니다. 그러나 응용 프로그램 상태가 변경 불가능하고 임시 객체 만 사용하여 원자 변경 트랜잭션을 커밋하려고하면 코드의 일부가 throw되면 새로운 불변 복사를 제공하기 전에 일시적이 사라지기 때문에 본질적으로 예외 안전합니다 . 따라서 복잡한 C ++ 코드베이스에서 항상 찾아낸 가장 어려운 것들 중 하나를 사소하게 만듭니다.
너무 많은 사람들이 종종 C ++에서 RAII 준수 리소스를 사용하고 예외 안전에 충분하다고 생각합니다. 함수가 일반적으로 해당 범위의 로컬 상태 이외의 상태에 부작용을 일으킬 수 있기 때문에 종종 그렇지 않습니다. 이러한 경우 일반적으로 스코프 가드 및 정교한 롤백 로직을 다루어야합니다. 이 데이터 구조로 인해 함수가 부작용을 일으키지 않기 때문에 종종 신경 쓰지 않아도됩니다. 그들은 응용 프로그램의 상태를 변환하는 대신 변환 된 불변의 응용 프로그램 상태 사본을 반환합니다.
비파괴 편집
비파괴적인 편집은 기본적으로 원래 사용자의 데이터를 건드리지 않고 (입력 데이터와 입력 데이터를 건드리지 않고 출력 데이터 만) 레이어링 / 스태킹 / 연결 작업입니다. 일반적으로 Photoshop과 같은 간단한 이미지 응용 프로그램으로 구현하는 것은 쉽지 않으며 많은 작업이 전체 이미지의 모든 픽셀을 변환하기를 원할 수 있기 때문에이 데이터 구조의 이점은 그리 크지 않을 수 있습니다.
그러나 비파괴 메시 편집의 경우 많은 작업에서 종종 메시의 일부만 변형하려고합니다. 하나의 작업으로 정점을 여기로 옮기고 싶을 수도 있습니다. 다른 하나는 일부 다각형을 세분화하려고 할 수도 있습니다. 불변의 데이터 구조는 작은 부분이 변경된 새로운 버전의 메쉬를 반환하기 위해 전체 메쉬의 전체 사본을 만들 필요가 없도록하는 데 도움이됩니다.
부작용 최소화
이러한 구조를 사용하면 성능에 큰 영향을 미치지 않으면 서 부작용을 최소화하는 기능을 쉽게 작성할 수 있습니다. 요즘 약간의 낭비로 보일지라도 부작용을 일으키지 않고 값으로 전체 불변의 데이터 구조를 반환하는 점점 더 많은 함수를 작성하는 것을 발견했습니다.
예를 들어, 일반적으로 여러 위치를 변형하려는 유혹은 매트릭스와 객체 목록을 수용하고 변경 가능한 방식으로 객체를 변형하는 것일 수 있습니다. 요즘 나는 새로운 객체 목록을 반환한다는 것을 알았습니다.
시스템에 부작용을 일으키지 않는 이와 같은 기능이 더 많으면 정확성을 쉽게 추론하고 정확성을 테스트 할 수 있습니다.
싸구려 사본의 장점
어쨌든, 이것은 불변의 데이터 구조 (또는 영구적 인 데이터 구조)에서 가장 많이 사용되는 영역입니다. 또한 처음에는 약간 이상해 불변 트리와 불변 링크 목록 및 불변 해시 테이블을 만들었지 만 시간이 지남에 따라 그다지 많이 사용하지는 못했습니다. 나는 위의 다이어그램에서 chunky 불변 배열 배열 컨테이너를 주로 사용한다는 것을 알았습니다.
나는 여전히 변형 가능 코드로 작업하는 많은 코드를 가지고 있지만 (적어도 저수준 코드에는 실용적 필요가 있음) 주요 응용 프로그램 상태는 불변의 계층 구조이며 불변의 장면에서 불변의 구성 요소로 드릴 다운됩니다. 더 저렴한 구성 요소 중 일부는 여전히 전체로 복사되지만 메쉬 및 이미지와 같은 가장 비싼 구성 요소는 불변 구조를 사용하여 변환해야 할 부분의 일부만 저렴한 복사본을 허용합니다.
ConcurrentModificationException
일반적으로 동일한 스레드foreach
가 동일한 컬렉션 의 루프 본문에서 동일한 스레드의 컬렉션을 변경함으로써 발생 하는 적절한 이름 을 찾으십시오 .