Julia: إعادة التجميع التلقائي للوظائف التابعة

تم إنشاؤها على ٢٦ نوفمبر ٢٠١١  ·  55تعليقات  ·  مصدر: JuliaLang/julia

إذا قمت بتعريف دالة d2 قبل دالة d1 التي تستدعي d2 ثم غيرت d2 ، فإن d1 تستخدم التعريف القديم لـ d2.
أفترض أن هذا لأنه تم تجميعه مسبقًا ، ولكن ربما يجب أن يكون هناك ملاحظة تحذر من هذا؟ أم أنه من الممكن استبدال التعريف القديم بـ longjmp بالتعريف الجديد؟
(مهم في الغالب لـ REPL ، لأنني لا أقوم دائمًا بحمل كامل)

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

التعليق الأكثر فائدة

نجاح باهر. يا لها من لحظة عظيمة!

ال 55 كومينتر

أعتقد أن السبب في ذلك هو إنشاء رمز d2() عند تحديد الوظيفة ، لذا يتم حل طريقة d() في ذلك الوقت. كما لاحظت ، يمكنك إعادة تعريف d2() كما تم تعريفه في الأصل لجعله يعمل كما هو متوقع. من الناحية المثالية ، سنقوم تلقائيًا بإعادة تعريف أي شيء يعتمد على d() عندما يتم تغييره على هذا النحو ، ولكن ليس لدينا أي من البيانات الوصفية في المكان الذي يسمح بذلك. أعتقد أنه يمكننا وضع تحذير حول هذا السلوك في الدليل. من الأفضل إصلاحه ، لكنني أعتقد أنه من الصعب بعض الشيء القيام به: - /

كنت أتمنى ألا يلاحظ أحد ذلك ، لكني أعتقد أن هذا لم يكن واقعيا بالنسبة لي :)

إنها أكثر متعة من ذلك ؛ يتيح لك حقًا رؤية JIT أثناء العمل ، حيث لا يتم حلها حتى التنفيذ:

julia> function f2()
       a
       end

julia> function f1()
       f2()
       end

julia> function f2()
       2
       end

julia> f1()
2

ومع ذلك ، أخشى أن يؤدي ذلك إلى بعض النتائج غير المتوقعة في REPL ، على الرغم من أنني لا أعرف حقًا أفضل طريقة للتعامل معها. على الرغم من أنني استخدمت خطأ لإظهار الاختلاف بطريقة دراماتيكية ، إلا أنه قد يحدث أيضًا إذا كنت أحاول رؤية تأثير تغيير معادلة داخلية أو ثابت!

هناك نوعان من المواقع التي قد تحتاج إلى تحديث عند تغيير الطريقة:

  • الأماكن التي يتم فيها استدعاء الطريقة بشكل صريح كدالة (استدعاء / إعادة)
  • الأماكن التي تم فيها تحديد الطريقة

يمكن تحديث الأول فقط عن طريق تغيير جسم الوظيفة: يمكن تعديله في مكانه ، أو يمكن تحديث مواقع الاتصال بنشاط لاستدعاء موقع جديد ، أو يمكن استبدال جسم الوظيفة القديم بجذع ينتقل إلى الإصدار الجديد ، اختياريًا تصحيح موقع الاتصال. للتضمين ، نحتاج إلى تتبع المتصلين الذين قاموا بتضمين الوظيفة وإعادة JIT لهم.

كل ما يمكننا فعله حقًا هو تجاهل الكود ، لأنه حتى لو لم تكن الطريقة مضمنة ، فقد تعتمد أي وظيفة تستدعيها على سلوك النوع الذي يمكن أن يتغير إذا تغيرت الطريقة. هذا في الغالب للحالات التفاعلية ، لذا فإن إعادة تجميع الكود ليس بالأمر الضخم.
يتطلب نفس عمل المشكلة رقم 47.

