최신 프로세서에서 비트 단위 연산만큼 빠른 추가가 필요한 이유는 무엇입니까?


72

현대 프로세서에서는 비트 단위 연산이 32 비트 또는 64 비트에서 병렬로 작동 할 수 있기 때문에 비트 단위 연산이 너무 빠르다는 점을 알고 있으므로 비트 단위 연산은 한 번의 클럭 주기만 걸립니다. 그러나 추가는 하나 이상의 비트 연산으로 구성되는 복잡한 작업이므로 3-4 배 느려질 것이라고 생각했습니다. 간단한 벤치 마크 후에 비트 단위 연산 (XOR, OR 및 AND 등)만큼 정확하게 추가하는 것이 놀랍습니다. 누구든지 이것에 빛을 비출 수 있습니까?




1
네, 테스트에서도 곱셈이 매우 빨랐습니다. 분할보다 약 2 배 더 느린 반면 분할은 약 30 배 (!) 배 느 렸습니다.
SoloNasus

최첨단 병렬 접두사 트리 가산기의 간단한 개요 : David Harris의 병렬 접두사 네트워크 분류법 : pages.hmc.edu/harris/research/taxonomy.pdf
Franki

더 정교화 : 박사 준첸
Franki

답변:


104

CPU 설계자가 빠른 속도를내는 데 필요한 회로를 배치했기 때문에 추가가 빠릅니다. 비트 단위 연산보다 훨씬 더 많은 게이트가 필요하지만 CPU 디자이너가 그 가치가 있다고 판단 할 정도로 자주 발생합니다. https://en.wikipedia.org/wiki/Adder_(electronics)를 참조 하십시오 .

두 CPU 모두 단일 CPU 사이클 내에서 충분히 빠르게 실행될 수 있습니다. 그것들은 동등하게 빠르지 않습니다. 또한 비트 연산보다 더 많은 게이트와 대기 시간이 필요합니다. 그러나 프로세서가 한 클록 사이클에서 처리 할 수있을만큼 빠릅니다. 명령어 디코딩 및 제어 로직에 대한 명령어 당 지연 시간 오버 헤드가 있으며, 이에 대한 지연 시간은 비트 연산을 수행하기위한 지연 시간보다 상당히 길기 때문에 둘 사이의 차이는 그 오버 헤드에 의해 엉켜 버립니다. 프로그래머의 대답Paul92의 대답 은 이러한 효과를 잘 설명합니다.


의견은 긴 토론을위한 것이 아닙니다. 이 대화는 채팅 으로 이동 되었습니다 .
DW

38

몇 가지 측면이 있습니다.

  • 비트 단위 연산 및 추가의 상대 비용. 순진 가산기는 단어의 폭에 선형 적으로 의존하는 게이트 깊이를 가질 것이다. 게이트를 기준으로 비용이 많이 드는 대체 방법이 있습니다.이 방법은 깊이를 줄입니다 (IIRC 깊이는 단어의 너비에 대해 로그에 의존 함). 다른 사람들은 그러한 기술에 대한 참고 자료를 제공했지만 지연을 추가하는 제어 논리가 필요하기 때문에 운영 비용을 고려할 때보 다 차이가 덜 중요하다는 점을 지적합니다.

  • 그런 다음 프로세서가 일반적으로 클럭킹된다는 사실이 있습니다 (저는 일부 연구 또는 특수 목적의 비 클럭킹 디자인에 대해 알고 있지만 일부는 상업적으로 이용 가능하지는 않습니다). 즉, 작업 속도에 상관없이 클록 사이클의 정수 배가 필요합니다.

  • 마지막으로 마이크로 아키텍처 고려 사항이 있습니다. 원하는 것을 측정 하시겠습니까? 요즘 프로세서는 파이프 라인 방식이고 다중 스칼라 방식이며 순서가 잘못된 실행 방식과 그 밖의 방식이 있습니다. 이는 여러 완료 단계에서 동시에 여러 명령을 실행할 수 있음을 의미합니다. 측정에 의해 다른 작업보다 더 많은 시간이 걸린다는 것을 측정으로 표시하려면 이러한 차이를 숨기는 것이 목표이므로 이러한 측면을 고려해야합니다. 독립 데이터를 사용할 때 덧셈 및 비트 단위 연산에 대해 동일한 처리량을 가질 수 있지만 지연 시간 측정 또는 연산 간 종속성 도입이 다르게 표시 될 수 있습니다. 또한 측정의 병목 현상이 메모리 액세스가 아닌 실행 중인지 확인해야합니다.


