copy-on-write 시맨틱의 장점


10

COW (Copy-On-Write)의 장점은 무엇입니까? 당연히 개인적 견해는 기대하지 않지만 실제적이고 실용적인 시나리오는 기술적으로나 실질적으로 유익 할 수 있습니다. 그리고 유형의 의미는 &문자 입력을 절약하는 것 이상을 의미 합니다.

명확히하기 위해이 질문은 할당 또는 복사본 구성이 암시 적 얕은 복사본을 만드는 데이터 유형의 컨텍스트에 있지만 수정하면 암시 적 깊은 복사본이 만들어지고 원본 개체 대신 변경 사항이 적용됩니다.

내가 묻는 이유는 COW를 기본 암시 적 동작으로 갖는 장점을 찾지 못하는 것 같습니다. 많은 데이터 유형에 대해 COW를 구현 한 Qt를 사용합니다. 실제적으로 모든 기본 동적 할당 스토리지가 있습니다. 그러나 실제로 사용자에게 어떤 이점이 있습니까?

예를 들면 :

QString s("some text");
QString s1 = s; // now both s and s1 internally use the same resource

qDebug() << s1; // const operation, nothing changes
s1[o] = z; // s1 "detaches" from s, allocates new storage and modifies first character
           // s is still "some text"

이 예에서 COW를 사용하면 무엇을 얻을 수 있습니까?

const 연산을 s1사용하는 것만으로도 여분의 것만 사용할 수 있습니다 s.

값을 변경하려는 경우 COW는 암시 적 공유 및 공유 스토리지에서 분리하기위한 참조 횟수를 증가시키는 (최소한이지만) 비용으로 첫 번째 비정상 작업까지 자원 복사 만 지연시킵니다. COW와 관련된 모든 오버 헤드가 무의미한 것처럼 보입니다.

매개 변수 전달 컨텍스트와 크게 다르지 않습니다. 값을 수정하지 않으려는 경우 const 참조로 전달하십시오. 수정하려는 경우 수정하지 않으려는 경우 암시 적 깊은 복사본을 만드십시오. 원본 객체를 수정하거나 수정하려는 경우 참조로 전달합니다. 다시 말하지만 COW는 아무 것도 달성하지 못하는 불필요한 오버 헤드처럼 보이며, 변경 사항이 원본 객체에서 분리되므로 원할 경우에도 원래 값을 수정할 수 없다는 제한 사항 만 추가합니다.

따라서 COW에 대해 알고 있는지 또는 알지 못하는지 여부에 따라 의도가 불분명하고 불필요한 오버 헤드가있는 코드가 생성되거나 예상과 일치하지 않고 머리를 긁을 수있는 완전히 혼란스러운 동작이 발생할 수 있습니다.

나에게 불필요한 딥 카피를 피하든, 만들려는지보다 더 효율적이고 읽기 쉬운 솔루션이있는 것 같습니다. 그렇다면 COW의 실질적인 이점은 어디에 있습니까? 나는 대중적이고 강력한 프레임 워크에서 사용되기 때문에 어떤 이점이 있어야한다고 생각합니다.

또한 내가 읽은 것으로부터 CW 표준 라이브러리에서는 COW가 명시 적으로 금지되어 있습니다. 내가 볼 수있는 죄가 그것과 관련이 있는지 알지 못하지만 어떤 식 으로든 이에 대한 이유가 있어야합니다.

답변:


15

쓰기시 복사는 개체의 복사본을 만들고 수정하지 않는 경우에 사용됩니다. 그러한 상황에서는 자체 비용을 지불합니다.

언급했듯이 const 객체를 전달할 수 있으며 많은 경우 충분합니다. 그러나 const는 호출자가 변경할 수 없다는 것을 보증합니다 ( const_cast물론 그들이 아닌 한 ). 멀티 스레딩 사례는 처리하지 않으며 콜백이있는 경우 (원래 객체를 변경시킬 수 있음)는 처리하지 않습니다. COW 객체를 가치별로 전달하면 API 사용자가 아닌 API 개발자에게 이러한 세부 정보를 관리하는 데 어려움이 있습니다.

