2. XML 파싱 모형.
❖ SAX(Simple API for XML) - 스트림과 ‘태그 시작, 끝, 문
자자료’등의 콜백으로 데이터 처리.!
❖ Pull Parsing - SAX 유사, iterator 객체를 통해 제어.!
❖ DOM(Document Object Model) - 입력을 문서 객체로 변
환후 처리.
3. pugixml DOM 파서
❖ 메모리 안에 들어갈 정도로 작은 문서.!
❖ 방문할 노드들이 서로 참조하는 복잡한 구조를 가진 문서!
❖ 복잡한 방식으로 변환해야 하는 문서.
4. pugixml 설계상의 선택
❖ 아주 빠르고 가벼운 DOM 기반 XML 조작 라이브러리를
목표로 개발.!
❖ 성능과 XML 검증의 절충점.!
❖ well-formed 검증.!
❖ DTD(Document Type Declaration)은 검증 않함.!
❖ 종종 well-formed 가 아닌 경우도 성공으로 처리.
5. 파싱
❖ 토큰 스트림 대신 문자 스트림에 대해 파싱을 수행.!
❖ UTF-8 문자만 지원.!
❖ 제자리 파싱(In-place parsing) - 스트림에 있는 자료를 직
접 처리하는 방식.
문자열 복사를 최소화하기 위해.
6. In-place parsing
❖ 문자열을 만나면 그 문자열의 포인터와 길이를 저장.!
❖ 성능 좋아지지만, 메모리 사용량은 증가.
- 원본 스트림 유지.
< n > T h e n o d e t e x t < / n >
포인터0xabc3, 길이 130xabc0
7. In-place parsing - 널문자 처리
❖ 문자열 접근을 보다 빠르게 하기 위하여 널문자를 삽입.!
❖ XML은 문자열 끝 다음 문자는 < 기호로 구분됨.
< n > T h e n o d e t e x t 0 / n >
포인터0xabc3, 길이 130xabc0
8. In-place parsing - 문자 표현 처리
❖ 문자열이 표현과 다른 경우 파싱중 처리.!
❖ `line1xDxAline2xDline3xAxA` 을
line1xAline2xAline3xAxA` 로 변환.!
❖ 문자 참조 확장 - a 을 a 로 변환.!
❖ 개체 참조 확장 - < (<), >(>), "(“), '(‘);
&(&)!
❖ 특성 값 정규화(Attribute-value normalization) - 모든 공
백문자를 빈칸으로 변환.
9. In-place parsing - 문자 표현 처리
❖ 변환 때문에 물자열이 더 길어져서는 안된다.!
❖ 변환 결과가 더 길면 문서 자료를 덮어 쓸 수 있기 때문.
< n > A & # 3 2 ; & l t ; B . < / n >0xabc0
< n > A < B . 0 l t ; B . < / n >0xabc0
10. In-place parsing - Copy-on-Write
❖ memory-mapped file I/O 을 사용.!
❖ 널종료와 텍스트 변환을 지원하기 위해 Copy-on-Write 방식을 적
용하여 원본 파일이 변경되는 것을 막음.!
❖ 프로세스 주소 공간에 직접 대응 시키므로 메모리 복사를 피할
수 있음.!
❖ 파일이 캐시되지 않은 경우 커널이 로딩하므로 입출력과 파싱이
병렬적으로 진행.!
❖ 수정된 페이지만 물리적 메모리에 할당되므로 메모리 소비를 줄
일 수 있음.
11. 문자별 연산의 최적화
Optimizing character-wise operations
❖ 문자 하나에 소비된 평균 프로세서 주기(cycle) 수이다.!
❖ 문자 집합 소속 여부 검출
한 문자가 어떤 문자 집합에 속하는지 판정하는 것.
15. 표준 라이브러리 is*() 함수
❖ 성능이 중요한 코드에서는 isalpha()등을 피해야 함.!
❖ locale 이 “C” 인지 점검하는 과정때문.
16. 문자열 변환의 최적화
Optimizing string transformations
❖ 문자열 값을 읽고 변환하는 과정에서 시간 소비가 크다.!
❖ A < B.!
❖ A < B.
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. PCDATA 틈(GAP) 관리
❖ " 를 “ 로 대체하면 문자 다섯개의 틈이 생김.!
❖ 두 틈을 병합 - 기존 틈과 새 틈 사이 자료를 앞으로 옮김.!
❖ 읽기/쓰기 포인터보다 좀 더 빠르게 병합(memmove)
19. 제어 흐름의 최적화
Optimizing control flow
❖ 재귀적 하강 파서(recursive-descent parser) 형태에서 성
능 향상을 위해 재귀를 반복 루프로 변경.!
❖ 노드 커서는 스택방식으로 동작.!
❖ 스택 공간 소비량이 입력 문서와 무관하게 일정.!
❖ 안정성을 증가.!
❖ 잠재적인 비싼 함수 호출을 피함. ???
20. 분기 순서와 코드 국소성
❖ 자주 실행되는 부분(태그이름,속성)과 거의 실행되지 않
는 부분(DOCTYPE)!
❖ 처리 확률 - ‘<‘ 문자 다음 ‘태그 이름’, ‘/‘, ‘!’, ‘?’ 순의 확률
로 나타남.!
❖ 코드 조각들의 확률에 따라 재배치!
❖ 인라인 코드량을 제한.
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. 메모리 안정성 보장
❖ 버퍼 오버플로우를 막기 위해.!
❖ 현재 읽기와 버퍼 끝을 비교.!
❖ 하나의 레지스터가 더 필요.!
❖ 함수 호출시 현재 위치와 끝위치를 전달할 포인터 필요!
❖ 널문자 처리.!
❖ 입력 버퍼와 버퍼 크기를 전달하는 경우 사용의 불편함
발생.
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;!
};!
27. 스택 기반 할당자의 메모리 해제 지원
❖ 메모리 해제와 재사용을 위해 참조 카운트 방식 도입.!
❖ 모든 페이지는 32바이트 경계로 정렬되고 모든 페이지 포인터의
하위 다섯 비트는 항상 0이다. ???!
❖ 5비트에 XML 메타 데이터를 저장.!
❖ 할당된 요소의 위치를 페이지 시작 위치를 기준으로 오프셋으로
저장.!
❖ 페이지 포인터의 주소 =>
(allocator_page*)((char*)(object) -object->offset -
offsetof(allocator_page, data))