6
+1. 예, 대부분의 프로세서는 클럭킹되어 있지만 몇 개의 클럭리스 CPU 는 시중에서 구입할 수 있습니다.
David Cary

2
다른 가능성은 프로세서가 64 비트 레지스터를 하나의 16 비트 조각과 세 개의 17 비트 조각으로 저장할 수 있으며 각 조각의 추가 비트는 아래에서 지연된 캐리를 보유하는 것입니다. 비트 단위 작업이나 상점을 추가 한 추가는 캐리를 전파하기 위해 1-2 회의 추가주기가 필요할 수 있지만 추가가 뒤 따르는 추가는 필요하지 않습니다. 또한, "스토어"의 경우, 추가적인 전파 시간은 스토어의 성능을 지연시킬 수 있지만, 코드를 "기다릴"필요는 없다.
supercat

3
@supercat Pentium 4는 2 배속 (나머지 프로세서와 관련이있는) ALU를 사용하여 16 비트 또는 32 비트의 하위 비트가 상반신 비트의 절반주기 전의 후속 사이클을 위해 준비된 ALU를 사용하여 이와 같은 작업을 수행했습니다.
Jeffrey Bosboom 21

2
원하는 것을 측정 하시겠습니까? 이 경우 측정 결과에 따른 OP의 결론은 대다수의 CPU에 맞습니다. 수퍼 스칼라 CPU는 모든 실행 포트에 추가 장치가 있고 부울은 구현하기에 너무 저렴하여 (트랜지스터 수로) 모든 포트에도 존재합니다. 따라서 add 및 boolean은 거의 항상 동일한 처리량을 갖습니다 (예 : Intel Haswell에서 클록 당 4).
Peter Cordes 17 년

2
SIMD 정수 추가는 일반적으로 대기 시간이 동일하더라도 SIMD 부울보다 처리량이 낮습니다. PentiumII에서 Broadwell까지의 Intel CPU paddw는 클록 당 2에서 벡터 -INT 추가 (예 :) 만 실행할 수 있지만 pand클록 당 3 에서는 부울 ( 예 :)을 실행할 수 있습니다 . (스카이 레이크는 세 벡터 실행 포트 벡터 가산기를 둔다.)
피터 코르

24

CPU는 주기적으로 작동합니다. 각주기마다 무언가가 발생합니다. 일반적으로 명령을 실행하는 데 더 많은주기가 필요하지만 여러 명령이 동시에 다른 상태로 실행됩니다.

예를 들어, 간단한 프로세서에는 각 명령에 대해 3 단계, 즉 페치, 실행 및 저장이있을 수 있습니다. 언제든지 3 개의 명령어가 처리되고 있습니다. 하나는 가져오고 있고, 하나는 실행 중이며 다른 하나는 결과를 저장합니다. 이것을 파이프 라인이라고하며이 예에서는 3 단계로 구성됩니다. 최신 프로세서에는 15 단계가 넘는 파이프 라인이 있습니다. 그러나 대부분의 산술 연산뿐만 아니라 추가는 일반적으로 한 단계에서 실행됩니다 (프로세서 자체에 대한 것이 아니라 ALU에 의해 2 개의 숫자를 추가하는 연산에 대해 말하고 있습니다-프로세서 아키텍처에 따라 명령에 필요할 수 있습니다 메모리에서 인수를 가져오고, 조건을 수행하고, 결과를 메모리에 저장하기위한 더 많은주기).

주기는 가장 긴 임계 경로에 의해 결정됩니다. 기본적으로 pipline의 일부 단계를 완료하는 데 필요한 가장 긴 시간입니다. CPU 속도를 높이려면 임계 경로를 최적화해야합니다. 임계 경로 자체를 줄일 수없는 경우 파이프 라인의 2 단계로 분할 할 수 있으며 이제 거의 두 배의 주파수에서 CPU를 클럭킹 할 수 있습니다 (이를 수행하는 데 방해가되는 다른 중요한 경로가 없다고 가정 할 경우). ). 그러나 여기에는 오버 헤드가 발생합니다. 파이프 라인 단계 사이에 레지스터를 삽입해야합니다. 이는 실제로 2 배속 (레지스터가 데이터를 저장하는 데 시간이 필요함)을 얻지 못하고 전체 설계를 복잡하게한다는 것을 의미합니다.

