Ch4 pugixml

587 views

Published on

Published in: Software
0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total views
587
On SlideShare
0
From Embeds
0
Number of Embeds
16
Actions
Shares
0
Downloads
1
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

Ch4 pugixml

  1. 1. 제 4장 빛의 속도록 
 XML 파싱하기 김경렬
  2. 2. XML 파싱 모형. ❖ SAX(Simple API for XML) - 스트림과 ‘태그 시작, 끝, 문 자자료’등의 콜백으로 데이터 처리.! ❖ Pull Parsing - SAX 유사, iterator 객체를 통해 제어.! ❖ DOM(Document Object Model) - 입력을 문서 객체로 변 환후 처리.
  3. 3. pugixml DOM 파서 ❖ 메모리 안에 들어갈 정도로 작은 문서.! ❖ 방문할 노드들이 서로 참조하는 복잡한 구조를 가진 문서! ❖ 복잡한 방식으로 변환해야 하는 문서.
  4. 4. pugixml 설계상의 선택 ❖ 아주 빠르고 가벼운 DOM 기반 XML 조작 라이브러리를 목표로 개발.! ❖ 성능과 XML 검증의 절충점.! ❖ well-formed 검증.! ❖ DTD(Document Type Declaration)은 검증 않함.! ❖ 종종 well-formed 가 아닌 경우도 성공으로 처리.
  5. 5. 파싱 ❖ 토큰 스트림 대신 문자 스트림에 대해 파싱을 수행.! ❖ UTF-8 문자만 지원.! ❖ 제자리 파싱(In-place parsing) - 스트림에 있는 자료를 직 접 처리하는 방식.
 문자열 복사를 최소화하기 위해.
  6. 6. In-place parsing ❖ 문자열을 만나면 그 문자열의 포인터와 길이를 저장.! ❖ 성능 좋아지지만, 메모리 사용량은 증가.
 - 원본 스트림 유지. < n > T h e n o d e t e x t < / n > 포인터0xabc3, 길이 130xabc0
  7. 7. In-place parsing - 널문자 처리 ❖ 문자열 접근을 보다 빠르게 하기 위하여 널문자를 삽입.! ❖ XML은 문자열 끝 다음 문자는 < 기호로 구분됨. < n > T h e n o d e t e x t 0 / n > 포인터0xabc3, 길이 130xabc0
  8. 8. In-place parsing - 문자 표현 처리 ❖ 문자열이 표현과 다른 경우 파싱중 처리.! ❖ `line1xDxAline2xDline3xAxA` 을
 line1xAline2xAline3xAxA` 로 변환.! ❖ 문자 참조 확장 - a 을 a 로 변환.! ❖ 개체 참조 확장 - &lt; (<), &gt;(>), &quot;(“), &apos;(‘); &amp;(&)! ❖ 특성 값 정규화(Attribute-value normalization) - 모든 공 백문자를 빈칸으로 변환.
  9. 9. In-place parsing - 문자 표현 처리 ❖ 변환 때문에 물자열이 더 길어져서는 안된다.! ❖ 변환 결과가 더 길면 문서 자료를 덮어 쓸 수 있기 때문. < n > A & # 3 2 ; & l t ; B . < / n >0xabc0 < n > A < B . 0 l t ; B . < / n >0xabc0
  10. 10. In-place parsing - Copy-on-Write ❖ memory-mapped file I/O 을 사용.! ❖ 널종료와 텍스트 변환을 지원하기 위해 Copy-on-Write 방식을 적 용하여 원본 파일이 변경되는 것을 막음.! ❖ 프로세스 주소 공간에 직접 대응 시키므로 메모리 복사를 피할 수 있음.! ❖ 파일이 캐시되지 않은 경우 커널이 로딩하므로 입출력과 파싱이 병렬적으로 진행.! ❖ 수정된 페이지만 물리적 메모리에 할당되므로 메모리 소비를 줄 일 수 있음.
  11. 11. 문자별 연산의 최적화
 Optimizing character-wise operations ❖ 문자 하나에 소비된 평균 프로세서 주기(cycle) 수이다.! ❖ 문자 집합 소속 여부 검출
 한 문자가 어떤 문자 집합에 속하는지 판정하는 것.
  12. 12. ! enum chartype_t! ! {! ! ! ct_parse_pcdata = 1,! // 0, &, r, <! ! ! ct_parse_attr = 2,!! // 0, &, r, ', "! ! ! ct_parse_attr_ws = 4,! // 0, &, r, ', ", n, tab! ! ! ct_space = 8,! ! ! // r, n, space, tab! ! ! ct_parse_cdata = 16,! // 0, ], >, r! ! ! ct_parse_comment = 32,!// 0, -, >, r! ! ! ct_symbol = 64,! ! ! // Any symbol > 127, a-z, A-Z, 0-9, _, :, -, .! ! ! ct_start_symbol = 128! // Any symbol > 127, a-z, A-Z, _, :! ! };! ! ! static const unsigned char chartype_table[256] =! ! {! ! ! 55, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 0, 0, 63, 0, 0, // 0-15! ! ! 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16-31! ! ! 8, 0, 6, 0, 0, 0, 7, 6, 0, 0, 0, 0, 0, 96, 64, 0, // 32-47! ! ! 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 192, 0, 1, 0, 48, 0, // 48-63! ! ! 0, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, // 64-79! ! ! 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 0, 0, 16, 0, 192, // 80-95! ! ! 0, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, // 96-111! ! ! 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 0, 0, 0, 0, 0, // 112-127! ! ! ! 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, // 128+! ! ! … … …! ! ! 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192! ! };! ! ! bool ischartype_utf8(char c, chartype_t ct){! ! ! return ct & chartype_table[(unsigned char)c];! ! }!
  13. 13. 특정 구간의 모든 문자 ❖ 주어진 문자가 숫자인지 판정하는 함수! ❖ bool isdigit(char ch) { return (ch >= '0' && ch <= '9'); }! ❖ bool isdigit(char ch) { return (unsigned)(ch - '0') < 10; }
  14. 14. UTF-8 바이트열 ❖ 연속된 4 바이트가 ASCII 기호를 나타내는 UTF-8 바이트 열인지 판별하는 코드.! ❖ (*(const uint32_t*)data & 0x80808080) == 0
  15. 15. 표준 라이브러리 is*() 함수 ❖ 성능이 중요한 코드에서는 isalpha()등을 피해야 함.! ❖ locale 이 “C” 인지 점검하는 과정때문.
  16. 16. 문자열 변환의 최적화 Optimizing string transformations ❖ 문자열 값을 읽고 변환하는 과정에서 시간 소비가 크다.! ❖ A &lt; B.! ❖ A < B.
  17. 17. PCDATA 파싱 함수 ❖ bool 플래그 2개 -> 4개 변형.! ❖ 문자들은 문자 집합 판정 이 용. template <bool opt_eol, bool opt_escape> struct! ! strconv_pcdata_impl {! ! static char_t* parse(char_t* s) {! ! gap g;! ! while (true) {! ! while (!PUGI__IS_CHARTYPE(*s, ct_parse_pcdata)) ++s;! ! if (*s == '<') { // PCDATA ends here! ! *g.flush(s) = 0;! ! return s + 1;! ! } else if (opt_eol && *s == 'r') { // 0x0d or 0x0d 0x0a pair! ! *s++ = 'n'; // replace first one with 0x0a! ! if (*s == 'n') g.push(s, 1);! ! } else if (opt_escape && *s == '&') {! ! s = strconv_escape(/s, g);! ! } else if (*s == 0) {! ! return s;! ! } else {! ! ++s;! ! }! ! }! ! }! ! };!
  18. 18. PCDATA 틈(GAP) 관리 ❖ &quot; 를 “ 로 대체하면 문자 다섯개의 틈이 생김.! ❖ 두 틈을 병합 - 기존 틈과 새 틈 사이 자료를 앞으로 옮김.! ❖ 읽기/쓰기 포인터보다 좀 더 빠르게 병합(memmove)
  19. 19. 제어 흐름의 최적화 Optimizing control flow ❖ 재귀적 하강 파서(recursive-descent parser) 형태에서 성 능 향상을 위해 재귀를 반복 루프로 변경.! ❖ 노드 커서는 스택방식으로 동작.! ❖ 스택 공간 소비량이 입력 문서와 무관하게 일정.! ❖ 안정성을 증가.! ❖ 잠재적인 비싼 함수 호출을 피함. ???
  20. 20. 분기 순서와 코드 국소성 ❖ 자주 실행되는 부분(태그이름,속성)과 거의 실행되지 않 는 부분(DOCTYPE)! ❖ 처리 확률 - ‘<‘ 문자 다음 ‘태그 이름’, ‘/‘, ‘!’, ‘?’ 순의 확률 로 나타남.! ❖ 코드 조각들의 확률에 따라 재배치! ❖ 인라인 코드량을 제한.
  21. 21. ❖ 조건 분기들을 확률이 높은 거에서 낮은 것 순서로 재배치.! ❖ 평균적인 조건 판정 및 분기 수행 횟수가 최소화. ! if (data[0] == '<')! ! {! ! if (data[1] == '!') { ... }! ! else if (data[1] == '/') { ... }! ! else if (data[1] == '?') { ... }! ! else { /* start-tag or unrecognized tag */ }! ! }! ! ! if (data[0] == '<')! ! {! ! if (PUGI__IS_CHARTYPE(data[1], ct_start_symbol)) { /* start-tag */ }! ! else if (data[1] == '/') { ... }! ! else if (data[1] == '!') { ... }! ! else if (data[1] == '?') { ... }! ! else { /* unrecognized tag */ }! ! }!
  22. 22. 메모리 안정성 보장 ❖ 버퍼 오버플로우를 막기 위해.! ❖ 현재 읽기와 버퍼 끝을 비교.! ❖ 하나의 레지스터가 더 필요.! ❖ 함수 호출시 현재 위치와 끝위치를 전달할 포인터 필요! ❖ 널문자 처리.! ❖ 입력 버퍼와 버퍼 크기를 전달하는 경우 사용의 불편함 발생.
  23. 23. DOM 자료 구조 ❖ 연결 목록 기반 접근 방식을 사용하 는 노드 수정은 O(1).! ❖ 연결 목록 접근 방식- 고정 크기 할 당을 위한 빠른 메모리 할당자를 설 계하는 것이 임의 크기 할당자보다 쉽다.! ❖ 메모리 절약 - last_child 제거, prev_sibling_cyclic 으로 대체O(1). struct Node {! Node* first_child;! Node* last_child;! Node* prev_sibling;! Node* next_sibling;! };! struct Node {! Node* first_child;! Node* prev_sibling_cyclic;! Node* next_sibling;! };!
  24. 24. Node* last_child(Node* node) {! return (node->first_child) ?! node->first_child->prev_sibling_cyclic : NULL;! }! ! Node* prev_sibling(Node* node) {! return (/node->prev_sibling_cyclic->next_sibling) ?! node->prev_sibling_cyclic : NULL;! }!
  25. 25. 스택 기반 메모리 할당 ❖ 가변 크기 문자열 할당.! ❖ 할당 국소성을 유지.! ❖ DOM 파괴 속도을 위한 메모리 해제.
  26. 26. 스택 할당자 const size_t allocator_page_size = 32768;! struct allocator_page {! allocator_page* next_page;! size_t offset;! char data[allocator_page_size];! };! struct allocator_state {! allocator_page* current;! };! ! void* allocate_new_page_data(size_t size) {! size_t extra_size = (size > allocator_page_size) ?! size - allocator_page_size : 0;! return malloc(sizeof(allocator_page) + extra_size);! } void* allocate_oob(allocator_state* state, size_t size) {! allocator_page* page = (allocator_page*)allocate_new_page_data(siz e);! // add page to page list! page->next_page = state->current;! state->current = page;! // user data is located at the beginning of the page! page->offset = size;! return page->data;! }! ! void* allocate(allocator_state* state, size_t size) {! if (state->current->offset + size <= allocator_page_size) {! void* result = state->current->data + state->current->offset;! state->current->offset += size;! return result;! }! return allocate_oob(state, size);! }!
  27. 27. 스택 기반 할당자의 메모리 해제 지원 ❖ 메모리 해제와 재사용을 위해 참조 카운트 방식 도입.! ❖ 모든 페이지는 32바이트 경계로 정렬되고 모든 페이지 포인터의 하위 다섯 비트는 항상 0이다. ???! ❖ 5비트에 XML 메타 데이터를 저장.! ❖ 할당된 요소의 위치를 페이지 시작 위치를 기준으로 오프셋으로 저장.! ❖ 페이지 포인터의 주소 => 
 (allocator_page*)((char*)(object) -object->offset - offsetof(allocator_page, data))

×