규칙을 작성할 때 가장 일반적인 성능 문제는 종속 항목에서 축적된 데이터를 순회하거나 복사하는 것입니다. 이러한 작업이 전체 빌드에 집계되면 O(N^2)의 시간이나 공간을 쉽게 소비할 수 있습니다. 이를 방지하기 위해서는 depset을 효과적으로 사용하는 방법을 이해하는 것이 중요합니다.
이는 쉽지 않을 수 있으므로 Bazel은 실수할 수 있는 부분을 찾는 데 도움이 되는 메모리 프로파일러도 제공합니다. 주의: 비효율적인 규칙을 작성하는 비용은 광범위하게 사용될 때까지 명확하게 드러나지 않을 수 있습니다.
디플레이트 사용
규칙 종속 항목에서 정보를 롤업할 때마다 Depsets를 사용해야 합니다. 일반 목록 또는 dict만 사용하여 현재 규칙에 로컬인 정보를 게시합니다.
depset은 정보를 공유할 수 있는 중첩 그래프로 정보를 나타냅니다.
다음 그래프를 살펴보세요.
C -> B -> A
D ---^
각 노드는 단일 문자열을 게시합니다. 디펜싱을 사용하면 데이터는 다음과 같습니다.
a = depset(direct=['a'])
b = depset(direct=['b'], transitive=[a])
c = depset(direct=['c'], transitive=[b])
d = depset(direct=['d'], transitive=[b])
각 항목은 한 번만 언급됩니다. 목록을 사용하면 다음과 같이 표시됩니다.
a = ['a']
b = ['b', 'a']
c = ['c', 'b', 'a']
d = ['d', 'b', 'a']
이 경우에는 'a'
가 4번 언급됩니다. 그래프가 클수록 문제는 더 악화될 뿐입니다.
다음은 디플레이트를 올바르게 사용하여 전이 정보를 게시하는 규칙 구현의 예입니다. O(N^2)가 아니므로 원하는 경우 목록을 사용하여 규칙-로컬 정보를 게시해도 됩니다.
MyProvider = provider()
def _impl(ctx):
my_things = ctx.attr.things
all_things = depset(
direct=my_things,
transitive=[dep[MyProvider].all_things for dep in ctx.attr.deps]
)
...
return [MyProvider(
my_things=my_things, # OK, a flat list of rule-local things only
all_things=all_things, # OK, a depset containing dependencies
)]
자세한 내용은 Depset 개요 페이지를 참고하세요.
depset.to_list()
호출 금지
to_list()
를 사용하여 디버깅을 플랫 목록으로 강제 변환할 수 있지만, 이 경우 일반적으로 O(N^2) 비용이 발생합니다. 가능하면 디버깅 목적 외에 디셋을 평평하게 만들지 마세요.
빌드 규칙과 같이 각 레벨에 비용이 누적되지 않기 때문에 <xx>_binary
규칙과 같은 최상위 타겟에서만 디플레이션을 자유롭게 평면화할 수 있습니다. 그러나 이는 중복되는 종속 항목이 있는 타겟 집합을 빌드할 때 여전히 O(N^2)입니다. //foo/tests/...
테스트를 빌드하거나 IDE 프로젝트를 가져올 때 발생합니다.
depset
에 대한 호출 수 줄이기
루프 내에서 depset
를 호출하는 것은 실수인 경우가 많습니다. 이 경우 실적이 매우 저조한 중첩으로 인해 디핑이 발생할 수 있습니다. 예를 들면 다음과 같습니다.
x = depset()
for i in inputs:
# Do not do that.
x = depset(transitive = [x, i.deps])
이 코드는 쉽게 교체할 수 있습니다. 먼저 전이적 편차를 수집하고 한 번에 병합합니다.
transitive = []
for i in inputs:
transitive.append(i.deps)
x = depset(transitive = transitive)
이는 목록 이해를 통해 줄일 수 있습니다.
x = depset(transitive = [i.deps for i in inputs])
명령줄에 ctx.actions.args() 사용
명령줄을 빌드할 때는 ctx.actions.args()를 사용해야 합니다. 이렇게 하면 모든 종속 항목이 실행 단계로 확대됩니다.
이렇게 하면 속도가 더 빠를 뿐만 아니라 규칙의 메모리 소비가 90% 이상 줄어들 수 있습니다.
몇 가지 요령은 다음과 같습니다.
desets 및 list를 직접 평면화하지 않고 인수로 직접 전달합니다.
ctx.actions.args()
에 의해 확장됩니다. depset 콘텐츠에 변환이 필요한 경우 ctx.actions.args#add를 확인하여 이 조건에 적합한지 확인하세요.File#path
를 인수로 전달하나요? 필요하지 않습니다. 모든 파일은 자동으로 경로로 전환되며 확장 시간을 연기합니다.여러 문자열을 함께 연결하여 문자열을 구성하지 마세요. 최적의 문자열 인수는 상수입니다. 규칙의 모든 인스턴스 간에 메모리가 공유되기 때문입니다.
인수가 너무 길어서
ctx.actions.args()
객체가ctx.actions.args#use_param_file
을 사용하여 매개변수 파일에 조건부 또는 무조건 작성될 수 있습니다. 이 작업은 작업이 실행될 때 백그라운드에서 진행됩니다. 매개변수 파일을 명시적으로 제어해야 한다면ctx.actions.write
를 사용하여 수동으로 작성하면 됩니다.
예:
def _impl(ctx):
...
args = ctx.actions.args()
file = ctx.declare_file(...)
files = depset(...)
# Bad, constructs a full string "--foo=<file path>" for each rule instance
args.add("--foo=" + file.path)
# Good, shares "--foo" among all rule instances, and defers file.path to later
# It will however pass ["--foo", <file path>] to the action command line,
# instead of ["--foo=<file_path>"]
args.add("--foo", file)
# Use format if you prefer ["--foo=<file path>"] to ["--foo", <file path>]
args.add(format="--foo=%s", value=file)
# Bad, makes a giant string of a whole depset
args.add(" ".join(["-I%s" % file.short_path for file in files])
# Good, only stores a reference to the depset
args.add_all(files, format_each="-I%s", map_each=_to_short_path)
# Function passed to map_each above
def _to_short_path(f):
return f.short_path
전이 작업 입력은 디셋이어야 합니다.
ctx.actions.run을 사용하여 작업을 빌드할 때는 inputs
필드에 오프셋을 허용하는 것을 잊지 마세요. 종속 항목에서 입력이 전이될 때마다 이 메서드를 사용합니다.
inputs = depset(...)
ctx.actions.run(
inputs = inputs, # Do *not* turn inputs into a list
...
)
그네형
Bazel이 중단된 것으로 보이는 경우 Ctrl-\를 누르거나 Bazel에 SIGQUIT
신호 (kill -3 $(bazel info server_pid)
)를 전송하여 $(bazel info output_base)/server/jvm.out
파일에 스레드 덤프를 가져올 수 있습니다.
bazel이 중단되면 bazel info
를 실행하지 못할 수 있으므로 output_base
디렉터리는 일반적으로 작업공간 디렉터리에 있는 bazel-<workspace>
심볼릭 링크의 상위 요소입니다.
성능 프로파일링
JSON 트레이스 프로필은 Bazel이 호출 중에 소비한 시간을 빠르게 이해하는 데 매우 유용할 수 있습니다.
메모리 프로파일링
Bazel은 규칙의 메모리 사용을 확인하는 데 도움이 되는 메모리 프로파일러를 기본 제공합니다. 문제가 있으면 힙을 덤프하여 문제를 일으키는 정확한 코드 줄을 찾을 수 있습니다.
메모리 추적 사용 설정
다음 두 시작 플래그를 모든 Bazel 호출에 전달해야 합니다.
STARTUP_FLAGS=\
--host_jvm_args=-javaagent:$(BAZEL)/third_party/allocation_instrumenter/java-allocation-instrumenter-3.3.0.jar \
--host_jvm_args=-DRULE_MEMORY_TRACKER=1
메모리 추적 모드에서 서버를 시작합니다. Bazel 호출 한 개라도 잊어버리면 서버가 다시 시작되며 처음부터 다시 시작해야 합니다.
메모리 추적기 사용
예를 들어 타겟 foo
를 살펴보고 기능을 알아봅니다. 빌드 실행 단계를 실행하지 않고 분석만 실행하려면 --nobuild
플래그를 추가합니다.
$ bazel $(STARTUP_FLAGS) build --nobuild //foo:foo
다음으로 전체 Bazel 인스턴스가 사용하는 메모리의 양을 확인합니다.
$ bazel $(STARTUP_FLAGS) info used-heap-size-after-gc
> 2594MB
bazel dump --rules
를 사용하여 규칙 클래스별로 분류합니다.
$ bazel $(STARTUP_FLAGS) dump --rules
>
RULE COUNT ACTIONS BYTES EACH
genrule 33,762 33,801 291,538,824 8,635
config_setting 25,374 0 24,897,336 981
filegroup 25,369 25,369 97,496,272 3,843
cc_library 5,372 73,235 182,214,456 33,919
proto_library 4,140 110,409 186,776,864 45,115
android_library 2,621 36,921 218,504,848 83,366
java_library 2,371 12,459 38,841,000 16,381
_gen_source 719 2,157 9,195,312 12,789
_check_proto_library_deps 719 668 1,835,288 2,552
... (more output)
bazel dump --skylark_memory
를 사용하여 pprof
파일을 생성하여 메모리 위치를 확인합니다.
$ bazel $(STARTUP_FLAGS) dump --skylark_memory=$HOME/prof.gz
> Dumping Starlark heap to: /usr/local/google/home/$USER/prof.gz
pprof
도구를 사용하여 힙을 조사합니다. 처음에는 pprof -flame $HOME/prof.gz
를 사용하여 Flame 그래프를 얻는 것이 좋습니다.
https://github.com/google/pprof에서 pprof
을(를) 다운로드합니다.
줄로 주석이 달린 가장 인기 있는 통화 사이트의 텍스트 덤프를 가져옵니다.
$ pprof -text -lines $HOME/prof.gz
>
flat flat% sum% cum cum%
146.11MB 19.64% 19.64% 146.11MB 19.64% android_library <native>:-1
113.02MB 15.19% 34.83% 113.02MB 15.19% genrule <native>:-1
74.11MB 9.96% 44.80% 74.11MB 9.96% glob <native>:-1
55.98MB 7.53% 52.32% 55.98MB 7.53% filegroup <native>:-1
53.44MB 7.18% 59.51% 53.44MB 7.18% sh_test <native>:-1
26.55MB 3.57% 63.07% 26.55MB 3.57% _generate_foo_files /foo/tc/tc.bzl:491
26.01MB 3.50% 66.57% 26.01MB 3.50% _build_foo_impl /foo/build_test.bzl:78
22.01MB 2.96% 69.53% 22.01MB 2.96% _build_foo_impl /foo/build_test.bzl:73
... (more output)