المشكله. حسنًا ، نظرًا لأن الرد هو الذي تأثر في المقام الأول ، فإن القيام بالشيء الصحيح ببطء أمر جيد. في وقت التشغيل ، يجب ألا يبدأ هذا مطلقًا - لماذا يقوم شخص ما بتعريف شيء ما ثم إعادة تعريفه مرة أخرى إلا بشكل تفاعلي؟ إذا انتهى الحل بإحداث الكثير من النفقات العامة ، فربما يتم قلبه ، فيجب أن يكون الحل اختياريًا ويتم إجراؤه تلقائيًا في الرد؟

نحن بحاجة إلى توثيق أن هذا غير محدد حاليًا.

من الناحية الفنية ، هذا ليس خطأ ، إنه سلوك غير محدد. عندما تعيد تعريف طريقة ، يكون السلوك الناتج غير محدد. لذلك تستطيع جوليا أن تفعل ما تشاء ، بما في ذلك ما تفعله حاليًا. يعد توفير سلوك محدد جيدًا عند إعادة تعريف الطريقة ميزة وليست إصلاحًا للأخطاء. كما أنني لست مقتنعًا بأن هذه مشكلة الإصدار 1.0 لأن الانتقال من سلوك غير محدد إلى تقديم سلوك محدد جيدًا ليس تغييرًا جذريًا. يمكن تنفيذ ذلك في الإصدار 1.1 بدون كسر أي كود صالح v1.0.

كان Greg Clayton من فريق LLVM / LLDB التابع لشركة Apple لطيفًا بما يكفي لتوثيق كيفية استخلاص (مع مكتبات lldb ، مشروع فرعي من llvm) المعلومات اللازمة لتحديد تبعيات ثنائي من معلومات الرمز المضمنة (استيراد الرمز) ؛ بالإضافة إلى تلك الرموز التي تم تصديرها من خلال ثنائي (ضروري لبناء الرسم البياني الكامل للتبعية).

  • جايسون

في 31 آذار (مارس) 2012 الساعة 11:02 مساءً ، كتب جايسون أتين:

أعزائي عشاق LLDB ،

أتساءل عما إذا كان بإمكاني استخدام مكتبة / مكتبات lldb لاستبدال كود معين يعمل على OSX والذي يعرض الآن قائمتين من الرموز - على غرار إخراج (dyldinfo -lazy_bind -exports) ؛ على سبيل المثال ، أحتاج إلى سرد الرموز المستوردة والمصدرة بواسطة كائن أو حزمة ثنائية مشتركة.

كنت آمل أنه باستخدام مكتبة lldb ، سأتمكن من استخدام نفس رمز العميل على OSX كما هو الحال في Linux. (تستخدم نسخة لينكس من الكود حاليًا libbfd و libdld لفعل الشيء نفسه ، لكن الأحدث لا يحصل على القليل من الحب / الصيانة).

أبحث من خلال include / lldb / ، حيث يبدو أن lldb ستحتاج إلى هذه المعلومات نفسها (قائمة الرموز المستوردة ، وقائمة الرموز المصدرة لملف Mach-O) لتعمل ، ولكن ليس من الواضح أي واجهة برمجة تطبيقات يجب استخدامها. جميع الاقتراحات / المؤشرات على رمز المثال في lldb ستكون موضع ترحيب!

شكرا لك.
جايسون

في حالة عدم وضوح ما يفعله dyldinfo ، فإليك مثال: (لكنني فقط بحاجة إلى أسماء الرموز ؛ وليس العناوين أو المقاطع أو الأقسام):

ملف $ / tmp / sample_bundle
/ tmp / sample_bundle: حزمة Mach-O 64 بت x86_64

$ dyldinfo -lazy_bind -export / tmp / sample_bundle