추가 (예 : 캐리 룩 어더 (carry lookahead adders))를 수행하는 데있어 매우 효율적인 방법이 이미 존재하며, 추가는 프로세서 속도에 중요한 경로가 아니므로 여러 사이클로 분할하는 것은 의미가 없습니다.

또한 하드웨어가 복잡해 보일 수 있지만 하드웨어에서는 매우 빠르게 병렬 작업을 수행 할 수 있습니다.


3
더 긴 파이프 라인으로 인한 큰 오버 헤드는 분기 오해로부터 복구하기위한 더 많은주기입니다! 현재 단계들 사이에서 데이터를 버퍼링하기위한 트랜지스터의 소비는 미미하다. 단순한 파이프 라인 CPU조차도 실제로 실행되는 명령어보다 먼저 페치 / 디코딩해야합니다. CPU가 브랜치가 예상 한 것과 다른 방식으로 진행 (또는 다른 오해)하여 프론트 엔드가 잘못된 코드로 작업하고 있음을 발견하면 해당 작업을 버리고 올바른 명령부터 시작해야합니다. 비행 중에 많은 기능을 수행 할 수있는 수퍼 스칼라 비 순차적 CPU 만 있으면 상황이 악화됩니다.
Peter Cordes 17 년

12

프로세서는 클럭킹되므로 일부 명령어가 다른 명령어보다 명확하게 더 빠르게 수행 될 수 있더라도 동일한 횟수의 사이클이 걸릴 수 있습니다.

레지스터와 실행 장치간에 데이터를 전송하는 데 필요한 회로가 가산기보다 훨씬 더 복잡하다는 것을 알 수 있습니다.

간단한 MOV (register to register) 명령어는 비트 논리보다 계산이 훨씬 적지 만 MOV와 ADD는 일반적으로 한주기가 걸립니다. MOV를 두 배 빠르게 만들 수 있다면 CPU는 두 배 빠르게 클럭되고 ADD는 두 사이클이됩니다.


의견은 긴 토론을위한 것이 아닙니다. 이 대화는 채팅 으로 이동 되었습니다 .
Gilles

1
토론 요약 : 일부 비 순차적 CPU는 효과적으로 레지스터 이름 변경을 통해 MOV를 처리하며 대기 시간은 거의 없습니다. x86의 MOV가 실제로 "무료"일 수 있습니까?를 참조하십시오 . 왜 이것을 전혀 재현 할 수 없습니까? MOV의 실제 비용에 대한 자세한 내용은
Peter Cordes

12

64 비트 누산기를 통해 캐리 비트가 리플 될 때까지 기다리지 않도록 추가하는 것이 중요 합니다. 즉, 캐리-캐리어 헤드 가산기 (carry-lookahead adder) 이며 기본적으로 8 비트 CPU (및 ALU)의 일부 이상입니다. 실제로 최신 프로세서는 전체 곱셈에 더 많은 실행 시간이 필요하지 않은 경향이 있습니다. carry-lookahead는 실제로 프로세서 디자이너의 도구 상자에서 실제로 오래되고 비교적 저렴한 도구입니다.


정수 곱셈은 x86의 ADD보다 대기 시간이 길고 처리량이 낮습니다. 그러나 빠른 승수를 구축하는 데 필요한 가산기 수를 고려하면 놀랍게도 빠릅니다. 예를 들어 Nehalem 이후의 Intel 및 Ryzen 이후의 AMD에서 8/16/32/64 비트 스칼라 정수 곱셈은 3 사이클 대기 시간이며 1c 처리량 당 1 (완전히 파이프 라인 된 실행 단위). 이것은 클럭 당 3 또는 4의 ADD 처리량에 비해 빠르지 만 Intel Pentium P5의 9주기 IMUL 대기 시간과 비교하면 놀랍습니다. SIMD와 비슷한 점은 vector-int multiply가 추가보다 지연 시간이 길고 처리량이 적지 만 여전히 빠릅니다.
Peter Cordes

