[V8] V8 엔진 뜯어보기(2)
lets-understand-chrome-v8 V8 Bindings Design v8-docs
Node와 V8의 관계
- Node의 핵심 의존성(dependencies)는 V8과 libuv이다.
- libuv는 네트워크, 파일시스템 등 커널과 연관된 작업을 한다.
- V8은 Node뿐만아니라 브라우저에서도 쓰이는데, V8은 가상머신이라고 생각하면된다.
javascript
- Javascript도 결국 인간이 이해할 수 있는 언어이기때문에
기계어로 컴파일
이 필요하다.- V8엔진은 인간의 언어로 쓰인 Javascript를 Parse해 AST로 나타낸다.
SHELL script도 마찬가지.
, minishell과제에서 사용자의 입력하나하나를 토큰화하고, 의미를 부여하고 구문분석해 명령어들을 실행시키는 과정을 parsing이라고하고, parse된 토큰들을 저장하는 곳을 AST라고한다.Ignition
이라 불리는interpreter
AST에 담겨져있는 각 노드를 방문해가상 bytecode
를 생성한다.
- bytecode는 기계어에 가깝게 파싱된 코드라고 생각하면된다.
- 이 과정에서 몇몇 최적화를 진행한다.
- 불필요한 로드와 레지스터 저장을 피하는 최적화
- 여러 바이트코드를 병합하는 최적화
- 필요없는 코드를 제거하는 최적화
- 이 가상 bytecode들은
TurboFan
을 통해 최적화된 코드로 변환된다.
- V8이 Javascript를 이렇게 최적화하는 이유는 javascript가 사용되는 생태계에 있다.
현재 Javascript가 주로사용되는 곳은 어디인지 생각해보자,브라우저와 서버
이다.
브라우저와 서버의 주된 특징은 Javascript를 실행시키고 나면 몇시간이고 계속 해당 코드를반복해서 사용
한다는 것이다.
javascript가 느리다는 것은 잘못된 말이다. 처음 실행할때는 파싱, 최적화하는 과정을 거쳐 다른 프로그래밍 언어보다는 느릴지라도, 첫 실행 이후에는 그 이상의 성능을 낼 수도 있다. 빠르고, 메모리를 적게 사용하는 프로그램을 작성하기위한 것이다.
v8_hello_world
- 소스코드를 까봅시다.
- 원본 소스코드
컴파일 팁
- outdefault 디렉터리에서 ninja를 치면 helloworld.cc에 대해 컴파일 해준다.
- 직접 컴파일 해보려고 시도해봤는데 결과는 아래와 같다.🥹
../../third_party/llvm-build/Release+Asserts/bin/clang++
-MMD -MF obj/v8_hello_world/hello-world.o.d
-D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -DCR_XCODE_VERSION=1420
-DCR_CLANG_REVISION=\"llvmorg-16-init-13328-g110fe4f4-1\" -DCOMPONENT_BUILD
-DCR_LIBCXX_REVISION=52399655fdafdd14ade17ab12ddc9e955423aa5a -D_LIBCPP_ENABLE_ASSERTIONS_DEFAULT=1
-D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1 -D_DEBUG -DDYNAMIC_ANNOTATIONS_ENABLED=1
-DCPPGC_VERIFY_HEAP -DENABLE_DISASSEMBLER -DV8_TYPED_ARRAY_MAX_SIZE_IN_HEAP=64 -DOBJECT_PRINT
-DVERIFY_HEAP -DV8_TRACE_MAPS -DV8_ENABLE_ALLOCATION_TIMEOUT -DV8_ENABLE_FORCE_SLOW_PATH
-DV8_ENABLE_DOUBLE_CONST_STORE_CHECK -DV8_INTL_SUPPORT -DENABLE_HANDLE_ZAPPING -DV8_CODE_COMMENTS
-DV8_ENABLE_DEBUG_CODE -DV8_ENABLE_HEAP_SNAPSHOT_VERIFY -DV8_SNAPSHOT_NATIVE_CODE_COUNTERS
-DV8_USE_EXTERNAL_STARTUP_DATA -DV8_ATOMIC_OBJECT_FIELD_WRITES -DV8_ENABLE_LAZY_SOURCE_POSITIONS
-DV8_SHARED_RO_HEAP -DV8_WIN64_UNWINDING_INFO -DV8_ENABLE_REGEXP_INTERPRETER_THREADED_DISPATCH
-DV8_SHORT_BUILTIN_CALLS -DV8_EXTERNAL_CODE_SPACE -DV8_ENABLE_SYSTEM_INSTRUMENTATION
-DV8_ENABLE_WEBASSEMBLY -DV8_ALLOCATION_FOLDING -DV8_ALLOCATION_SITE_TRACKING -DV8_ADVANCED_BIGINT_ALGORITHMS
-DV8_USE_ZLIB -DV8_USE_LIBM_TRIG_FUNCTIONS -DV8_ENABLE_CHECKS -DV8_COMPRESS_POINTERS
-DV8_COMPRESS_POINTERS_IN_SHARED_CAGE -DV8_31BIT_SMIS_ON_64BIT_ARCH -DV8_ENABLE_SANDBOX -DV8_DEPRECATION_WARNINGS
-DV8_IMMINENT_DEPRECATION_WARNINGS -DCPPGC_CAGED_HEAP -DCPPGC_YOUNG_GENERATION -DCPPGC_POINTER_COMPRESSION
-DV8_TARGET_ARCH_ARM64 -DV8_HAVE_TARGET_OS -DV8_TARGET_OS_MACOS -DDEBUG -DV8_RUNTIME_CALL_STATS
-DUSING_V8_SHARED -DUSING_V8_BASE_SHARED -DUSING_V8_PLATFORM_SHARED -DU_USING_ICU_NAMESPACE=0
-DU_ENABLE_DYLOAD=0 -DUSE_CHROMIUM_ICU=1 -DU_ENABLE_TRACING=1 -DU_ENABLE_RESOURCE_TRACING=0
-DICU_UTIL_DATA_IMPL=ICU_UTIL_DATA_FILE -I../.. -Igen -I../../buildtools/third_party/libc++
-I../../include -Igen/include -I../../third_party/icu/source/common -I../../third_party/icu/source/i18n
-Wall -Werror -Wextra -Wimplicit-fallthrough -Wextra-semi -Wunreachable-code-aggressive -Wthread-safety
-Wunguarded-availability -Wno-missing-field-initializers -Wno-unused-parameter -Wno-psabi -Wloop-analysis
-Wno-unneeded-internal-declaration -Wenum-compare-conditional -Wno-ignored-pragma-optimize -Wno-deprecated-builtins
-Wno-bitfield-constant-conversion -Wshadow -fno-delete-null-pointer-checks -fno-ident -fno-strict-aliasing
-fstack-protector-strong -fcolor-diagnostics -fmerge-all-constants -fcrash-diagnostics-dir=../../tools/clang/crashreports
-mllvm -instcombine-lower-dbg-declare=0 -ffp-contract=off -fcomplete-member-pointers -arch arm64 -fno-global-isel
-Wno-builtin-macro-redefined -D__DATE__= -D__TIME__= -D__TIMESTAMP__= -ffile-compilation-dir=. -no-canonical-prefixes
-ftrivial-auto-var-init=pattern -fno-omit-frame-pointer -g2 -gdwarf-aranges -Xclang -debug-info-kind=limited -isysroot
../../../../../../../../Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX13.1.sdk
-mmacos-version-min=10.13 -Wheader-hygiene -Wstring-conversion -Wtautological-overlap-compare -Wmissing-field-initializers
-Wunreachable-code -Wctad-maybe-unsupported -Wno-shadow -Wshorten-64-to-32 -O0 -fvisibility=default
-Wno-undefined-bool-conversion -Wno-tautological-undefined-compare -std=c++20 -Wno-trigraphs -fno-exceptions
-fno-rtti -nostdinc++ -isystem../../buildtools/third_party/libc++/trunk/include -isystem../../buildtools/third_party/libc++abi/trunk/include
-c ../../samples/hello-world.cc -o obj/v8_hello_world/hello-world.o
- 100줄짜리 코드 하나 컴파일하는데에 이정도의 플래그가 들어갑니다. V8이 얼마나 큰 코드인지 짐작이 가시나요..?
- 저처럼 뻘짓하지 마시구, ninja로 컴파일 하세요… makefile처럼 변경된 파일에 대해서만 컴파일을 시도합니다.
Isolate
- isolate는 V8의 인스턴스 개념이다.
- Blink(크롬의 웹 렌더러)에서 isolate와 쓰레드는
1:1관계
이다.- Blink는 V8을 포함하는 웹 브라우저의 렌더링엔진.
1개의 isolate는 메인쓰레드와 연관
되어있다.또 다른 isolate는 워커쓰레드와 연관
되어있다.또 다른 isolate는 compositor worker라고 하는 여러개의 compositor worker들에 의해 공유
된다.
Handle
heap에 있는 객체의 위치에 대한 참조
를 제공한다.- V8의
가비지 콜렉터
는 더이상 접근이 불가능한 객체에 대한 메모리를 회수한다. - 회수하는 동안 객체를 heap의 다른 위치로 이동시키기도한다.
- 물론 이때 GC는 객체가 옮겨진 새로운 위치에 대해 객체를 참조하는 모든
handle
을 업데이트한다. - 만약 객체가 자바스크립트로부터 접근이 불가능하고, 그 객체를 참조하는 handle이 없다면 Garbage된 것이다.
handle 종류
Local Handle
- 스택에 존재하다가
소멸자가 호출되면 삭제
된다. 함수가 호출되면서 생성된 handle scope에 의해 lifetime이 결정
된다.- handle scope가 삭제되면, GC는 handle scope내에서 이전에 handle들에 의해 참조되고 있던 객체들의 메모리를 회수한다.
- handle stack은 V8의 C++ call stack의 일부분이 아니다.
- handle scope는 V8의 C++ stack의 일부분이다.
- handle scope는 무조건 stack에만 할당될 수 있으며 new로는 불가능하다
Local<SomeType>
- 스택에 존재하다가
Persistent Handle
- local handle처럼 heap에 할당된 객체에 대한 참조를 제공하나, lifetime에서 차이가 있다.
- 1개 이상의 함수 호출을 위해 참조를 유지해야하거나, C++ scope과 관련없는 handle이 필요할때이다.
-
예를들어, 크롬의 경우에는 DOM 노드가 persistent handle
에 속한다. PersistentBase::SetWeak
를 사용해 객체가 weak persistent handle에게만 참조될떄 콜백함수를 실행할 수 있다.UniquePersistent<SomeType>
는 객체의 lifetime조절을 C++의 생성자와 소멸자에 의존한다.Persistent<SomeType>
로 생성자를 통해 만들 수는 있지만, 명시적으로Persistent::Reset
로 소멸되어야한다.
Eternal Handle
- 절대 삭제되지 않는 handle
- GC가 이 handle은 GC대상으로 여기지 않기때문에 비용이 적다.
Context
Context
: V8의 단일 인스턴스에서 독립적인 자바스크립트 어플리에키션을 실행하기위핸 실행환경을 의미.- 필요성 : 자바스크립트는 자바스크립트 코드에 의해 변경될 수 있는 built-in 함수와 객체를 제공하기때문이다.
- 완전히 관련없는 두 코드가 전역객체를 수정할 경우, 예측불가능한 결과를 낳기때문이다.
- 생성되어야하는 모든 built-in 객체에 새로운 execution context를 만든다는 게 굉장히 CPU와 메모리의 입장에서는 비싼 연산이다.
- 하지만, V8은 캐싱을 통해 첫번째 context은 무겁고, 그 이후의 context들은 가볍게만든다.
- 첫번째 context는 javascript 코드를 파싱하고, built-in object를 생성해야하는 임무를 맡지만
- 이후의 context는 단지 built-in 객체 생성하는 임무만을 가진다.
- context A에 있을때, 다른 context B로 변경할 수 있는데, B를 exit하면 A가 다시 현재 context로 복구된다.
hello_world.cc에서 handle
Isolate를 생성
한다.(handle stack의 생성)- 생성된 Isolate의 scope를 기반으로
Context
생성- Local handle을 리턴한다.
- 지속적으로 유지되는 persistent_context
댓글남기기