معلومات الربط البطيئة (من جزء lazy_bind من معلومات dyld):
مقطع عنوان فهرس رمز dylib
__DATA __la_symbol_ptr 0x00001030 0x0000 مساحة الاسم المسطحة __mm_pop_chunk
__DATA __la_symbol_ptr 0x00001038 0x0015 مساحة الاسم المسطحة _dh_define
تصدير المعلومات (من 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

في يوم الإثنين 2 أبريل 2012 الساعة 3:13 مساءً ، كتب جريج كلايتون [email protected] :

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

في يوم الإثنين 2 أبريل 2012 الساعة 4:05 مساءً ، كتب جريج كلايتون [email protected] :

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.

مناقشة قائمة مطوري هنا: https://groups.google.com/forum/؟fromgroups=#!topic/julia -dev / snnGKJul4vg.

هذا هو تجريف خيط قديم بشكل مثير للدهشة ، لكنني أدركت أنني ربما أتعامل مع حواف هذه المشكلة مع كود قياس سرعة الشفرة الخاص بي. قمت مرارًا بتكرار ملفات Core.include() التي تحتوي على وظيفتين ، listTests() و runTests() . ثم اتصل بكل بساطة بـ listTests() و runTests() ، وبذلك أعيد تعريفهما ثم استدعائهما في كل مرة أقوم فيها بتحميل ملف معياري جديد. نظرًا لأنني لا أقوم بإعادة تعريف وظائف المستوى الثاني ، لا أعتقد أنني سأواجه المشكلات المدرجة هنا ، ولكن هل إعادة تعريف أشياء مثل هذه طريقة سيئة للاستمرار في التحميل في ملفات متعددة باستخدام نفس "واجهة برمجة التطبيقات"؟

قد يكون من الأفضل تضمينها في وحدة بشكل متكرر ، لكني أدرك أن هذه الصعوبات تتعارض مع مشكلة الوحدة المجهولة التي أثيرت أيضًا في قائمة المطورين. في هذه الحالة ، قد يكون الأمر جيدًا ، نظرًا لأنه يمكنك فقط تحديد نفس الوحدة عدة مرات وتجاهل التحذير الذي ينبعث.

ربما يكون موضوعًا قديمًا ، ولكنه وثيق الصلة كما كان دائمًا.
ربما طريقة أخرى هي استخدام evalfile وجعل الملف "يعرض" مجموعة من الدوال. ما هي مشكلة الوحدة المجهولة؟

كنت أشير إلى هذا: https://github.com/JuliaLang/julia/issues/3661.

أي شخص لديه أفكار إضافية حول استراتيجيات التنفيذ؟ كنت أفكر في ترميز مخطط بسيط يحافظ على رسم بياني للمكالمات المقلوبة يتم تحديثه كلما تم إنشاء طريقة جديدة (بناءً على AST غير المحسن). ثم يمشي في هذا الرسم البياني عندما يتم إعادة تعريف طريقة ما ، ويزيل النسخة المترجمة لكل من أحفاد الوظيفة المعاد تعريفها.

هذا نهج معقول مثل أي نهج يمكن تجربته. إذا كنت تشعر بالحافز للقيام بذلك ، فسأقول تفضل ودعنا نرى ما سيحدث!

ماذا يحدث إذا أدى _إعدام المستدعي إلى إعادة تجميع المتصل؟

أسأل لأنني أدركت متأخراً أن حل هذه المشكلة قد يكون له عواقب مثيرة للاهتمام تتجاوز تجربة المستخدم في REPL: فقد يسمح بالإجابة النهائية لمشكلة البرمجة الوصفية ، وهي إنشاء وظائف "مرحلية" فعالة. كخلفية لأولئك الذين قد لا يعرفون: يتم تنفيذ بعض الوظائف حاليًا عن طريق البرمجة الوصفية ، لا سيما تلك التي يعتمد فيها جانب من جوانب الخوارزمية على أنواع المدخلات بطريقة غير بديهية - المثال الأساسي هو الذي فيه الرقم من الحلقات في جسم الوظيفة تساوي أبعاد المصفوفة. الطريقة التي نتعامل بها عادة مع هذا هي تحديد الوظيفة بشكل صريح ، على سبيل المثال ، الأبعاد 1 و 2 ، ثم لدينا غلاف يشبه ما يلي:

_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

لذلك في المرة الأولى التي يتم فيها تنفيذ myfunction لمصفوفة رباعية الأبعاد ، يقوم أولاً بتعريف إصدار خاص بأربعة أبعاد ، ويضيفها إلى _method_cache ، ثم يقيّم الوظيفة الجديدة على الإدخال. في المكالمات المستقبلية إلى myfunction باستخدام مصفوفة رباعية الأبعاد ، يقوم فقط باسترداد التعريف من القاموس وتقييمه. _method_cache هو نوع من "جدول أسلوب الظل" بالتوازي مع جدول الأسلوب الداخلي الخاص بجوليا ، وهو جدول يستخدم فقط لهذه الوظيفة المحددة. (للحفاظ على خصوصيتها ، يتم تغليفها عادةً بـ let .)

بينما يعمل النهج في هذا المثال جيدًا للهيئات الوظيفية التي تستغرق بعض الوقت للتنفيذ ، إلا أنه ليس مناسبًا تمامًا للوظائف التي يتم تنفيذها في وقت أقل مما هو مطلوب للبحث في القاموس ، وهو سيء بشكل خاص للوظائف التي تريد أن تكون قادرة على مضمنة.

قد تكون أفضل طريقة للقيام بذلك هي ما يلي:

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

هنا ، يؤدي تنفيذ myfunction إنشاء إصدار _أكثر تحديدًا_ من myfunction لأنواع الإدخال المحددة هذه. الرمز الذي تم تجميعه بعد أن يصبح هذا التعريف الجديد متاحًا سيستخدم هذا الإصدار الجديد عند الاقتضاء ؛ يصبح الإصدار أعلاه "احتياطيًا" عامًا لتلك الحالات التي لم تحدد فيها بالفعل شيئًا أكثر تحديدًا. وبالتالي ، ستكون هذه طريقة لإنشاء وظائف مرحلية تستغل آليات جدول الأساليب الداخلية الخاصة بجوليا ، وبالتالي تسمح لأحد بتوليد رمز فعال.

حاليًا ، هذا لا يعمل لسبب واحد حاسم: كل ما يسمى myfunction تم تجميعه بالفعل ، لذلك لا يعرف عن التعريف الجديد. وبالتالي ، سينشئ هذا المتصل المعين دائمًا إصدارًا جديدًا جديدًا myfunction باستخدام eval ، والذي سيكون بطيئًا للغاية.

لذا ستكون الحيلة هي إعادة ترجمة المتصل ، لكن لاحظ أن هذا يجب أن يحدث - بينما يكون في منتصف التنفيذ -. ماذا سيحدث؟

أنظر أيضا # 5395.

هناك مشكلة مرتبطة بأنواع مجردة لا تتضمن في الواقع إعادة تعريف الطريقة ولكنها تحتاج إلى معالجة مماثلة. يعتبر:

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()])