예, 곱하기는 지금보다 다른 명령에 비해 훨씬 비쌉니다. 2 개 이상의 명령어를 사용하여 비용을 피하는 것은 일반적으로 가치가 없으며 때로는 2 개 명령어로 대체 할 가치도 없습니다 (예 : shift + add lea명령어).
Peter Cordes 17 년

9

비트 연산보다 더 많은 사이클을 수행하는 프로세서를 찾기가 어려울 것이라고 생각합니다. 부분적으로 대부분의 프로세서는 단순히 프로그램 카운터를 증가시키기 위해 명령주기마다 하나 이상의 추가를 수행해야하기 때문입니다. 단순한 비트 단위 연산이 그다지 유용한 것은 아닙니다.

(클럭 사이클이 아닌 명령 사이클-예를 들어 6502는 파이프 라인되지 않고 명령 캐시가 없기 때문에 명령 당 최소 2 클럭 사이클을 취함)

당신이 놓칠 수있는 실제 개념은 중요한 경로 의 개념입니다 . 칩 내에서, 한주기 내에서 수행 될 수있는 가장 긴 동작은 하드웨어 레벨에서 칩의 클럭 속도를 나타냅니다.

이에 대한 예외는 (아주 거의 사용되지 않고 상용화되지 않은) 비동기 로직이며, 로직 전파 시간, 디바이스 온도 등에 따라 다른 속도로 실제로 실행됩니다.


사용자가 제어 할 수있는 비트 단위 연산은 아니지만 8086의 일부 명령어 (예 : 인터럽트 플래그 지우기 )는 정수를 추가하는 것보다 사이클이 덜 소요되었습니다. 보다 추상적으로, 모든 명령어가 한 단어 크기 인 RISC 시스템은 PC에 간단한 이진 카운터를 사용할 수 있으며, 이는 범용 가산기보다 훨씬 빠른 회로입니다.
Mark

피연산자 중 하나가 작기 때문에 (명령 크기 또는 크기 제한이있는 상대 점프 오프셋) 프로그램 카운터의 추가는 추가 산술 명령어에 비해 매우 간단한 경향이 있습니다.
Ben Voigt

6502가 파이프 라인되었습니다. 이전 명령의 마지막주기 동안 다음 명령의 첫 번째 바이트를 읽습니다. 그렇지 않으면, 페치 / 디코딩 / 실행은 적어도 3주기 일 것이다.
gnasher729

8

게이트 레벨에서는 추가 작업에 더 많은 작업이 필요하므로 시간이 더 오래 걸린다는 것이 맞습니다. 그러나 그 비용은 중요하지 않은 정도로 사소합니다.

최신 프로세서가 작동합니다. 이 클럭 속도의 배수를 제외하고는 명령을 수행 할 수 없습니다. 비트율 연산의 속도를 최대화하기 위해 클럭 속도가 더 높아지면 추가에 적어도 2주기를 소비해야합니다. 이 시간의 대부분은 실제로 2주기 분량의 시간이 필요하지 않기 때문에 기다리는 것입니다. 당신은 1.1 (또는 그와 같은 숫자) 만 필요했습니다. 이제 칩이 시장의 다른 모든 사용자보다 느리게 추가됩니다.

더구나, 비트 단위 연산을 추가하거나 수행하는 단순한 동작은주기 동안 진행되는 작업의 일부에 지나지 않습니다. 사이클 내에서 명령어를 페치 / 디코딩 할 수 있어야합니다. 주기 내에서 캐시 작업을 수행 할 수 있어야합니다. 다른 많은 것들이 간단한 추가 또는 비트 단위 연산과 동일한 시간 단위로 진행되고 있습니다.

물론 해결책은 매우 깊은 파이프 라인을 개발하여 이러한 작업을 비트 단위 작업으로 정의 된 작은 사이클 시간에 맞는 작은 부품으로 나누는 것입니다. 펜티엄 4는 이러한 심오한 파이프 라인 용어로 사고의 한계를 잘 보여주었습니다. 모든 종류의 문제가 발생합니다. 특히 어떤 분기를 취해야하는지 파악할 데이터가 있으면 파이프 라인을 플러시해야하기 때문에 분기가 매우 어려워집니다.


7

