Julia: kompilasi ulang otomatis dari fungsi dependen

Dibuat pada 26 Nov 2011  ·  55Komentar  ·  Sumber: JuliaLang/julia

jika saya mendefinisikan fungsi d2 sebelum fungsi d1 yang memanggil d2 kemudian mengubah d2, d1 menggunakan definisi lama untuk d2.
Saya berasumsi ini karena semuanya telah dikompilasi sebelumnya, tetapi mungkin harus ada catatan peringatan tentang ini? Atau apakah mungkin untuk mengganti definisi lama dengan longjmp ke yang baru?
(Paling penting untuk REPL, karena saya tidak selalu melakukan beban penuh)

julia> function d2()
       a
       end

julia> function d()
         d2()
       end

julia> d()
in d: a not defined

julia> function d2()
       b=2
       end

julia> d()
in d: a not defined

julia> d2
Methods for generic function d2
d2() at prompt:2

julia> function d()
         d2()
       end

julia> d()
2
bug

Komentar yang paling membantu

Wow. Momen yang luar biasa!

Semua 55 komentar

Saya percaya ini karena kode untuk d2() dihasilkan ketika fungsi didefinisikan sehingga metode d() diselesaikan pada saat itu. Seperti yang Anda catat, Anda dapat mendefinisikan kembali d2() seperti yang awalnya didefinisikan agar berfungsi seperti yang diharapkan. Idealnya, kami akan secara otomatis mendefinisikan ulang apa pun yang bergantung pada d() ketika diubah seperti ini, tetapi kami tidak memiliki metadata apa pun yang memungkinkannya. Saya kira kita bisa memberi peringatan tentang perilaku ini di manual. Lebih baik lagi memperbaikinya, tetapi saya pikir ini agak sulit untuk dilakukan: - /

Saya berharap tidak ada yang akan memperhatikan ini, tetapi saya rasa itu tidak realistis bagi saya :)

Lebih menyenangkan dari itu; itu benar-benar memungkinkan Anda melihat JIT di tempat kerja, karena tidak terselesaikan hingga eksekusi:

julia> function f2()
       a
       end

julia> function f1()
       f2()
       end

julia> function f2()
       2
       end

julia> f1()
2

Namun, saya khawatir ini dapat menyebabkan beberapa hasil yang tidak terduga di REPL, meskipun saya tidak tahu cara terbaik untuk menghadapinya. Meskipun saya menggunakan kesalahan untuk menunjukkan perbedaan dalam gaya dramatis, itu juga bisa terjadi jika saya mencoba melihat efek dari mengubah persamaan dalam atau konstanta!

Ada dua jenis lokasi yang mungkin perlu diperbarui saat metode diubah:

  • tempat di mana metode disebut secara eksplisit sebagai fungsi (call / ret)
  • tempat di mana metode telah disebariskan

Yang pertama dapat diperbarui hanya dengan mengubah badan fungsi: dapat diubah di tempat, atau situs pemanggil dapat diperbarui secara aktif untuk memanggil lokasi baru, atau badan fungsi lama dapat diganti dengan rintisan yang melompat ke versi baru , secara opsional menambal situs pemanggil. Untuk menyebariskan, kita perlu melacak penelepon yang memiliki fungsi sebaris dan melakukan JIT ulang.

Sungguh, yang bisa kita lakukan hanyalah membuang kode, karena meskipun metode tidak sebaris fungsi apa pun yang memanggilnya mungkin bergantung pada perilaku tipe yang bisa berubah jika metode berubah. Ini sebagian besar untuk kasus interaktif sehingga mengompilasi ulang kode bukanlah masalah besar.
Membutuhkan pekerjaan yang sama seperti masalah # 47.

Kekecewaan. Nah, karena yang terpengaruh terutama adalah replay, melakukan hal yang benar secara perlahan tidak masalah. Pada saat berjalan, ini seharusnya tidak pernah muncul - mengapa seseorang mendefinisikan sesuatu dan kemudian mendefinisikannya kembali kecuali secara interaktif? Jika solusinya akhirnya menimbulkan banyak overhead, mungkin mengubahnya menjadi opsional dan secara otomatis dilakukan di repl?

Kami perlu mendokumentasikan bahwa ini tidak ditentukan saat ini.

Secara teknis, ini bukan bug, ini adalah perilaku yang tidak ditentukan. Saat Anda mendefinisikan ulang metode, perilaku yang dihasilkan tidak ditentukan. Jadi Julia bisa melakukan apapun yang dia suka, termasuk apa yang dilakukannya saat ini. Menyediakan perilaku yang terdefinisi dengan baik pada redefinisi metode adalah fitur, bukan perbaikan bug. Saya juga tidak yakin bahwa ini adalah masalah v1.0 karena beralih dari perilaku yang tidak ditentukan ke memberikan perilaku yang terdefinisi dengan baik bukanlah perubahan yang mengganggu. Ini dapat diterapkan di v1.1 tanpa melanggar kode v1.0 yang valid.

Greg Clayton dari staf LLVM / LLDB Apple berbaik hati mendokumentasikan cara memperoleh (dengan pustaka lldb, subproyek llvm) informasi yang diperlukan untuk menentukan dependensi biner dari info simbol yang disematkan (impor simbol); serta simbol yang diekspor oleh biner (diperlukan untuk membuat grafik dependensi lengkap).

  • Jason