السطر الأخير يعطي 4602678819172646912 . نحتاج إلى التخلص من الكود الخاص بـ g لأن نوع الاستدلال لـ f(::A) لم يعد صالحًا.

نعم ، هذا واضح تمامًا. نحن نعلم أن استبدال الطرق هو حالة خاصة واحدة فقط. لكنها تتضمن دائمًا تعريفات طرق جديدة.

يبدو أن الوضع الحالي يمكن أن يكون غامضًا إلى حد ما منذ ذلك الحين

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

يعطي

1
1

في حين

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

يعطي

1
2

يبدو أنه يمكن استخدام الحالة اللاحقة كحل بديل لمحاكاة إعادة تجميع المتصل (أعلم أنها ليست إعادة تجميع فعلاً).

لدي شعور بأنني على وشك أن أقول شيئًا غبيًا ، لكن ألا يمكن التخلص من هذه المشكلة بمستوى واحد من المراوغة؟ بدلاً من الترميز الثابت لعنوان دالة ، ابحث عنه من موقع ثابت واستدع هذا العنوان. ألن يكون لهذا أداء مشابه لوظائف C ++ أو Java الافتراضية؟ يمكنك بعد ذلك التعليق على الوظائف للحصول على عنوان ثابت أو ديناميكي ، والحصول على تحذير / خطأ عند محاولة إعادة تعريف وظيفة بعنوان ثابت. يمكن أن يكون هناك مفتاح لتعيين سلوك الوظيفة الافتراضي. أنا جديد على اللغة وغير معتاد تمامًا على قاعدة الشفرة ، ولكن إذا كان هذا يبدو ممكناً ، أفترض أنني يمكن أن أعطيها دفعة.