C + 11에 대한 새로운 규칙 std::string은 특히 COW를 금지 합니다. 백업 버퍼가 분리 된 경우 문자열의 반복자를 무효화해야합니다. 반복자가 char*( string*및 색인과 반대로) 구현 된 경우이 반복자는 더 이상 유효하지 않습니다. C ++ 커뮤니티는 반복자가 무효화 될 수있는 빈도를 결정 operator[]해야 했으며 결정은 그러한 경우 중 하나가 아니어야합니다. operator[]에 a를 std::string반환하면 char&수정할 수 있습니다. 따라서 operator[]반복자를 무효화하여 문자열을 분리해야합니다. 이것은 가난한 무역로 간주하고, 같은 기능을 달리 한 end()cend()의 CONST 버전을 요청하는 방법은 없다 operator[]문자열을 주조 형 구조 짧은. ( 관련 ).

COW는 여전히 살아 있으며 STL 외부에 있습니다. 특히, 나는 API 사용자가 매우 가벼운 객체로 보이는 것 뒤에 무거운 객체가 있다고 기대하는 것이 불합리한 경우에 매우 유용하다는 것을 알았습니다. 이러한 구현 세부 사항에 대해 걱정할 필요가 없도록 백그라운드에서 COW를 사용하고 싶을 수 있습니다.


여러 스레드에서 동일한 문자열을 변경하면 반복자를 사용하든 []연산자 를 사용하든 관계없이 매우 나쁜 디자인처럼 보입니다 . 따라서 COW는 나쁜 디자인을 가능하게합니다. 많은 이점은 들리지 않습니다.) 마지막 단락의 요점은 타당 해 보이지만, 나는 내 자신의 암묵적 행동의 팬이 아닙니다. 사람들은 그것을 당연한 것으로 여기고, 코드가 예상대로 작동하지 않는 이유를 파악하는 데 어려움을 겪고 암시적인 동작 뒤에 숨겨진 것이 무엇인지 확인할 때까지 계속 궁금해합니다.
dtech

사용 포인트는 const_castconst 참조로 전달하는 것처럼 쉽게 COW를 깰 수있는 것처럼 보입니다. 예를 들어, - 를 QString::constData()반환 하고 COW가 축소되면 원본 객체의 데이터가 변경됩니다. const QChar *const_cast
dtech

COW에서 데이터를 반환 할 수있는 경우 분리하기 전에 분리하거나 여전히 COW를 인식하는 형식으로 데이터를 반환해야합니다 ( char*확실히 알 수 없음). 암묵적인 행동에 관해서는, 당신이 옳다고 생각합니다. 그것에 문제가 있습니다. API 디자인은 두 극단 사이의 일정한 균형입니다. 너무 암묵적이며 사람들은 마치 실제로는 스펙의 일부인 것처럼 특수한 행동에 의존하기 시작합니다. 너무 명시 적이 지 않으면 API가 너무 중요하지 않아서 실제로 중요하지 않았고 갑자기 API 사양에 기록되는 기본 정보를 너무 많이 노출 할 수 있습니다.
Cort Ammon

string컴파일러 디자이너가 const-reference를 사용하지 않고 문자열을 복사하고 있음을 알았 기 때문에 클래스에 COW 동작이 있다고 생각합니다 . COW를 추가하면이 사례를 최적화하고 더 많은 사람들을 행복하게 만들 수 있습니다 (C ++ 11까지는 합법적이었습니다). 나는 그들의 위치를 ​​고맙게 생각합니다. 항상 const 참조로 문자열을 전달하는 동안 가독성을 떨어 뜨리는 구문 쓰레기를 모두 보았습니다. 나는 const std::shared_ptr<const std::string>&올바른 의미를 포착하기 위해 글쓰기를 싫어 합니다!
Cort Ammon

5

문자열에 대한 일반적인 경우는 종종 작은 문자열이므로 COW의 오버 헤드는 단순히 작은 문자열을 복사하는 비용보다 훨씬 큰 경향이 있기 때문에 문자열 및 이와 같은 경우보다 일반적인 사용 사례를 비관적으로 생각하는 것처럼 보입니다. 작은 버퍼 최적화는 문자열 사본 대신 힙 할당을 피하기 위해 훨씬 더 의미가 있습니다.