Pada tanggal 31 Maret 2012, pukul 23.02, Jason E. Aten menulis:

Para penggemar LLDB yang terhormat,

Saya ingin tahu apakah saya dapat menggunakan lldb library / libraries untuk mengganti kode tertentu yang berjalan di OSX yang sekarang mengembalikan dua daftar simbol-- mirip dengan output (dyldinfo -lazy_bind -exports); yaitu saya perlu membuat daftar simbol yang diimpor dan diekspor oleh objek atau bundel bersama biner.

Harapan saya adalah dengan menggunakan pustaka lldb, saya akan dapat menggunakan kode klien yang sama di OSX seperti di linux. (Versi linux dari kode saat ini menggunakan libbfd dan libdld untuk melakukan hal yang sama, tetapi belakangan hanya mendapatkan sedikit cinta / pemeliharaan).

Saya mencari melalui include / lldb /, karena sepertinya lldb akan membutuhkan info yang sama ini (daftar simbol yang diimpor, dan daftar simbol yang diekspor untuk file Mach-O) agar berfungsi, tetapi tidak jelas API mana yang akan digunakan. Semua saran / petunjuk ke kode contoh di lldb akan diterima!

Terima kasih.
Jason

Jika tidak jelas apa yang dilakukan dyldinfo, berikut ini contohnya: (tetapi saya hanya perlu nama simbol; bukan alamat atau segmen atau bagian):

$ file / tmp / sample_bundle
/ tmp / sample_bundle: Mach-O 64-bit bundel x86_64

$ dyldinfo -lazy_bind -export / tmp / sample_bundle

informasi pengikatan malas (dari bagian lazy_bind dari info dyld):
segmen bagian alamat indeks simbol dylib
__DATA __la_symbol_ptr 0x00001030 0x0000 ruang nama datar __mm_pop_chunk
__DATA __la_symbol_ptr 0x00001038 0x0015 ruang nama datar _dh_define
mengekspor informasi (dari trie):
0x000008A0 _C_ipair
0x00000920 _init_ipair
0x00000BC0 _C_iprot
0x00000C40 _C_ipi2
0x00000CC0 _C_ipi1
0x00001040 _K_ipair_R43808f40
0x00001160 _K_ipi1_R5cb4475d
0x00001260 _K_ipi2_R5cb4475d
0x00001360 _K_iprot_Rfc8fe739
0x00001460 _majver_ipair
0x00001464 _minver_ipair

Pada hari Senin, 2 April 2012 pukul 15.13 , Greg Clayton

Yes you can do this with LLDB. If you load a binary and dump its symbol table, you will see the information you want. For symbols that are lazily bound, you can look for "Trampoline" symbols:

cd lldb/test/lang/objc/foundation
make
lldb a.out
(lldb) target modules dump symtab a.out
Symtab, file = .../lldb/test/lang/objc/foundation/a.out, num_symbols = 54:
              Debug symbol
              |Synthetic symbol
              ||Externally Visible
              |||
