Mengoptimalkan simulasi penerbangan dengan stabilitas tipe yang ditingkatkan

 – Beragampengetahuan
13 mins read

Mengoptimalkan simulasi penerbangan dengan stabilitas tipe yang ditingkatkan – Beragampengetahuan

Oleh: Big Lake Consulting

Re -Post dari:

Posting ini ditulis oleh Steven Whitaker.

Dalam hal ini Julia untuk pengembang Posting, kita akan membahas peningkatan kode simulasi kinerja yang ditulis dalam Juliaby menghilangkan sumber interaksi.

Kami telah menulis posting lain tentang stabilitas apa yang ada dan bagaimana kekuatan dapat merusak kinerja. Kami juga menunjukkan bagaimana SnoopCompile.jl dan cthulhu.jlcan digunakan untuk mengidentifikasi penyebab jenisnya.

Posting ini akan mencakup beberapa instabilitas yang telah membantu salah satu pelanggan kami untuk diatasi.

Pelanggan kami adalah inovasi teknologi. Mereka membangun sistem logistik pertama untuk fokus pada distribusi listrik otonom untuk mengurangi polusi lalu lintas dan polusi udara. Tujuannya adalah untuk menyediakan layanan distribusi yang efektif untuk menyelamatkan produsen di daerah perkotaan dan pedesaan.

Dengan pelanggan ini, kami:

  • Menghilangkan kegagalan yang terkait dengan memperlambat skenario simulasi yang paling penting.
  • Pengurangan 30%dalam waktu terjemahan skrip.
  • Tingkatkan langkah waktu paling lambat dari 300 ms menjadi 10 ms (kecepatan 30 kali), memungkinkan kinerja real -Time 2 kali.

Kami tidak akan berbagi kode untuk pelanggan, tetapi akan memberikan tes serupa untuk menggambarkan masalah penyebab asli dan resolusi yang diusulkan.

Berikut adalah masalah dan resolusi dari penyebab asli yang akan kami fokuskan:

  • Membantu jenis penalaran dengan tidak mengendalikan rekursif.
  • Standar output dari berbagai cabang.
  • Hindari loopTupleS.
    • Gunakan Snoopompile.jl untuk mengungkapkan pengiriman dinamis.
    • Selidiki fungsi dengan cthulhu.jl.
  • Hindari pemetaan kamus ke fungsi.

Silakan pergi jauh!

Salah satu masalah menarik yang kami temukan bahwa ada bagian dari codethat snoopCompile.jl pelanggan yang melaporkan panggilan masuk, tetapi ketika kami memeriksa kode dengan cthulhu.jl kode, itu terlihat benar -benar stabil.

Kode ini mencakup serangkaian fungsi yang disebut rekursif, melewati koleksi data model Treeto dari semua sub -model.