@ omer4d إحدى مشكلات هذه الفكرة هي أن جوليا تقوم

يمكن أن تكون مضمنة فقط في العناوين الثابتة ، مثل C ++. لا ينبغي أن يسبب هذا أي إزعاج.
لن يتأثر الأشخاص الذين لا يهتمون بالتطوير التفاعلي على الإطلاق ، لأن السلوك الافتراضي سيكون استخدام العناوين الثابتة لجميع الوظائف.
يمكن للأشخاص الذين لا يهتمون بالأداء ولكنهم يريدون التطوير التفاعلي استخدام مفتاح لجعل عناوين الوظائف ديناميكية بشكل افتراضي.
يمكن للأشخاص الذين يرغبون في التفاعل والأداء أن يقوموا فقط بالتعليق على الوظائف ذات الصلة ، والتي لا ينبغي أن تتطلب الكثير من العمل ، نظرًا لأن التضمين مناسب فقط للوظائف القصيرة منخفضة المستوى التي يتم استدعاؤها كثيرًا.
ربما يمكن تعيين السلوك الافتراضي لكل وحدة.

أعتقد أن هناك عدة أسباب تجعل هذا النهج غير قابل للتطبيق. أول،
تعد التعليقات التوضيحية مصدر إزعاج حقًا ، لا سيما عندما تكون ضرورية للاختراق
حول تفاصيل التنفيذ مثل هذا. إذا تم استخدام التعليقات التوضيحية لحل 2
أو 3 مشاكل ، تبدأ في التراكم بشكل ملحوظ. ثانيًا ، لا يوجد
طريقة جيدة لاختيار التعليق التوضيحي المناسب لوظيفة ما. لا يوجد
الاتصال بين ما إذا كنت تريد شيئًا مضمّنًا وما إذا كنت كذلك
تطويره بشكل تفاعلي. ثالثًا ، اكتب الخصومات في وظائف أخرى
قد تحتاج إلى التغيير ، لذا فهي ليست مجرد مواقع اتصال. في الحقيقة نحن حاليا
استخلاص معلومات أقل مما يمكننا صنع الأشياء
بأمان في مواجهة هذه المشكلة.

حسنًا ، بصفتك شخصًا يقوم بمعظم أعماله بلغات مع عشرات الكلمات الرئيسية المؤهلة وإعادة ترجمة يدوية متكررة ، فإن الاضطرار أحيانًا إلى تأهيل وظيفة أو اثنتين وإعادة التجميع بعد بضع دورات من التطوير التفاعلي لا يبدو سيئًا للغاية ، ولكن هذا أنا فقط . لا أعرف ما يكفي لتناول النقطة الثالثة ، لذلك سأستقيل. أعتقد أنه من المؤسف أن مثل هذا الحل غير قابل للتطبيق ، لأن إعادة التجميع تعني أنه إذا كان جذر شجرة استدعاء الوظيفة المعدلة يحتوي على لعبة أو حلقة رسوم متحركة ، فسيلزم إنهاؤها وإعادة تشغيلها. = (

JeffBezanson ربما سأكون أكثر اهتمامًا بالقضية السهلة ، على الأقل في البداية. لا يهمني أنه يتم تحديث الكثير من التعليمات البرمجية قيد التشغيل ، على عكس الأشياء التي يتم الوصول إليها من خلال eval (على سبيل المثال ، تم إدخالها في موجه REPL). في النهاية ، سيكون من المهم أيضًا الحصول على الإصدار الصحيح من display عند إعادة تعريفه. لكنني أعتقد أن ذلك سيقربنا بسرعة من الحصول على تجميعات تدريجية.

لقد اندهشت قليلاً لمعرفة هذه المشكلة ، ليس لأنني أعتقد أنه من السهل التعامل معها ، ولكن لأن ما يلي يعمل كما هو متوقع.

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

تحرير: في الواقع هذا يبدو كحالة خاصة لإدخال Vararg ..... هل هذا يعني أنه يتم إعادة ترجمة دالة vararg في كل مرة يتم استدعاؤها؟ أم أنها ذكية بما يكفي لإعادة الترجمة عند الضرورة فقط؟ وهل من الممكن التعامل مع الوظائف الأخرى بنفس الطريقة؟

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

في الواقع ، ترجع الدالة النتيجة الصحيحة لكن @code_typed ترجع نتيجة خاطئة .....

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))))