최신 프로세서가 클럭킹 됨 : 모든 작업에는 일정한 수의 클럭 사이클이 필요합니다. 프로세서 설계자는 클럭 사이클의 길이를 결정합니다. 두 가지 고려 사항이 있습니다. 하나는 하드웨어 속도 (예 : 단일 NAND- 게이트의 지연으로 측정)입니다. 이는 사용 된 기술 및 속도 대 전력 사용량과 같은 트레이드 오프에 따라 다릅니다. 프로세서 설계와 무관합니다. 둘째, 설계자들은 클록 사이클의 길이가 단일 NAND- 게이트의 n 지연과 동일하다고 결정한다. 여기서 n은 10, 30 또는 다른 값일 수있다.

이 선택 n은 한 번의 주기로 처리 할 수있는 복잡한 작업의 수를 제한합니다. 16 개에서는 수행 할 수 있지만 15 개의 NAND 지연에서는 수행 할 수없는 작업이 있습니다. 따라서 n = 16을 선택하면 사이클에서 이러한 작업을 수행 할 수 있으며 n = 15를 선택하면 작업을 수행 할 수 없습니다.

설계자들은 많은 중요한 작업이 한 번 또는 두 번 또는 세 번의 주기로 수행 될 수 있도록 n을 선택할 것입니다. n은 로컬에서 최적으로 선택됩니다. n을 n-1로 바꾸면 대부분의 작업이 약간 빨라지지만 일부 (실제로 전체 NAND 지연이 필요한 작업)는 느려집니다. 전체 프로그램 실행 속도가 평균적으로 빨라질 정도로 작업 속도가 느려질 경우 n-1을 선택했을 것입니다. n + 1을 선택할 수도 있습니다. 따라서 대부분의 작업이 약간 느려지지만 n 지연 내에서 수행 할 수 없지만 n + 1 지연 내에서 수행 할 수있는 많은 작업이 있으면 프로세서가 전체적으로 더 빨라집니다.

이제 귀하의 질문 : 더하기와 빼기는 너무 일반적인 작업이므로 단일 주기로 실행할 수 있습니다. 결과적으로 AND, OR 등이 더 빨리 실행될 수 있다는 것은 중요하지 않습니다. 그들은 여전히 ​​그 한 사이클이 필요합니다. 물론 단위 "계산"AND 또는 OR 등은 엄지 손가락을 돌리는 데 많은 시간이 있지만 도움이되지 않습니다.

n NAND- 지연 내에서 작업을 수행 할 수 있는지 여부가 아니라는 점에 유의하십시오. 마지막으로 프로세서는 매우 빠르고 매우 비싸고 조금 더 느리고 저렴한 회로를 혼합하여 사용할 수 있으므로 더 많은 돈을 소비함으로써 한 번의 작업만으로도 충분히 빨리 수행 할 수 있습니다.

이제 당신이 클럭 속도가 너무 높은 만들 / 그래서 단지 간단한 비트 연산을 한 사이클에 실행하는 것이 짧고 두 개 이상의에서 다른 모든주기. 프로세서가 느려질 수 있습니다. 두 사이클을 수행하는 작업의 경우 일반적으로 불완전한 명령을 한 사이클에서 다음 사이클로 이동하는 데 오버 헤드가 발생하므로 두 사이클이 실행 시간이 두 배라는 의미는 아닙니다. 따라서 두 사이클을 더하기 위해 클럭 속도를 두 배로 늘릴 수 없었습니다.


6

기존 답변에서 명시 적으로 언급되지 않은 몇 가지 사항을 수정하겠습니다.

현대 프로세서에서는 비트 단위 연산이 32 비트 또는 64 비트에서 병렬로 작동 할 수 있기 때문에 비트 단위 연산이 너무 빠르다는 것을 알고 있습니다.

사실입니다. 일반적으로 (항상 그런 것은 아님) CPU를 "XX"비트로 표시한다는 것은 대부분의 공통 구조 (레지스터 폭, 주소 지정 가능한 RAM 등)가 XX 비트 (종종 "+/- 1"등)임을 의미합니다. 그러나 귀하의 질문과 관련하여 32 비트 또는 64 비트가있는 CPU는 32 또는 64 비트에서 일정한 시간에 기본 비트 작업을 수행한다고 안전하게 가정 할 수 있습니다.

따라서 비트 단위 연산은 하나의 클럭 주기만 걸립니다.