Index   UserID DSX Type         File Address/Value Load Address       Size               Flags      Name
------- ------ --- ------------ ------------------ ------------------ ------------------ ---------- ----------------------------------
[    0]      0 D   SourceFile   0x0000000000000000                    Sibling -> [   15] 0x00640000 /Volumes/work/gclayton/Documents/src/lldb/test/lang/objc/foundation/main.m
[    1]      2 D   ObjectFile   0x000000004f79f1ca                    0x0000000000000000 0x00660001 /Volumes/work/gclayton/Documents/src/lldb/test/lang/objc/foundation/main.o
[    2]      4 D   Code         0x00000001000010f0                    0x00000000000000c0 0x000e0000 -[MyString initWithNSString:]
[    3]      8 D   Code         0x00000001000011b0                    0x0000000000000090 0x000e0000 -[MyString dealloc]
[    4]     12 D   Code         0x0000000100001240                    0x00000000000000a0 0x000e0000 -[MyString description]
[    5]     16 D   Code         0x00000001000012e0                    0x0000000000000020 0x000e0000 -[MyString descriptionPauses]
[    6]     20 D   Code         0x0000000100001300                    0x0000000000000030 0x000e0000 -[MyString setDescriptionPauses:]
[    7]     24 D   Code         0x0000000100001330                    0x0000000000000030 0x000e0000 -[MyString str_property]
[    8]     28 D   Code         0x0000000100001360                    0x0000000000000050 0x000e0000 -[MyString setStr_property:]
[    9]     32 D   Code         0x00000001000013b0                    0x0000000000000040 0x000f0000 Test_Selector
[   10]     36 D   Code         0x00000001000013f0                    0x0000000000000130 0x000f0000 Test_NSString
[   11]     40 D   Code         0x0000000100001520                    0x0000000000000120 0x000f0000 Test_MyString
[   12]     44 D   Code         0x0000000100001640                    0x00000000000001b0 0x000f0000 Test_NSArray
[   13]     48 D   Code         0x00000001000017f0                    0x00000000000000e1 0x000f0000 main
[   14]     56 D X Data         0x0000000100002680                    0x0000000000000000 0x00200000 my_global_str
[   15]     58 D   SourceFile   0x0000000000000000                    Sibling -> [   19] 0x00640000 /Volumes/work/gclayton/Documents/src/lldb/test/lang/objc/foundation/my-base.m
[   16]     60 D   ObjectFile   0x000000004f79f1ca                    0x0000000000000000 0x00660001 /Volumes/work/gclayton/Documents/src/lldb/test/lang/objc/foundation/my-base.o
[   17]     62 D   Code         0x00000001000018e0                    0x0000000000000020 0x000e0000 -[MyBase propertyMovesThings]
[   18]     66 D   Code         0x0000000100001900                    0x000000000000001f 0x000e0000 -[MyBase setPropertyMovesThings:]
[   19]     82     Data         0x0000000100002000                    0x0000000000000460 0x000e0000 pvars
[   20]     83     ObjCIVar     0x0000000100002518                    0x0000000000000148 0x001e0000 MyBase.propertyMovesThings
[   21]     84   X Data         0x0000000100002660                    0x0000000000000008 0x000f0000 NXArgc
[   22]     85   X Data         0x0000000100002668                    0x0000000000000008 0x000f0000 NXArgv
[   23]     86   X ObjCClass    0x00000001000024d8                    0x0000000000000028 0x000f0000 MyBase
[   24]     87   X ObjCClass    0x0000000100002460                    0x0000000000000028 0x000f0000 MyString
[   25]     88   X ObjCIVar     0x0000000100002510                    0x0000000000000008 0x000f0000 MyString._desc_pauses
[   26]     89   X ObjCIVar     0x0000000100002508                    0x0000000000000008 0x000f0000 MyString.date
[   27]     90   X ObjCIVar     0x0000000100002500                    0x0000000000000008 0x000f0000 MyString.str
[   28]     91   X ObjCMetaClass 0x00000001000024b0                    0x0000000000000028 0x000f0000 MyBase
[   29]     92   X ObjCMetaClass 0x0000000100002488                    0x0000000000000028 0x000f0000 MyString
[   30]     97   X Data         0x0000000100002678                    0x0000000000000008 0x000f0000 __progname
[   31]     98   X Data         0x0000000100000000                    0x00000000000010b0 0x000f0010 _mh_execute_header
[   32]     99   X Data         0x0000000100002670                    0x0000000000000008 0x000f0000 environ
[   33]    101   X Data         0x0000000100002680                    0x0000000000000000 0x000f0000 my_global_str
[   34]    102   X Code         0x00000001000010b0                    0x0000000000000040 0x000f0000 start
[   35]    103     Trampoline   0x0000000100001938                    0x0000000000000006 0x00010200 NSLog
[   36]    104   X Undefined    0x0000000000000000                    0x0000000000000000 0x00010400 OBJC_CLASS_$_NSArray
[   37]    105   X Undefined    0x0000000000000000                    0x0000000000000000 0x00010200 OBJC_CLASS_$_NSAutoreleasePool
[   38]    106   X Undefined    0x0000000000000000                    0x0000000000000000 0x00010400 OBJC_CLASS_$_NSDate
[   39]    107   X Undefined    0x0000000000000000                    0x0000000000000000 0x00010400 OBJC_CLASS_$_NSObject
[   40]    108   X Undefined    0x0000000000000000                    0x0000000000000000 0x00010200 OBJC_CLASS_$_NSString
[   41]    109   X Undefined    0x0000000000000000                    0x0000000000000000 0x00010400 OBJC_METACLASS_$_NSObject
[   42]    110   X Undefined    0x0000000000000000                    0x0000000000000000 0x00010400 __CFConstantStringClassReference
[   43]    111   X Undefined    0x0000000000000000                    0x0000000000000000 0x00010100 _objc_empty_cache
[   44]    112   X Undefined    0x0000000000000000                    0x0000000000000000 0x00010100 _objc_empty_vtable
[   45]    113     Trampoline   0x000000010000193e                    0x0000000000000006 0x00010300 exit
[   46]    114     Trampoline   0x0000000100001920                    0x0000000000000006 0x00010100 objc_getProperty
[   47]    115     Trampoline   0x0000000100001926                    0x0000000000000006 0x00010100 objc_msgSend
[   48]    116     Trampoline   0x000000010000192c                    0x0000000000000006 0x00010100 objc_msgSendSuper2
[   49]    117   X Undefined    0x0000000000000000                    0x0000000000000000 0x00010100 objc_msgSend_fixup
[   50]    118     Trampoline   0x0000000100001932                    0x0000000000000006 0x00010100 objc_setProperty
[   51]    119     Trampoline   0x0000000100001944                    0x0000000000000006 0x00010300 printf
[   52]    120     Trampoline   0x000000010000194a                    0x0000000000000006 0x00010300 usleep
[   53]    121   X Undefined    0x0000000000000000                    0x0000000000000000 0x00010300 dyld_stub_binder
(lldb)


All lazily bound symbols will have type Trampoline:

[   45]    113     Trampoline   0x000000010000193e                    0x0000000000000006 0x00010300 exit
[   46]    114     Trampoline   0x0000000100001920                    0x0000000000000006 0x00010100 objc_getProperty
[   47]    115     Trampoline   0x0000000100001926                    0x0000000000000006 0x00010100 objc_msgSend
[   48]    116     Trampoline   0x000000010000192c                    0x0000000000000006 0x00010100 objc_msgSendSuper2
[   50]    118     Trampoline   0x0000000100001932                    0x0000000000000006 0x00010100 objc_setProperty
[   51]    119     Trampoline   0x0000000100001944                    0x0000000000000006 0x00010300 printf
[   52]    120     Trampoline   0x000000010000194a                    0x0000000000000006 0x00010300 usleep