و

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))))

هل سيعمل على فهرسة طرق الوظائف (أو فقط أنواع الوظائف / الكائنات نفسها) برقم يتغير في كل مرة يتم فيها تعيين أي شيء وبالتالي يصبح قادرًا على التحقق مما إذا كان الرقم المخزن مع الوظيفة المترجمة (المتصل) (أي استخدام البنية الجديدة من الوظائف ، حفظ هذه الأرقام كحقل وما إلى ذلك) لا يزال يطابق الرقم "الحالي" في الوظيفة التي تم تضمينها (تسمى)؟
(بدون عقوبة أداء كبيرة بالطبع)
وفي حالة التغيير ، فكر في تجميع الإصدار الجديد أو إكماله عن طريق استدعاء صريح.

سيؤدي ذلك إلى نقل تأثير الأداء إلى وقت التشغيل (وسيكون في كل مكان) ، وهو أمر غير مرغوب فيه. ما نحتاجه هو تتبع شبكة التبعيات وإعادة تجميع أي شيء قد يتأثر عند إعادة تعريف الطريقة جزئيًا. من المحتمل أن يكون الأمر ممكنًا ، إنه مجرد ألم "هائل".

لا أعرف ما إذا كان حل هذه المشكلة يمكن تعميمه بسهولة على وحدات الماكرو ، ولكن إذا كان الأمر كذلك ، فسيكون ذلك أمرًا رائعًا.

لذلك يجب على المرء أن يسير في الاتجاه الآخر ويخزن جميع الطرق التي تضمنت الوظيفة وعند التغيير يعيد تجميعها. (بالطبع التحقق من التنفيذ الحالي ، إلخ)

المشاكل الصعبة هنا هي:

  • إبطال الكود كما تقول ، اكتشف جميع الأماكن التي تستخدم الوظائف القديمة (في الكود الذي تم إنشاؤه). ربما يعني ذلك القدرة على إعادة تحديد موضع الكود الذي يستدعي الوظائف التي تغيرت ولكن لم يتغير نوع اصطلاح الاستدعاء ونوع الإرجاع
  • أصعب: ماذا يحدث إذا تم تشغيل مهمة أخرى أثناء تحديد طريقة جديدة؟ ما الدولة التي ترى؟ تريد حل ذلك بدون الاستبدال على المكدس لأنه من الصعب جدًا تنفيذه بكفاءة. نعتقد أن لدينا حلًا لا يقتل الأداء وأعتقد أن vtjnash سيأتي

أثناء العمل على حل المشكلة ، أشعر بالفضول فيما إذا كان من الممكن وفكرة جيدة إضافة علامة إلى كل وظيفة تم تعيينها إذا (وفقط إذا) تم استخدام الوظيفة المحددة بأي من الطرق العليا ( inlining إلخ) لإصدار تحذير من إعادة تعريف طريقة مضمنة. (على غرار الطريقة التي يعمل بها التحذير الغامض)
إذا لم تكن هناك طريقة أفضل لحفظ العلم ، فربما مرة أخرى باستخدام الهيكل الجديد للطرق / الوظائف في جوليا 0.5

أصبح من الأسهل الآن النقر على هذا في الرد وربما يجب توثيق هذا السلوك للخريطة؟

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

هذا لا يزال مجرد فانيلا # 265. أعتقد أنك ستقدر أن هذا سيتم إصلاحه قريبًا في v0.6-dev :)