그러나 안드로이드와 같이 heftier 객체가 있고 복사하고 사이버네틱스 팔을 교체하려는 경우 COW는 전체 안드로이드를 딥 카피 할 필요없이 가변 구문을 유지하는 방법으로 상당히 합리적입니다. 사본에 고유 한 팔을 제공하십시오. 이 시점에서 영구적 인 데이터 구조로 변경할 수없는 것은 우수 할 수 있지만 개별 안드로이드 부분에 적용되는 "부분 COW"는 이러한 경우에 합리적입니다.

이 경우 안드로이드의 두 복사본은 같은 몸통, 다리, 발, 머리, 목, 어깨, 골반 등을 공유 / 인스턴스하게됩니다. 그들 사이에 다르고 공유되지 않는 유일한 데이터는 만들어진 팔입니다. 팔을 덮어 쓰는 두 번째 안드로이드에 고유합니다.


이것은 모두 좋지만 COW를 요구하지는 않지만 여전히 많은 유해한 암시를받습니다. 또한 단점이 있습니다. 객체 인스턴스를 만들고 싶을 수도 있습니다. 타입 인스턴스를 의미하는 것이 아니라 객체를 인스턴스로 복사하는 것이므로 소스 객체를 수정하면 사본도 업데이트됩니다. COW는 "공유 된"객체에 대한 변경이 분리 될 때 그 가능성을 배제합니다.
dtech

정확성 IMO는 암시적인 행동이 아니라 달성하기가 쉽지 않습니다. 정확성의 좋은 예는 CONST 정확성입니다. 명확하고 모호하거나 보이지 않는 부작용의 여지가 없습니다. 이와 같은 "쉬운"및 자동 기능을 사용하면 일이 어떻게 작동하는지에 대한 이해 수준이 높아지지 않습니다. 이는 전반적인 생산성에 중요 할뿐만 아니라 바람직하지 않은 행동의 가능성을 거의 제거합니다. . COW로 내재적으로 가능해진 모든 것은 명시 적으로 쉽게 달성 할 수 있으며 더 명확합니다.
dtech

내 질문은 내가 작업중 인 언어로 기본적으로 COW를 제공할지 여부에 대한 딜레마에 동기를 부여했습니다. 찬반 양론에 가중치를 부여한 후, 나는 기본적으로 그것을 갖지 않기로 결정했지만 새로운 유형이나 기존 유형 모두에 적용 할 수있는 수정 자입니다. 두 세계에서 가장 좋은 것처럼 보이지만 COW를 원한다고 명시 할 때 암시 적 암시를 가질 수 있습니다.
dtech

@ddriver 우리가 가진 것은 노드 패러다임의 프로그래밍 언어와 유사하다. 단순히 노드 종류의 사용 값 의미론과 참조 유형 의미론이 없다는 점을 제외하고 ( C ++ 11에서 의미론 을 std::vector<std::string>갖기 전에 emplace_back의미 가 있기도하다 ) . 그러나 기본적으로 인스 턴싱을 사용하고 있습니다. 노드 시스템은 데이터를 수정하거나 수정하지 않을 수 있습니다. 우리는 입력으로 아무것도하지 않고 사본을 출력하는 통과 노드와 같은 것을 가지고 있습니다 (그들의 프로그램의 사용자 조직을 위해 있습니다). 이 경우 복잡한 유형에 대해 모든 데이터가 얕게 복사됩니다.

@ddriver 쓰기시 복사는 사실상 "변경시 암시 적으로 고유 한 인스턴스 만들기" 종류의 복사 프로세스입니다. 원본을 수정할 수 없습니다. 객체 A가 복사되고 객체 에 아무것도 수행되지 않으면 B메쉬와 같은 복잡한 데이터 유형에 대한 저렴한 얕은 복사입니다. 이제 수정하면 수정 B한 데이터가 BCOW를 통해 고유 해지지 만 A일부 원자 기준 카운트를 제외하고는 변경되지 않습니다.
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.