Ternyata, rekursif dapat menyulitkan jenis penalaran Julia. Dalam hal, jika jenis penemuan rekursif tidak dapat membuktikannya berakhir (hanya berdasarkan jenis input.

Solusi yang telah kami terapkan untuk digunakan @generated Fungsi pembatalan rekursif pada saat kompilasi, yang mengarah ke implementasi datar dapat disimpulkan dengan benar.

Di bawah ini adalah contoh dari sifat ilustratif dari kode rekursif:

get_data(model::NamedTuple) = (; data = model.data, submodels = get_submodel_data(model.submodels))@generated function get_submodel_data(submodels::NamedTuple)    assignments = map(fieldnames(submodels)) do field        Expr(:kw, field, :(get_data(submodels.$field)))    end    return Expr(:tuple, Expr(:parameters, assignments...))endget_submodel_data(::@NamedTuple) = (;)

Perhatikan bahwa dalam contoh iniget_data panggilan get_submodel_dataTapi pada gilirannya panggilan get_dataDi sub -model.

Ini adalah kode untuk tidak mengontrol rekursif:

function _gen_get_data(T, path)    subT = _typeof_field(T, :submodels)    subpath = :($path.submodels)    return quote        (;            data = $path.data,            submodels = $(_gen_get_submodel_data(subT, subpath)),        )    endendfunction _typeof_field(::TypeNamedTuplenames, T, field::Symbol) where names, T    i = findfirst(n -> n === field, names)    return T.parameters[i]endfunction _gen_get_submodel_data(::Type@NamedTuple, subpath)    return quote        (;)    endendfunction _gen_get_submodel_data(subT, subpath)    assignments = map(fieldnames(subT)) do field        T = _typeof_field(subT, field)        path = :($subpath.$field)        Expr(:kw, field, _gen_get_data(T, path))    end    return Expr(:tuple, Expr(:parameters, assignments...))end@generated get_data_generated(model::NamedTuple) = _gen_get_data(model, :model)

Sayangnya, contoh ini tidak meregenerasi masalah yang dihadapi pelanggan kami, tetapi menunjukkan cara menggunakan @generated Fungsi untuk membatalkan rekursif. Catatan bahwa masih ada rekursif:_gen_get_data Dan _gen_get_submodel_data Memanggil satu sama lain. sebelum Disimpulkan, itu berarti kapan get_data_generated Disimpulkan, rekursif, yang mengarah ke codewithout yang tidak diobati dapat menyebabkan masalah deduksi.

Ketika kami menggunakan solusi ini untuk pelanggan kami, kami melihat memori total mereka menggunakan SimulationDecrease ~ 35%. Ini cukup untuk memungkinkan mereka menonaktifkan koleksi sampah, mempercepat lebih cepat dari waktu nyata! Dan ini adalah simulasi Timethis pertama yang berjalan lebih cepat dari waktu nyata!

Pelanggan memiliki bagian yang berbeda dalam pemodelan mereka pada frekuensi yang berbeda. Akibatnya, pada waktu tertentu adalah subset dari semua sub -model untuk diperbarui. Ini adalah contoh dari apa ini:

function get_output(model, t)    if should_update(model, t)        out = update_model(model, t)    else        out = get_previous_output(model)    end    return outend

Sayangnya,update_model Dan get_previous_outputNilai yang dikembalikan dari berbagai jenis, yang mengarah ke jenis: jenis output get_outputTergantung pada hasil waktu berjalan should_update.

Selain itu, fungsi ini dipanggil pada setiap titik dari setiap sub -model (dan setiap submodel, dll.), Jadi jenis fungsi ini telah mempengaruhi seluruh simulasi.

Masalahnya adalah update_modelSering mengembalikan pengumpulan informasi minimum informasi yang diperlukan untuk model spesifik, sementara get_previous_output Genericand mengembalikan serangkaian informasi yang lebih luas. Misalnya, mungkin update_model akan mengembalikan a NamedTuplesama (x = [1, 2], xdot = [0, 0]),ketika get_previous_output Bagaimana itu akan kembali? (x = [1, 2], xdot = [0, 0], p = nothing, stop_sim = false).

Untuk memperbaiki masalah ini, alih -alih memperbarui nilai secara manual yang mengembalikan semua metode update_modelUntuk semua sub -model dalam sistem, kami telah membuat fungsi standardize_outputItu membutuhkan apapun NamedTuple Kembali oleh update_modeldan menambahkan sekolah yang kurang get_previous_output termasuk. Setelah itu, satu -satunya perubahan yang dibutuhkan get_outputadalah menelepon standardize_output:

out = update_model(model, t) |> standardize_output

Hasil perubahan ini dikurangi sebesar 30% dari waktu terjemahan untuk simulasi mereka!

Pelanggan disimpan di sub -model Tuple atau NamedTuple. Ini penting untuk stabilitas tipe karena setiap sub -model adalah satu -satunya jenis, jadi menyimpannya dengan cara ini adalah informasi yang dipertahankan saat mengakses sub -model. VectorAnyAkan kehilangan informasi sub -model.

Namun, masalah stabilitas muncul saat diulang TupleSof berbagai jenis objek. Masalahnya adalah bahwa kode kompiler diperlukan untuk tubuh loop, tetapi tubuh loop perlu diproses TupleAkibatnya, kompiler harus menggunakan koordinasi badan loop (tetapi lihat catatan tentang aliansi di bawah).

Presiden menunggu resepsionis menemukan kantornya

Ini adalah contoh masalahnya:

module TupleLoopfunction tupleloop(t::Tuple)    for val in t        do_something(val)    endenddo_something(val::Number) = val + 1do_something(val::String) = val * "!"do_something(val::VectorT) where T = isempty(val) ? zero(T) : val[1]do_something(val::DictString,Int) = get(val, "hello", 0)end

Menggunakan snoopCompile.jl menunjukkan pengiriman dinamis do_something:

julia> using SnoopCompileCorejulia> tinf = @snoop_inference TupleLoop.tupleloop((1, 2.0, "hi", [10.0], DictString,Int()));julia> using SnoopCompilejulia> tinfInferenceTimingNode: 0.019444/0.020361 on Core.Compiler.Timings.ROOT() with 4 direct childrenjulia> itrigs = inference_triggers(tinf); mtrigs = accumulate_by_source(Method, itrigs)2-element VectorSnoopCompile.TaggedTriggersMethod: eval_user_input(ast, backend::REPL.REPLBackend, mod::Module) @ REPL ~/.julia/juliaup/julia-1.11.5+0.x64.linux.gnu/share/julia/stdlib/v1.11/REPL/src/REPL.jl:247 (1 callees from 1 callers) tupleloop(t::Tuple) @ Main.TupleLoop /path/to/TupleLoop.jl:3 (3 callees from 1 callers)

Lihat tupleloop Dengan cthulhu.jl:

julia> using Cthulhujulia> ascend(mtrigs[2].itrigs[1])Choose a call for analysis (q to quit):     do_something(::String) >     tupleloop(::TupleInt64, Float64, String, VectorFloat64, DictString, Int64) at /path/to/TupleLoop.jltupleloop(t::Tuple) @ Main.TupleLoop /path/to/TupleLoop.jl:3 3 function tupleloop(t::TupleInt64, Float64, String, VectorFloat64, DictString, Int64::Tuple)::Core.Const(nothing) 5 5     for val::Any in t::TupleInt64, Float64, String, VectorFloat64, DictString, Int64 6         do_something(val::Any) 7     end 9 9 end

Dan kita melihat masalahnya! Meskipun Tuple Disimpulkan, variabel loop val Disimpulkan Anyberarti menelepon do_something(val)Harus menjadi pengiriman yang dinamis.

Perhatikan bahwa dalam beberapa kasus, divisi otomatis secara otomatis dapat dilakukan untuk menghilangkan pengiriman dinamis berdasarkan jenis ini. TupleBerisi 4 (secara default) atau beberapa jenis. Namun, ini bukan solusi umum.

Salah satu cara untuk menghilangkan pengiriman dinamis akan bergantung pada aliansi untuk menghapus loop:

do_something(t[1])do_something(t[2])

Tapi kita dapat dengan cepat menulis kode ini tidak semua; Kita harus mengenkripsi jumlah panggilan masuk do_somethingItu berarti kode hanya akan berfungsi TupleS memiliki panjang tertentu. Sayangnya, ada jalan di sekitar masalah ini. Kita bisa menulis satu @generated Fungsi untuk kompiler untuk membatalkan loop untuk kami secara umum:

@generated function tupleloop_generated(t::Tuple)    body = [:(do_something(t[$i])) for i in fieldnames



(Perhatikan bahwa kode ini juga akan berfungsi jika kami telah menentukan t::NamedTupleDalam tanda tanda tangan.)

Oleh @generated Fungsi operasi, snoopCompile.jl masih menemukan pengiriman dinamis, tetapi perhatikan itu tupleloop_generatedTidak ada pengiriman dinamis yang dilaporkan:

julia> using SnoopCompileCorejulia> tinf = @snoop_inference TupleLoop.tupleloop_generated((1, 2.0, "hi", [10.0], DictString,Int()));julia> using SnoopCompilejulia> tinfInferenceTimingNode: 0.022208/0.050369 on Core.Compiler.Timings.ROOT() with 5 direct childrenjulia> itrigs = inference_triggers(tinf); mtrigs = accumulate_by_source(Method, itrigs)3-element VectorSnoopCompile.TaggedTriggersMethod: (g::Core.GeneratedFunctionStub)(world::UInt64, source::LineNumberNode, args...) @ Core boot.jl:705 (1 callees from 1 callers) eval_user_input(ast, backend::REPL.REPLBackend, mod::Module) @ REPL ~/.julia/juliaup/julia-1.11.5+0.x64.linux.gnu/share/julia/stdlib/v1.11/REPL/src/REPL.jl:247 (1 callees from 1 callers) var"#s1#1"(::Any, t) @ Main.TupleLoop none:0 (3 callees from 1 callers)

Dan kita dapat memverifikasi dengan cthulhu.jlThat tidak lagi pengiriman dinamis tupleloop_generated:

julia> using Cthulhujulia> ascend(mtrigs[2].itrigs[1])Choose a call for analysis (q to quit): >   tupleloop_generated(::TupleInt64, Float64, String, VectorFloat64, DictString, Int64)       eval at ./boot.jl:430 => eval_user_input(::Any, ::REPL.REPLBackend, ::Module) at /cache/build/tester-amdci5-12/julialang/julia-release-1-dot-1tupleloop_generated(t::Tuple) @ Main.TupleLoop /path/to/TupleLoop.jl:11Variables    t::TupleInt64, Float64, String, VectorFloat64, DictString, Int64Body::Core.Const(nothing)    @ /path/to/TupleLoop.jl:11 within `tupleloop_generated`    @ /path/to/TupleLoop.jl:15 within `macro expansion`1  %1  = Main.TupleLoop.do_something::Core.Const(Main.TupleLoop.do_something)   %2  = Base.getindex(t, 1)::Int64         (%1)(%2)   %4  = Main.TupleLoop.do_something::Core.Const(Main.TupleLoop.do_something)   %5  = Base.getindex(t, 2)::Float64         (%4)(%5)   %7  = Main.TupleLoop.do_something::Core.Const(Main.TupleLoop.do_something)   %8  = Base.getindex(t, 3)::String         (%7)(%8)   %10 = Main.TupleLoop.do_something::Core.Const(Main.TupleLoop.do_something)   %11 = Base.getindex(t, 4)::VectorFloat64         (%10)(%11)   %13 = Main.TupleLoop.do_something::Core.Const(Main.TupleLoop.do_something)   %14 = Base.getindex(t, 5)::DictString, Int64         (%13)(%14)   @ /path/to/TupleLoop.jl:16 within `macro expansion`       return Main.TupleLoop.nothing   

Di sini kita harus memeriksa kode yang diketik secara online SO (karena kode sumber dibuat melalui metaprogramming), tetapi kami menemukan bahwa tidak ada loop dalam kode ini. do_somethingPengiriman statis dengan input spesifik.

Pelanggan telah mendaftarkan fungsi pembaruan yang jelas untuk mensimulasikan kamus yang dipetakan dari satu String Kunci untuk fungsi pembaruan yang sesuai.

Terkadang dapat lebih mudah untuk memiliki kamus fungsional, misalnya:

d = DictString, Function(    "sum" => sum,    "norm" => norm,    )x = [1.0, 2.0, 3.0]d["sum"](x) d["norm"](x) 

Ini memungkinkan Anda untuk menulis enkripsi umum dapat memanggil fungsi perantara yang sesuai yang disediakan oleh kunci yang disediakan oleh penelepon.

Anda dapat menggunakan banyak pengiriman resmi untuk mencapai hasil yang sama, tetapi membutuhkan sedikit lebih banyak untuk mengatur kode sedemikian rupa untuk memastikan penelepon memiliki akses ke tipe yang akan dikirim.

Sebagai gantinya, Anda juga dapat memiliki Callerjust dalam fungsi untuk menelepon. Tetapi sekali lagi, dibutuhkan sedikit lebih banyak upaya untuk mengatur Codeto agar berhasil.

Sayangnya, menggunakan Kamus Gaya Wayis ini: Julia tidak dapat menemukan fungsi yang akan disebut waktu berjalan, ketika tidak mengetahui kunci kamus yang benar. Dan karena fungsinya tidak diketahui, jenis hasil fungsi tidak diketahui.

Solusi parsial untuk menggunakan keterangan:

d[func_key](x)::Float64

Kemudian setidaknya output fungsi digunakan dengan cara yang stabil. Namun, ini hanya berfungsi jika semua fungsi dalam nilai kamus dari input yang sama adalah serupa.

Penggantian yang sedikit ketat untuk mengubah hasil dengan jelas menjadi tipe yang umum, tetapi ini membutuhkan konversi menjadi mungkin.

Pelanggan kami telah memperbarui kamus menggunakan output dari fungsi terdaftar, sehingga solusi penuh yang telah kami gunakan untuk klien kami untuk menghapus kamus dan menggantinya.

updates[key] = d[key](updates[key])

Kami punya

if key == "k1"    updates[key] = f1(updates[key]::OUTPUT_TYPE_F1)elseif key == "k2"    updates[key] = f2(updates[key]::OUTPUT_TYPE_F2)end

Perhatikan bahwa kita membutuhkan kategoriOUTPUT_TYPE_F1 Dan OUTPUT_TYPE_F2Karena updates Ada jenis nilai yang diabstraksikan. Kunci membuat solusi ini berfungsi untuk menyadari bahwa di cabang pertamaupdates[key] adalah output dari f1Dari langkah sebelumnya dalam simulasi (dan mirip dengan cabang lain). Oleh karena itu, di setiap cabang, kita tahu jenisnya updates[key] Adalah, jadi kami dapat memberikan terjemahan informasi jenis itu.

Perhatikan juga bahwa ide -ide yang disebutkan sebelumnya menggunakan banyak koordinator hanya perlu ditransmisikan dalam fungsi untuk menggunakan tidak aktif dalam situasi ini saat menghilangkan updates Kamus (dan kode yang terpengaruh).

Membuat tipe in-in-in-in-in-in-in-in-in-in-in-incere dari COUSTER CODE.

Dalam posting ini, kami menemukan beberapa masalah yang terkait dengan stabilitas jenis ini, kami membantu pelanggan kami menyelesaikan. Kita dapat mendiagnosis masalah snoopCompile.jl dan cthulhu.

Bisakah Anda memiliki interaksi yang menyebabkan kode Julia Anda? Hubungi kami dan kami dapat membantu Anda!

Gambar sampul seseorang saat memulai balap ditemukan di freepik.com.

Menghidupkan tentang pengulangan TupleDengan siapa swas diciptakan.

]>>

Contents

Software Terbaru Saat Ini



Aplikasi yang sedang trend saat ini

object oriented programming, programming language, programming adalah, web programming, belajar programming, tournament software, software, software adalah, contoh software, apa itu software, pengertian software, aplikasi, aplikasi penghasil uang, aplikasi bokep, aplikasi video, programming

#Mengoptimalkan #simulasi #penerbangan #dengan #stabilitas #tipe #yang #ditingkatkan

Tinggalkan Balasan

Alamat email Anda tidak akan dipublikasikan. Ruas yang wajib ditandai *