vtjnash هل هناك أي أداء وقت تشغيل أو مقايضة الذاكرة؟

يتطلب القليل من الذاكرة (140 ميجابايت -> 170 ميجابايت) ، ولكن لا ينبغي أن يكون له تأثير كبير على الأداء (التجميع أو وقت التشغيل). وأنا لم أحاول الكثير من التحسين حتى الآن.

العروض التوضيحية حتى الآن ممتعة:

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

هل السبب في عدم إرجاع 2 ، 3 ، 4 يرجع إلى الترتيب الذي يحدث فيه تجميع g مقابل التنفيذ الذي يعيد تعريف f ؟

هذا صحيح تقريبًا. لاحظ أن دلالات اللغة لا تحدد التجميع ، لذلك من الأصح القول أن الترتيب الذي يتم فيه تفسير g مقابل الوقت الذي يصبح فيه إعادة تعريف f مرئيًا للمترجم.

حسنًا ، إليك عرضًا تجريبيًا ممتعًا آخر يعيد تعريف العنصر البدائي + للاحتفاظ بحساب مقدار استخدامه:

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

غش هذا الشخص: لا أعتقد أن Base تستخدمه على الإطلاق + ، لذا لم يكن هناك أي شيء لإعادة تجميعه. أعد تحديد وظيفة حقيقية ، مثل svd ، والتي تعلم أنه يجب استدعاءها 100 مرة على الأقل قبل ظهور REPL. ثم سننبهر.

إن فرص إعادة التشغيل المباشر للشفرة للتنميط / التتبع تدهشني.

في الواقع. وها أنا كنت على وشك شراء لوحة مفاتيح جديدة ، لأن التسلسل Ctrl-D; julia<Enter> أوشك على التلف. يبدو أنني قد لا أضطر إلى ذلك.

قد يكون وضع ملاحظات الإصدار في ملاحظات الإصدار أمرًا غامضًا بعض الشيء ، ولكن يجب الإشارة في مكان ما إلى أن الفهم أصبح الآن بناء جملة لـ collect -ing a Generator (على عكس شيء ربما على مستوى التحليل) في 0.4؟ - @which لا يعمل هناك)؟ هذا المثال ، على غرار vchuravy أعلاه ، هو نوع من مسكتك حتى مع الأخذ في الاعتبار أن كل وظيفة هي الآن نوع في 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

ليس صحيح تماما؟

مفيد للإصلاح رقم 265

هل هذا ثابت حقا 😲

هل مازال هذا الحل مخططًا ليتم نقله إلى 0.5.x؟

لا.

@ rapus95 ، هذا تغيير جائر للغاية ، وقد

يسعدني جدًا أن أرى تقدمًا في هذه المسألة! سيكون من الرائع لو سمح هذا أيضًا للمترجم بتحسين الحالة الموضحة في https://groups.google.com/forum/#!topic/julia -users / OBs0fmNmjCU.

نجاح باهر. يا لها من لحظة عظيمة!

اهلا ياجماعة!

هل هناك أي إمكانية لدعم هذا الإصلاح إلى 0.5 سلسلة؟ أم أنها تعمل فقط فيما سيصبح 0.6؟

هذا تغيير جذري وسيكون متاحًا فقط في 0.6

من الصعب المبالغة في تقدير مدى روعة إصلاح هذا الأمر. تموت العادات القديمة بصعوبة لذلك أقوم أحيانًا بإعادة تشغيل و / أو إعادة بناء جوليا دون داع ، لكن عندما أتذكر أنها تغير قواعد اللعبة بالكامل لإصلاح المشكلات.

هل كانت هذه الصفحة مفيدة؟
0 / 5 - 0 التقييمات

القضايا ذات الصلة

tkoolen picture tkoolen  ·  3تعليقات

wilburtownsend picture wilburtownsend  ·  3تعليقات

i-apellaniz picture i-apellaniz  ·  3تعليقات

m-j-w picture m-j-w  ·  3تعليقات

TotalVerb picture TotalVerb  ·  3تعليقات