The other symbols that are exernal are marked with an "X" (which is a boolean flag on each symbol).

The symbols can be accessed via the SBModule:

   size_t
   SBModule::GetNumSymbols ();

   lldb::SBSymbol
   SBModule::GetSymbolAtIndex (size_t idx);

And then you can get the symbol type from each SBSymbol:

   SymbolType
   SBSymbol::GetType ();


I just added the ability to see if a symbol is externally visible:
% svn commit
Sending        include/lldb/API/SBSymbol.h
Sending        scripts/Python/interface/SBSymbol.i
Sending        source/API/SBSymbol.cpp
Transmitting file data ...
Committed revision 153893.


   bool
   SBSymbol::IsExternal();


So your flow should be:

SBDebugger::Initialize();
SBDebugger debugger(SBDebugger::Create());
SBTarget target (debugger.CreateTarget (const char *filename,
                                       const char *target_triple,
                                       const char *platform_name,
                                       bool add_dependent_modules,
                                       lldb::SBError& error));

SBFileSpec exe_file_spec (filename);
SBModule exe_module (target.FindModule(exe_file_spec));
if (exe_module.IsValid()
{
   const size_t num_symbols = exe_module. GetNumSymbols();
   for (size_t i=0; i<num_symbols; ++i)
   {
       SBSymbol symbol (exe_module. GetSymbolAtIndex(i));
       if (symbol.IsExternal())
       {
       }

       if (symbol.GetType() == lldb::eSymbolTypeTrampoline)
       {
       }
   }
}




> _______________________________________________
> lldb-dev mailing list
> [email protected]
> http://lists.cs.uiuc.edu/mailman/listinfo/lldb-dev

Pada hari Sen, 2 Apr 2012 jam 16.05, Greg Clayton [email protected] menulis:

A quick clarification on the args to CreateTarget:

"filename" is just a full path to the local object file you want to observer. "target_triple" is your <arch>-<vendor>-<os>, or something like "x86_64-apple-darwin" or "i386-pc-linux" and can be NULL if the file is only a single architecture. "platform_name" can be NULL. "add_dependent_modules" should be false, since you are only interested in seeing the one object file itself, an SBError instance  should be created and passed in.

Diskusi daftar pengembang di sini: https://groups.google.com/forum/?fromgroups=#!topic/julia -dev / snnGKJul4vg.

Ini mengeruk utas yang luar biasa tua, tetapi saya menyadari saya mungkin menggoda di sekitar tepi masalah ini dengan kode pembandingan kecepatan kode saya. Saya berulang kali Core.include() file yang berisi dua fungsi, listTests() dan runTests() . Saya kemudian cukup memanggil listTests() dan runTests() , dengan demikian mendefinisikan ulang dan kemudian memanggil mereka setiap kali saya memuat file benchmark baru. Karena saya tidak mendefinisikan ulang fungsi tingkat-2, saya rasa saya tidak akan mengalami masalah yang tercantum di sini, tetapi apakah mendefinisikan ulang hal-hal seperti ini adalah cara yang buruk untuk memuat dalam banyak file dengan "API" yang sama?

Mungkin lebih baik untuk memasukkannya ke dalam modul berulang kali, tetapi saya menyadari bahwa itu bertentangan dengan masalah modul anonim yang juga telah dimunculkan di daftar pengembang. Dalam kasus ini, mungkin baik-baik saja, karena Anda bisa mendefinisikan modul yang sama beberapa kali dan mengabaikan peringatan yang muncul.

Mungkin utas lama, tetapi masih relevan seperti sebelumnya.
Mungkin pendekatan lain adalah dengan menggunakan evalfile dan membuat file "mengembalikan" sejumlah fungsi. Apa masalah modul anonim?

Adakah yang punya pemikiran tambahan tentang strategi implementasi? Saya sedang berpikir untuk membuat kode skema sederhana yang mempertahankan grafik panggilan terbalik yang diperbarui setiap kali metode baru dibuat (berdasarkan AST yang tidak dioptimalkan). Kemudian menjalankan grafik tersebut saat metode didefinisikan ulang, menghapus versi terkompilasi dari masing-masing turunan fungsi yang didefinisikan ulang.

Itu pendekatan yang masuk akal untuk dicoba. Jika Anda merasa termotivasi untuk melakukannya, saya akan mengatakan lanjutkan dan mari kita lihat apa yang terjadi!

Apa yang terjadi jika _execution_ of the callee memicu kompilasi ulang pemanggil?

Saya bertanya karena saya terlambat menyadari bahwa menyelesaikan masalah ini mungkin memiliki konsekuensi menarik yang melampaui pengalaman pengguna di REPL: mungkin memungkinkan jawaban akhir untuk masalah metaprogramming, yaitu menciptakan fungsi "bertahap" yang efisien. Sebagai latar belakang bagi mereka yang mungkin tidak tahu: beberapa fungsi saat ini diimplementasikan oleh metaprogramming, khususnya yang beberapa aspek algoritmanya bergantung pada jenis input secara nontrivial --- contoh kanonik adalah salah satu di mana nomor tersebut loop dalam tubuh fungsi sama dengan dimensi array. Cara kita biasanya menangani ini adalah dengan mendefinisikan fungsi secara eksplisit, misalnya, dimensi 1 dan 2, dan kemudian memiliki pembungkus yang terlihat seperti ini:

_method_cache = Dict()
function myfunction(A::AbstractArray)
    N = ndims(A)
    if !haskey(_method_cache, N)
        func = eval(<an expression that generates the function definition for N dimensions>)
        _method_cache[N] = func
    else
        func = _method_cache[N]
    end
    return func(A)
end

Jadi pertama kali myfunction dieksekusi untuk array 4-dimensi, pertama kali mendefinisikan versi khusus untuk 4 dimensi, menambahkannya ke _method_cache , dan kemudian mengevaluasi fungsi baru pada input. Pada panggilan selanjutnya ke myfunction dengan array 4-dimensi, itu hanya mengambil definisi dari kamus dan mengevaluasinya. _method_cache adalah semacam "tabel metode bayangan" yang paralel dengan tabel metode internal Julia sendiri, yang digunakan hanya untuk fungsi khusus ini. (Untuk membuatnya tetap pribadi, biasanya dikemas dengan let .)

Meskipun pendekatan dalam contoh ini berfungsi dengan baik untuk badan fungsi yang memerlukan beberapa waktu untuk dieksekusi, pendekatan ini tidak terlalu cocok untuk fungsi yang dieksekusi dalam waktu kurang dari yang diperlukan untuk pencarian kamus, dan sangat buruk untuk fungsi yang Anda inginkan. bisa sebaris.

Cara yang lebih baik untuk melakukannya mungkin sebagai berikut:

function myfunction(A::AbstractArray)
    bodyexpr = <an expression for the body of the function specific for N dimensions>
    f = <strong i="17">@eval</strong> begin
        function myfunction(A::$(typeof(A)))
            $bodyexpr
        end
    end
    return f(A)
end

Di sini, eksekusi myfunction menghasilkan versi _ yang lebih spesifik_ dari myfunction untuk jenis masukan khusus ini. Kode yang dikompilasi setelah definisi baru ini tersedia akan menggunakan versi baru ini jika berlaku; versi di atas menjadi "fallback" generik untuk kasus-kasus di mana Anda belum mendefinisikan sesuatu yang lebih spesifik. Akibatnya, ini akan menjadi cara untuk membuat fungsi bertahap yang mengeksploitasi mekanisme tabel metode internal Julia sendiri, dan oleh karena itu memungkinkan seseorang untuk menghasilkan kode yang efisien.

Saat ini, ini tidak berfungsi karena satu alasan penting: apa pun yang disebut myfunction telah dikompilasi, jadi ia tidak tahu tentang definisi baru. Akibatnya, pemanggil khusus ini akan selalu menghasilkan versi baru yang segar dari myfunction menggunakan eval , yang akan sangat lambat.

Jadi triknya adalah dengan mengkompilasi ulang pemanggil, tetapi perhatikan bahwa ini perlu terjadi _ saat berada di tengah-tengah eksekusi_. Apa yang akan terjadi?

Lihat juga # 5395.

Ada masalah terkait dengan tipe abstrak yang sebenarnya tidak melibatkan redefinisi metode tetapi membutuhkan penanganan serupa. Mempertimbangkan:

abstract A
immutable B <: A; end
immutable C <: A; end

g(x::Vector{A}) = f(x[1])

f(::B) = 1
g(A[B()])

f(::C) = 0.5
g(A[C()])

Baris terakhir memberikan 4602678819172646912 . Kita perlu membuang kode untuk g karena jenis inferensi untuk f(::A) tidak lagi valid.

Ya, itu cukup jelas. Kami tahu mengganti metode hanyalah satu kasus khusus. Tapi itu selalu melibatkan definisi metode baru.

Tampaknya situasi saat ini bisa jadi ambigu sejak saat itu

f() = x()
x() = 1
println(f())
x() = 2
println(f())

memberi

1
1

sementara

g() = y()
precompile(g, ())
y() = 1
println(g())
y() = 2
println(g())

memberi

1
2

Tampaknya kasus selanjutnya dapat digunakan sebagai solusi untuk meniru kompilasi ulang pemanggil (saya tahu itu bukan kompilasi ulang).

Saya memiliki perasaan bahwa saya akan mengatakan sesuatu yang bodoh, tetapi tidak bisakah masalah ini dihilangkan dengan satu tingkat tipuan? Alih-alih melakukan hard-coding alamat fungsi, cari dari lokasi tetap dan panggil. Bukankah ini memiliki kinerja yang mirip dengan C ++ atau fungsi virtual Java? Anda kemudian dapat memberi anotasi pada fungsi agar memiliki alamat statis atau dinamis, dan mendapatkan peringatan / kesalahan saat Anda mencoba mendefinisikan ulang fungsi dengan alamat statis. Bahkan mungkin ada tombol untuk mengatur perilaku fungsi default. Saya baru mengenal bahasanya dan sama sekali tidak terbiasa dengan basis kode, tetapi jika ini terdengar layak, saya kira saya bisa mencobanya.

@ omer4d satu masalah dengan gagasan itu adalah bahwa Julia melakukan sebaris banyak fungsi yang lebih kecil, jadi kita masih membutuhkan cara untuk mencari semua "dependen" pada fungsi tertentu.

Itu hanya bisa menyebariskan yang memiliki alamat statis, seperti C ++. Ini seharusnya tidak menimbulkan ketidaknyamanan.
Orang yang tidak peduli dengan pengembangan interaktif tidak akan terpengaruh sama sekali, karena perilaku defaultnya adalah menggunakan alamat statis untuk semua fungsi.
Orang yang tidak peduli dengan kinerja tetapi menginginkan pengembangan interaktif dapat menggunakan sakelar untuk membuat alamat fungsi dinamis secara default.
Orang yang menginginkan interaktivitas dan performa bisa saja memberi anotasi pada fungsi yang relevan, yang seharusnya tidak merepotkan, karena inlining hanya relevan untuk fungsi pendek tingkat rendah yang sering dipanggil.
Mungkin perilaku default bahkan dapat disetel per modul.

Saya yakin ada beberapa alasan mengapa pendekatan tidak dapat dijalankan. Pertama,
anotasi benar-benar mengganggu, terutama bila perlu diretas
seputar detail implementasi seperti ini. Jika penjelasan digunakan untuk menyelesaikan 2
atau 3 masalah, mereka mulai menumpuk secara signifikan. Kedua, tidak ada
cara yang baik untuk memilih anotasi yang tepat untuk suatu fungsi. Tidak ada
hubungan antara apakah Anda menginginkan sesuatu yang sebaris, dan apakah Anda ingin
mengembangkannya secara interaktif. Ketiga, ketikkan potongan di fungsi lain
mungkin perlu diubah, jadi tidak hanya menelepon situs. Faktanya kami saat ini
memperoleh informasi jenis yang lebih sedikit daripada yang dapat kami lakukan untuk membuat sesuatu
aman dalam menghadapi masalah ini.

Nah, sebagai seseorang yang melakukan sebagian besar pekerjaannya dalam bahasa dengan lusinan kata kunci kualifikasi dan kompilasi ulang manual yang berulang, terkadang harus memenuhi syarat satu atau dua fungsi dan mengkompilasi ulang setelah beberapa siklus pengembangan interaktif tidak terdengar terlalu buruk, tapi itu hanya saya . Saya tidak cukup tahu untuk membahas poin ketiga, jadi saya akan mundur. Saya pikir sangat disayangkan bahwa solusi seperti itu tidak dapat dijalankan, karena kompilasi ulang berarti bahwa jika root dari pohon panggilan fungsi yang dimodifikasi berisi loop permainan atau animasi, itu perlu dihentikan dan dimulai ulang. = (

@JeffBezanson Saya mungkin akan sangat tertarik dengan kasus yang mudah, setidaknya pada awalnya. Saya tidak peduli banyak kode yang berjalan akan diperbarui, sebagai lawan dari hal-hal yang diakses melalui eval (misalnya dimasukkan pada prompt REPL). Akhirnya, juga penting untuk misalnya mendapatkan versi yang benar dari display dipanggil ketika didefinisikan ulang. Tapi saya percaya itu akan segera membuat kita lebih dekat untuk memiliki kompilasi tambahan.

Saya sedikit terkejut mengetahui masalah ini, bukan karena menurut saya mudah untuk menangani ini, tetapi karena berikut ini berfungsi seperti yang diharapkan.

julia> f(a, b) = a + b
f (generic function with 1 method)

julia> g(args...) = f(args...)
g (generic function with 1 method)

julia> g(1, 2)
3

julia> f(a::Int, b::Int) = a - b
f (generic function with 2 methods)

julia> g(1, 2)
-1

Sunting: Sebenarnya ini tampak seperti kasus khusus untuk masukan Vararg ..... Apakah ini berarti bahwa fungsi vararg dikompilasi ulang setiap kali dipanggil? Atau apakah cukup pintar untuk hanya mengkompilasi ulang bila perlu? Dan mungkinkah memperlakukan fungsi lain dengan cara yang sama?

julia> f(a, b) = a + b
f (generic function with 1 method)

julia> g(a, b) = f(a, b)
g (generic function with 1 method)

julia> g(1, 2)
3

julia> f(a::Int, b::Int) = a - b
f (generic function with 2 methods)

julia> g(1, 2)
3

Sebenarnya, fungsi mengembalikan hasil yang benar tetapi @code_typed mengembalikan hasil yang salah .....

julia> f(a, b) = a + b
f (generic function with 1 method)

julia> g(args...) = f(args...)
g (generic function with 1 method)

julia> g(1, 2)
3

julia> <strong i="7">@code_typed</strong> g(1, 2)
1-element Array{Any,1}:
 :($(Expr(:lambda, Any[:(args::(top(apply_type))(Vararg,Any)::Any::Any)], Any[Any[],Any[Any[:args,(Int64,Int64),0]],Any[]], :(begin  # none, line 1:
        return (top(box))(Int64,(top(add_int))((top(tupleref))(args::(Int64,Int64),1)::Int64,(top(tupleref))(args::(Int64,Int64),2)::Int64))
    end::Int64))))

julia> f(a::Int, b::Int) = a - b
f (generic function with 2 methods)

julia> g(1, 2)
-1

julia> <strong i="8">@code_typed</strong> g(1, 2)
1-element Array{Any,1}:
 :($(Expr(:lambda, Any[:(args::(top(apply_type))(Vararg,Any)::Any::Any)], Any[Any[],Any[Any[:args,(Int64,Int64),0]],Any[]], :(begin  # none, line 1:
        return (top(box))(Int64,(top(add_int))((top(tupleref))(args::(Int64,Int64),1)::Int64,(top(tupleref))(args::(Int64,Int64),2)::Int64))
    end::Int64))))

Dan

julia> f(a, b) = a + b
f (generic function with 1 method)

julia> g(args...) = f(args...)
g (generic function with 1 method)

julia> g(1, 2)
3

julia> f(a::Int, b::Int) = a - b
f (generic function with 2 methods)

julia> g(1, 2)
-1

julia> <strong i="12">@code_typed</strong> g(1, 2)
1-element Array{Any,1}:
 :($(Expr(:lambda, Any[:(args::(top(apply_type))(Vararg,Any)::Any::Any)], Any[Any[],Any[Any[:args,(Int64,Int64),0]],Any[]], :(begin  # none, line 1:
        return (top(box))(Int64,(top(sub_int))((top(tupleref))(args::(Int64,Int64),1)::Int64,(top(tupleref))(args::(Int64,Int64),2)::Int64))
    end::Int64))))

apakah itu akan bekerja untuk mengindeks metode fungsi (atau hanya jenis fungsi / objek itu sendiri) dengan nomor yang berubah setiap kali ada yang ditugaskan dan dengan demikian menjadi dapat memeriksa apakah nomor yang disimpan dengan fungsi (pemanggil) yang dikompilasi (yaitu menggunakan struktur baru fungsi, menyimpan angka-angka ini sebagai bidang dll) masih cocok dengan salah satu yang "saat ini" pada fungsi yang sebaris (dipanggil)?
(tanpa penalti performa yang besar tentunya)
Dan jika terjadi perubahan pertimbangkan untuk mengkompilasi versi baru atau diselesaikan dengan panggilan eksplisit.

Itu akan memindahkan dampak kinerja ke runtime (dan itu akan terjadi di mana-mana), yang tidak diinginkan. Yang diperlukan adalah melacak web dependensi dan mengompilasi ulang apa pun yang mungkin terpengaruh ketika metode didefinisikan ulang sebagian. Itu mungkin bisa dilakukan, itu hanya sakit _masif_.

Saya tidak tahu apakah solusi untuk masalah ini akan dengan mudah digeneralisasikan ke makro, tetapi jika demikian, itu akan menyenangkan.

Jadi seseorang sebaiknya pergi sebaliknya dan menyimpan semua metode yang menyebariskan fungsi dan pada perubahan mengkompilasi ulang mereka. (tentu saja memeriksa eksekusi saat ini dll)

Masalah yang sulit di sini adalah:

  • kode tidak valid seperti yang Anda katakan, cari tahu semua tempat yang menggunakan fungsi lama (dalam kode yang dihasilkan). Itu mungkin menyiratkan kemampuan untuk merelokasi kode yang memanggil fungsi yang berubah tetapi konvensi pemanggilan & tipe pengembaliannya tidak berubah
  • lebih keras: apa yang terjadi jika tugas lain sedang berjalan saat Anda menentukan metode baru? keadaan apa yang dilihatnya? Anda ingin menyelesaikannya tanpa penggantian on-stack karena sangat sulit untuk diterapkan secara efisien. Kami pikir kami memiliki solusi untuk itu yang tidak membunuh kinerja dan saya pikir @vtjnash akan menghasilkan sedikit tulisan setelah dalam kondisi yang layak

Sementara itu bekerja pada solusi untuk masalah, saya penasaran apakah itu mungkin dan ide yang baik untuk menambahkan bendera ke setiap fungsi yang diatur jika (dan hanya jika) fungsi yang diberikan digunakan di salah satu cara atas ( sebaris dll) untuk menghasilkan peringatan bahwa metode sebaris harus didefinisikan ulang. (Mirip dengan cara kerja peringatan ambigu)
Jika tidak ada cara yang lebih baik untuk menyimpan flag, mungkin dengan menggunakan struktur baru dari metode / fungsi di julia 0.5

Sekarang lebih mudah untuk menekan ini di repl dan perilaku ini mungkin harus didokumentasikan untuk map?

julia> function f(x)
         1
       end 
f (generic function with 1 method)

julia> map(f, 1:10)
10-element Array{Int64,1}:
 1
 1
 1
 1
 1
 1
 1
 1
 1
 1

julia> function f(x)
         2
       end
WARNING: Method definition f(Any) in module Main at REPL[9]:2 overwritten at REPL[11]:2.
f (generic function with 1 method)

julia> map(f, 1:10)
10-element Array{Int64,1}:
 1
 1
 1
 1
 1
 1
 1
 1
 1
 1

Itu masih vanilla # 265. Saya pikir Anda akan menghargai bahwa ini akan segera diperbaiki di v0.6-dev :)

@ vtjnash apakah ada kinerja runtime atau pertukaran memori?

ini membutuhkan sedikit memori (140MB -> 170 MB), tetapi seharusnya tidak terlalu mempengaruhi kinerja (kompilasi atau runtime). Dan saya belum mencoba banyak pengoptimalan.

Demo sejauh ini menyenangkan:

julia> f() = 1
f (generic function with 1 method)

julia> function g(x)
    <strong i="7">@eval</strong> f() = $x # this is the correct way to write `global f() = x` (which should be a syntax error, but isn't currently)
    return @eval(f)() # use `eval` to hide the value from optimization / inlining, but the call is not inside eval
end
g (generic function with 1 method)

julia> g(2)
WARNING: Method definition f() in module Main at REPL[1]:1 overwritten at REPL[2]:2.
1

julia> g(3)
WARNING: Method definition f() in module Main at REPL[2]:2 overwritten at REPL[2]:2.
2

julia> g(4)
WARNING: Method definition f() in module Main at REPL[2]:2 overwritten at REPL[2]:2.
3

Apakah alasan yang tidak mengembalikan 2, 3, 4 karena urutan di mana kompilasi g vs eksekusi yang mengubah f terjadi?

Itu kira-kira benar. Perhatikan bahwa semantik bahasa tidak menentukan kompilasi, jadi lebih tepat untuk mengatakan bahwa urutan di mana g diinterpretasikan vs. waktu saat definisi ulang f menjadi terlihat oleh penerjemah

baiklah, inilah demo kecil menyenangkan lainnya yang mendefinisikan ulang primitif + untuk menghitung seberapa banyak ia digunakan:

julia> add_ctr = UInt(0)
0x0000000000000000

julia> Base.:+(a::Int, b::Int) = (global add_ctr += 1; Core.Intrinsics.add_int(a, b))

julia> add_ctr
0x0000000000000016

julia> last = 0;

julia> println(Int(add_ctr - last)); last = add_ctr;
287

julia> println(Int(add_ctr - last)); last = add_ctr;
17

Yang itu curang: Saya tidak berpikir + digunakan oleh Base sama sekali, jadi tidak ada apa pun untuk dikompilasi ulang. Definisikan ulang fungsi nyata , seperti svd , yang Anda tahu harus dipanggil setidaknya 100 kali sebelum REPL muncul. Kemudian kami akan terkesan.

Peluang untuk re-instrumentasi langsung kode untuk pembuatan profil / penelusuran membuat saya kagum.

Memang. Dan di sini saya akan membeli keyboard baru, karena urutan Ctrl-D; julia<Enter> sudah usang. Sepertinya saya tidak perlu melakukannya.

Mungkin agak misterius untuk dimasukkan ke dalam catatan rilis, tetapi harus dicatat di suatu tempat bahwa pemahaman sekarang sintaks untuk collect -ing Generator (sebagai lawan dari sesuatu yang mungkin di tingkat penguraian di 0,4? - @which tidak berfungsi di sana)? Contoh ini, mirip dengan vchuravy di atas, sedikit berlebihan bahkan mengingat bahwa setiap fungsi sekarang bertipe 0,5:

               _
   _       _ _(_)_     |  A fresh approach to technical computing
  (_)     | (_) (_)    |  Documentation: http://docs.julialang.org
   _ _   _| |_  __ _   |  Type "?help" for help.
  | | | | | | |/ _` |  |
  | | |_| | | | (_| |  |  Version 0.5.0-rc3+0 (2016-08-22 23:43 UTC)
 _/ |\__'_|_|_|\__'_|  |
|__/                   |  x86_64-linux-gnu

julia> f(x) = 1
f (generic function with 1 method)

julia> [f(x) for x in 1:5]
5-element Array{Int64,1}:
 1
 1
 1
 1
 1

julia> f(x) = 2
WARNING: Method definition f(Any) in module Main at REPL[1]:1 overwritten at REPL[3]:1.
f (generic function with 1 method)

julia> [f(x) for x in 1:5]
5-element Array{Int64,1}:
 1
 1
 1
 1
 1

julia> <strong i="9">@which</strong> [f(x) for x in 1:5]
collect(itr::Base.Generator) at array.jl:295

tidak benar?

berguna untuk memperbaiki # 265

Apakah ini benar-benar sudah diperbaiki 😲

Apakah solusi ini masih direncanakan untuk di-backport ke 0,5.x?

Tidak.

@ rapus95 , ini adalah perubahan yang sangat invasif, dan melakukan kesalahan dapat membuat julia tidak stabil bagi banyak pengguna. Karena inti dari rilis adalah untuk memberikan stabilitas lebih daripada mengikuti master, jauh lebih baik untuk memilikinya di 0.6. (Anda selalu dapat mengikuti master jika Anda menginginkannya lebih awal dari rilis 0.6.)

Saya sangat senang melihat kemajuan dalam masalah ini! Akan luar biasa jika ini juga memungkinkan kompiler untuk mengoptimalkan kasus yang dijelaskan di https://groups.google.com/forum/#!topic/julia -users / OBs0fmNmjCU.

Wow. Momen yang luar biasa!

Hai kawan!

Apakah ada kemungkinan untuk mendukung perbaikan ini menjadi 0,5 seri? Atau apakah itu hanya bekerja dalam apa yang akan menjadi 0,6?

Ini adalah perubahan besar dan hanya akan tersedia di 0.6

Sulit untuk melebih-lebihkan betapa hebatnya memperbaiki ini. Kebiasaan lama sulit dihilangkan jadi saya terkadang memulai ulang dan / atau membangun kembali julia secara tidak perlu, tetapi jika saya ingat, ini adalah pengubah permainan yang lengkap untuk memperbaiki masalah.

Apakah halaman ini membantu?
0 / 5 - 0 peringkat