이 결론이 반드시 그런 것은 아닙니다. 특히 풍부한 명령어 세트 (Google CISC vs. RISC)가있는 CPU는 간단한 명령으로도 한 번 이상 사이클을 수행 할 수 있습니다. 인터리빙을 사용하면 간단한 명령조차도 3 클럭 (예 : fetch-exec-store)으로 분류 될 수 있습니다.

그러나 덧셈은 복잡한 연산입니다

아니요, 정수 추가는 간단한 작업입니다. 빼기. 전체 하드웨어에서 가산기를 구현하는 것은 매우 쉽고 기본 비트 작업만큼 즉각적으로 작업을 수행합니다.

적어도 1 개에서 최대 12 개의 비트 단위 연산으로 구성되므로 자연스럽게 3-4 배 느려질 것이라고 생각했습니다.

그것은 많은 트랜지스터보다 3-4 배가 걸리지 만 무시할 수있는 큰 그림과 비교할 때.

간단한 벤치 마크 후에 비트 단위 연산 (XOR, OR, AND 등)만큼 정확하게 추가하는 것이 놀랍습니다. 누구든지 이것에 빛을 비출 수 있습니까?

예 : 정수 덧셈 비트 단위 연산입니다 (다른 것보다 조금 더 비트는 있지만 여전히). 단계적으로 아무것도 할 필요가 없으며 복잡한 알고리즘, 시계 또는 다른 것이 필요하지 않습니다.

CPU 아키텍처보다 더 많은 비트를 추가하려면 단계적으로 처리해야하는 패널티가 발생합니다. 그러나 이것은 또 다른 수준의 복잡성에 있습니다 (조립 / 기계 코드 수준이 아닌 프로그래밍 언어 수준). 이것은 과거에 (또는 오늘날 소형 임베디드 CPU에서) 일반적인 문제였습니다. PC 등의 경우 32 비트 또는 64 비트로 가장 일반적인 데이터 유형으로 충분합니다.


O (N)에서 O (sqrt (N)) 로의 추가에 소요되는 시간 비용을 줄여도 필요한 트랜지스터 수 또는 라우팅 복잡성이 크게 증가하지는 않는다는 점에 주목하는 것이 흥미 롭습니다 (각 단계는 하나의 캐리 와이어가 아래에서 몰래 들어 오도록해야 함) sqrt (N) 추가 병합 단계가 필요합니다 .O (lgN) 트랜지스터 비용으로 시간 비용을 O (lgN)으로 줄일 수 있지만 많은 경우 64와 같은 것을 처리하는 것이 도움이 될 수 있습니다 8 개의 8 비트 추가 (sqrtN 포워딩 사용)는 6 개의 레이어의 병합으로 64 개의 1 비트 추가가 아니라 3 개의 레이어의 병합 로직과 결합되었습니다.
supercat

예, 가산기는 매우 간단합니다. 실제로 인상적인 것은 완전히 파이프 라인 된 3 사이클 대기 시간 64 비트 정수 곱셈기 가있는 최신 x86 CPU입니다 . (예 : imul rax, rcx3c 대기 시간 및 Intel Sandybridge 제품군 및 AMD Ryzen에서 1c 처리량 당 하나). 64 비트 완전 곱셈 (128 비트 결과 rdx : rax 생성)도 동일한 대기 시간과 처리량을 갖지만 2 개의 UOP (다른 포트에서 병렬로 실행)로 구현됩니다. 지침 테이블과 훌륭한 마이크로 아치 안내서는 agner.org/optimize 를 참조하십시오 .
Peter Cordes 17 년

[add-with-carry]는 또 다른 수준의 복잡성 (어셈블리 / 머신 코드 레벨이 아닌 프로그래밍 언어 레벨입니다 . 언어에 따라 다릅니다. 16 비트 CPU를 대상으로하는 AC 컴파일러는 컴파일 할 때 add / adc를 방출해야합니다. 두 가지의 추가 uint32_t.. : 값이 32 비트 대상에 int64_t에 대한 관련 오늘 아직도 AVR 8 비트 RISC 마이크로 컨트롤러, 그래서 32 비트 정수는 4 설명이 필요 godbolt.org/g/wre0fM
피터 코르

예, @PeterCordes, 이것이 제가 의미하는 바입니다. 제 문장을 조금 명확하게했습니다.
AnoE
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.