Depset'ler, yüksek performans gösteren ve Hedefin geçişli bağımlılıkları genelinde veri toplama. Proje yöneticilerinin öğesi oluşturun.
Depset’in belirleyici özelliği, zaman ve alandan tasarruf sağlayan birleştirme işlemidir. Depset oluşturucu, bir öğe listesini ("doğrudan") ve diğer depsets ("geçişli") çalışır ve tüm doğrudan öğeler ve tüm geçişli kümelerin birleşimi. Kavramsal olarak kurucu, doğrudan ve geçişli düğümlere sahip yeni bir grafik düğümü oluşturur hale geldi. Depset'lerin anlamlarına göre, çapraz geçişidir.
Depset'lerin örnek kullanımları şunlardır:
Tüm nesne dosyalarının yollarını bir programın kitaplıklarında depolayarak daha sonra bir sağlayıcı aracılığıyla bağlayıcı işlemine iletilebilir.
Yorumlanan bir dil için dosya taşıma işlemleri yürütülebilir.
Açıklama ve işlemler
Kavramsal olarak depset, genelde hedef grafiğe benzer. Yapraklardan köke kadar oluşturulur. Bağımlılık zincirindeki her hedef, önceki sayfaları kullanabilirsiniz.
DAG'deki her düğüm, doğrudan öğelerin ve alt düğümlerin bir listesini içerir. Depset'in içeriği, doğrudan öğeler gibi geçişli öğelerdir tümünü görebiliyoruz. Yeni bir depolama alanı oluşturmak için depset oluşturucu: Doğrudan bir öğelerinin ve başka bir alt düğümlerin listesi.
s = depset(["a", "b", "c"])
t = depset(["d", "e"], transitive = [s])
print(s) # depset(["a", "b", "c"])
print(t) # depset(["d", "e", "a", "b", "c"])
Bir kümenin içeriğini almak için şunu kullanın: to_list() yöntemini kullanın. Tüm geçişlilerin listesini döndürür öğelerinin (yinelenenler dahil) içermediğini unutmayın. Verileri doğrudan incelemenin bir yolu yoktur. kesin bir yapıda olmasa da bu yapı, DAG'nin hangi öğelerin döndürüleceğini belirler.
s = depset(["a", "b", "c"])
print("c" in s.to_list()) # True
print(s.to_list() == ["a", "b", "c"]) # True
Bir depoda izin verilen öğeler, şurada izin verilen anahtarlar gibi kısıtlanır: Sözlükler kısıtlandı. Özellikle, indirilmiş içerikler değiştirilemeyebilir.
Depset'ler referans eşitliği kullanır: "Depset" kendisine eşittir, ancak diğerlerine eşit değildir. aynı içeriğe ve aynı dahili yapıya sahip olsalar bile farklı birimleri kullanabilirler.
s = depset(["a", "b", "c"])
t = s
print(s == t) # True
t = depset(["a", "b", "c"])
print(s == t) # False
d = {}
d[s] = None
d[t] = None
print(len(d)) # 2
Denemeleri içeriklerine göre karşılaştırmak için bunları sıralanmış listelere dönüştürün.
s = depset(["a", "b", "c"])
t = depset(["c", "b", "a"])
print(sorted(s.to_list()) == sorted(t.to_list())) # True
Bir kullanımdan öğe kaldırmak mümkün değildir. Bu gerekiyorsa depset'in tüm içeriğini okumalı, istediğiniz öğeleri filtrelemelisiniz yeniden oluşturmayı öğreneceksiniz. Bu pek de faydalı değil.
s = depset(["a", "b", "c"])
t = depset(["b", "c"])
# Compute set difference s - t. Precompute t.to_list() so it's not done
# in a loop, and convert it to a dictionary for fast membership tests.
t_items = {e: None for e in t.to_list()}
diff_items = [x for x in s.to_list() if x not in t_items]
# Convert back to depset if it's still going to be used for union operations.
s = depset(diff_items)
print(s) # depset(["a"])
Sipariş
to_list
işlemi, DAG üzerinde bir geçiş gerçekleştirir. Geçişin türü
dağıtım sırasında belirtilen sıralamaya bağlıdır
somut olarak ortaya koyar. Bazen Bazel'in birden fazla siparişi desteklemesi faydalıdır çünkü bazen
araçların giriş sırasını önemser. Örneğin, bir bağlayıcı işlemi
B
, A
değerine bağlıysa A.o
, sayfada B.o
karakterinden önce gelir.
bağlayıcının komut satırında yer alır. Diğer araçlar için ise tam tersi bir gereklilik olabilir.
Üç geçiş siparişi desteklenir: postorder
, preorder
ve
topological
. İlk ikisi ağaç gibidir
geçişler
ancak DAG'lerde çalışır ve ziyaret edilmiş düğümleri atlar. Üçüncü sipariş
kökten yapraklara kadar topolojik bir sıralama işlevi görür.
yalnızca ebeveynlerinden sonra listelenen çocuklar için ön sipariş verilebilir.
Ön sipariş ve sonradan sipariş, soldan sağa geçişler olarak çalışır, ancak
her bir düğümün doğrudan öğesi, alt öğelere göre bir sıraya sahip değildir. Topolojik için
soldan sağa doğru garanti yoktur ve hatta
alt öğeden önce tümü garantisi
DAG'nin farklı düğümlerinde yinelenen öğeler.
# This demonstrates different traversal orders.
def create(order):
cd = depset(["c", "d"], order = order)
gh = depset(["g", "h"], order = order)
return depset(["a", "b", "e", "f"], transitive = [cd, gh], order = order)
print(create("postorder").to_list()) # ["c", "d", "g", "h", "a", "b", "e", "f"]
print(create("preorder").to_list()) # ["a", "b", "e", "f", "c", "d", "g", "h"]
# This demonstrates different orders on a diamond graph.
def create(order):
a = depset(["a"], order=order)
b = depset(["b"], transitive = [a], order = order)
c = depset(["c"], transitive = [a], order = order)
d = depset(["d"], transitive = [b, c], order = order)
return d
print(create("postorder").to_list()) # ["a", "b", "c", "d"]
print(create("preorder").to_list()) # ["d", "b", "a", "c"]
print(create("topological").to_list()) # ["d", "b", "c", "a"]
Geçişlerin uygulanma biçimi nedeniyle, sıra, zamanda belirtilmelidir
depset, oluşturucunun order
anahtar kelime bağımsız değişkeniyle oluşturulur. Bu
bağımsız değişken atlanırsa depset, özel default
sırasına sahiptir. Bu durumda,
unsurlarından herhangi birinin sıralamasıyla ilgili garanti verilmez (yalnızca
deterministiktir).
Tam örnek
Bu örneği şu adreste bulabilirsiniz: https://github.com/bazelbuild/examples/tree/main/rules/depsets.
Foo varsayıma dayalı yorumlanmış bir dil bulunduğunu varsayalım. Herkes için
her foo_binary
içindeki tüm *.foo
dosyalarını da bilmeniz gerekir veya
dolaylı olarak da değişir.
# //depsets:BUILD
load(":foo.bzl", "foo_library", "foo_binary")
# Our hypothetical Foo compiler.
py_binary(
name = "foocc",
srcs = ["foocc.py"],
)
foo_library(
name = "a",
srcs = ["a.foo", "a_impl.foo"],
)
foo_library(
name = "b",
srcs = ["b.foo", "b_impl.foo"],
deps = [":a"],
)
foo_library(
name = "c",
srcs = ["c.foo", "c_impl.foo"],
deps = [":a"],
)
foo_binary(
name = "d",
srcs = ["d.foo"],
deps = [":b", ":c"],
)
# //depsets:foocc.py
# "Foo compiler" that just concatenates its inputs to form its output.
import sys
if __name__ == "__main__":
assert len(sys.argv) >= 1
output = open(sys.argv[1], "wt")
for path in sys.argv[2:]:
input = open(path, "rt")
output.write(input.read())
Burada, d
ikili programının geçişli kaynakları *.foo
içindeki tüm dosyalardır
a
, b
, c
ve d
srcs
alanları. foo_binary
için
diğer dosyalar hakkında bilgilendirilmesi için foo_library
hedeflerinind.foo
sağlayıcıda iletebilirim. Her kütüphane kendi sağlayıcıları alır
bağımlılıklarını, kendi kaynaklarını ekler ve mevcut kaynaklarla
arttırılmış içeriklerdir. foo_binary
kuralı da aynısını yapar. Ancak bu kural,
bir kaynak listesi oluşturmak için tüm kaynakların listesini kullanır.
komut satırını kullanın.
foo_library
ve foo_binary
kurallarının eksiksiz bir şekilde nasıl uygulandığını aşağıda görebilirsiniz.
# //depsets/foo.bzl
# A provider with one field, transitive_sources.
FooFiles = provider(fields = ["transitive_sources"])
def get_transitive_srcs(srcs, deps):
"""Obtain the source files for a target and its transitive dependencies.
Args:
srcs: a list of source files
deps: a list of targets that are direct dependencies
Returns:
a collection of the transitive sources
"""
return depset(
srcs,
transitive = [dep[FooFiles].transitive_sources for dep in deps])
def _foo_library_impl(ctx):
trans_srcs = get_transitive_srcs(ctx.files.srcs, ctx.attr.deps)
return [FooFiles(transitive_sources=trans_srcs)]
foo_library = rule(
implementation = _foo_library_impl,
attrs = {
"srcs": attr.label_list(allow_files=True),
"deps": attr.label_list(),
},
)
def _foo_binary_impl(ctx):
foocc = ctx.executable._foocc
out = ctx.outputs.out
trans_srcs = get_transitive_srcs(ctx.files.srcs, ctx.attr.deps)
srcs_list = trans_srcs.to_list()
ctx.actions.run(executable = foocc,
arguments = [out.path] + [src.path for src in srcs_list],
inputs = srcs_list + [foocc],
outputs = [out])
foo_binary = rule(
implementation = _foo_binary_impl,
attrs = {
"srcs": attr.label_list(allow_files=True),
"deps": attr.label_list(),
"_foocc": attr.label(default=Label("//depsets:foocc"),
allow_files=True, executable=True, cfg="host")
},
outputs = {"out": "%{name}.out"},
)
Bu durumu, bu dosyaları yeni bir pakete kopyalayıp
etiketlerine uygun şekilde, kaynak *.foo
dosyalarını sahte içerikle oluşturma ve
d
hedefini oluşturacaksınız.
Performans
Depsy'leri kullanmak için motivasyonu öğrenmek istiyorsanız,
get_transitive_srcs()
, kaynaklarını bir listede topladı.
def get_transitive_srcs(srcs, deps):
trans_srcs = []
for dep in deps:
trans_srcs += dep[FooFiles].transitive_sources
trans_srcs += srcs
return trans_srcs
Bu durumda yinelemeler dikkate alınmaz. Bu nedenle a
için kaynak dosyalar
komut satırında iki kez, çıkışın içeriğinde de iki kez görünür
dosyası olarak kaydedebilirsiniz.
Alternatif bir yöntem de, bir simülasyon aracı tarafından simüle edilen genel bir küme kullanmaktır
Sözlüğün tamamındaki anahtarlar öğe, tüm anahtarlar ise True
ile eşlenir.
def get_transitive_srcs(srcs, deps):
trans_srcs = {}
for dep in deps:
for file in dep[FooFiles].transitive_sources:
trans_srcs[file] = True
for file in srcs:
trans_srcs[file] = True
return trans_srcs
Bu, yinelemeleri ortadan kaldırır ancak komut satırının sırasını belirler bağımsız değişkenler (ve dolayısıyla dosyaların içeriği) belirtilmemiştir ancak deterministik.
Dahası, her iki yaklaşım da bağımlılık temelli yaklaşımlardan asimptotik olarak daha kötüdür. bahsedeceğiz. Scrum Ekibi’nde uzun bir bağımlılık zincirinin olduğu Foo kitaplıkları. Her kuralın işlenmesi için tüm geçişli kuralların kopyalanması yeni veri yapısına entegre etmek için kullanılır. Bu, tek bir kitaplığın veya ikili hedefin analiz edilmesi için zaman ve alan maliyeti zincirdeki kendi yüksekliğiyle doğru orantılıdır. n uzunluğundaki bir zincir için, foolib_1 ← foolib_2 ← ... ← foolib_n, toplam maliyet etkin bir şekilde O(n^2) olur.
Genel olarak, geçişli bağımlılıklarınızla bilgi edinmeye çalışır. Böylece ekibin ve hedef grafiğiniz derinleşir.
Son olarak, depset öğesinin içeriğini almamak önemlidir.
gereksiz yere kullanmamalıdır. to_list()
için bir arama
toplam maliyet yalnızca O(n) olduğundan, bir ikili kuralının sonunda herhangi bir sorun yoktur. İnsanların
terminal olmayan birçok hedef to_list()
işlemini, ikinci dereceden davranışın
gerçekleşir.
Uygulamaları verimli bir şekilde kullanma hakkında daha fazla bilgi için performans sayfasını inceleyin.
API Referansı
Daha fazla bilgiyi burada bulabilirsiniz.