Julia: 自动重新编译相关函数

创建于 2011-11-26  ·  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

最有用的评论

哇。 多么美好的时刻!

所有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相同的工作。

笨蛋好吧,由于受影响的主要是代表,所以慢慢地做正确的事就可以了。 在运行时,这几乎应该永远不会开始-为什么有人要定义某些内容,然后再以交互方式重新定义它? 如果该解决方案最终导致大量开销,也许将其转向应该是可选的,并且可以在repl中自动完成?

我们需要记录当前尚未定义。

从技术上讲,这不是错误,而是未定义的行为。 重新定义方法时,结果的行为是不确定的。 因此,Julia可以做任何喜欢的事情,包括当前所做的事情。 在方法重新定义时提供明确定义的行为是功能,而不是错误修复。 我也不认为这是v1.0问题,因为从不确定的行为转变为提供良好定义的行为并不是一项重大变化。 这可以在v1.1中实现,而不会破坏任何有效的v1.0代码。

苹果公司LLVM / LLDB员工的Greg Clayton非常友好地记录了如何从嵌入式符号信息(符号导入)中引出必要的信息(使用lldb库,llvm的子项目)来确定二进制文件的依赖关系。 以及由二进制文件导出的那些符号(构造完整的依赖图是必需的)。

  • 杰森

2012年3月31日,下午11:02,Jason E. Aten写道:

尊敬的LLDB爱好者,

我想知道是否可以使用lldb库/库替换OSX上运行的某些代码,该代码现在返回两个符号列表-类似于(dyldinfo -lazy_bind -exports的输出); 即我需要列出由二进制共享对象或捆绑软件导入和导出的符号。

我的希望是,通过使用lldb库,我将能够在OSX和Linux上使用相同的客户端代码。 (该代码的linux版本当前使用libbfd和libdld来做同样的事情,但后来却很少有人爱护/维护)。

我正在查看include / lldb /,因为lldb似乎需要此相同的信息(导入的符号列表和Mach-O文件的导出的符号列表)才能起作用,但尚不清楚要使用哪个API。 欢迎对lldb中的示例代码提出所有建议/指针!

谢谢。
杰森

如果不清楚dyldinfo的功能,请举一个例子:(但我只需要符号名称;不需要地址,段或节):

$ file / tmp / sample_bundle
/ tmp / sample_bundle:Mach-O 64位捆绑软件x86_64

$ dyldinfo -lazy_bind -export / tmp / sample_bundle

延迟绑定信息(来自dyld信息的lazy_bind部分):
段节地址索引dylib符号
__DATA __la_symbol_ptr 0x00001030 0x0000平面命名空间__mm_pop_chunk
__DATA __la_symbol_ptr 0x00001038 0x0015平面命名空间_dh_define
出口信息(从特里):
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

2012年4月2日,星期一,下午3:13,格雷格·克莱顿(Greg Clayton) [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

2012年4月2日,星期一,下午4:05,格雷格·克莱顿(Greg Clayton) [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() ,从而重新定义,然后在每次加载新的基准文件时调用它们。 因为我没有重新定义第二层函数,所以我认为我不会遇到这里列出的问题,但是重新定义这样的事情是否是一种不好的方式来加载具有相同“ API”的多个文件?

最好将它们反复包含在模块中,但是我意识到,这与开发列表中也提到的匿名模块问题大相径庭。 但是,在这种情况下,可能会很好,因为您可以多次定义同一模块,而忽略发出的警告。

也许是一个旧的线程,但与以往一样重要。
也许另一种方法是使用evalfile并使文件“返回”一个功能元组。 什么是匿名模块问题?

我指的是这个: https :

有人对实施策略还有其他想法吗? 我当时正在考虑编写一个简单的方案,以维护一个反向调用图,该调用图在创建新方法时会更新(基于未优化的AST)。 然后,当重新定义方法时,它将遍历该图,从而删除每个重新定义函数的后代的编译版本。

与任何尝试的方法一样,这是合理的方法。 如果您有动力去做,那就说吧,让我们看看会发生什么!

如果被调用方的_execution_触发了调用方的重新编译会怎样?

我问,因为我已经很晚才意识到解决此问题可能会带来超出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

因此,第一次为4维数组执行myfunction时,它首先定义了一个4维特定的版本,将其添加到_method_cache ,然后在输入上求值新函数。 在以后使用4维数组调用myfunction时,它仅从字典中检索定义并对其求值。 _method_cache是与Julia自己的内部方法表平行的一种“影子方法表”,该表仅用于此特定功能。 (为使其保密,通常将其封装在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已经被编译,因此它不知道新定义。 因此,这个特定的调用者将始终使用eval生成myfunction新版本,这真是太糟糕了。

因此,诀窍是重新编译调用方,但请注意,这需要在execution_中间进行。 会发生什么事?

另请参阅#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这个想法的一个问题是Julia内联了许多较小的函数,因此我们仍然需要一种方法来查找某个函数的所有“依赖项”。

它只能像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))))

是否可以使用每次分配任何内容时都会更改的数字来索引函数的方法(或仅函数类型/对象本身),从而可以检查是否已随编译(调用方)函数存储的数字(即使用新结构)对索引进行工作?函数,将这些数字保存为字段等)仍与内联(称为)函数上的“当前”数字匹配?
(当然不会有很大的性能损失)
如有更改,请考虑编译新版本或通过显式调用完成。

这会将性能影响转移到运行时(并且无处不在),这是不希望的。 需要的是跟踪依赖项的网络并重新编译部分重新定义方法时可能会影响的任何内容。 这可能是可行的,只是一种巨大的痛苦。

我不知道该问题的解决方案是否可以轻松地推广到宏,但是如果是这样的话,那将是一个很好的选择。

因此,一个人应该走另一条路,并存储所有内联函数的方法,并在更改时重新编译它们。 (当然要检查当前执行情况等)

这里的难题是:

  • 如您所说的使代码无效,找出使用旧函数的所有位置(在生成的代码中)。 这可能意味着能够重新定位调用已更改但调用约定和返回类型未更改的函数的代码。
  • 更难:如果在定义新方法时另一个任务正在运行,会发生什么? 它看到什么状态? 您要解决此问题而无需更换堆栈,因为这很难有效地实现。 我们认为我们有一个解决方案,不会破坏性能,而且我认为

当解决问题的方法时,我很好奇是否有可能(在(且仅当)给定函数以任何上面的方式使用时,才将标志添加到设置的每个函数中(内联等),以产生警告,说明内联方法已重新定义。 (类似于模棱两可的警告的工作方式)
如果没有更好的方法来保存标志,那么可以再次使用julia 0.5中的方法/函数的新结构

现在,在repl中更容易实现这一点,也许应该将这种行为记录在地图中?

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有运行时性能或内存折衷吗?

它需要一点内存(140MB-> 170 MB),但对性能(编译或运行时)的影响不大。 而且我还没有真正尝试过优化。

到目前为止的演示很有趣:

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

是不是由于g编译与重新定义f执行的顺序而导致不返回2、3、4的原因?

没错。 请注意,语言语义没有定义编译,因此更正确地说是解释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 ,您知道必须在REPL显示之前至少调用100次。 然后,我们会留下深刻的印象。

实时重新构建用于概要分析/跟踪的代码的机会让我震惊。

确实。 在这里我要购买一个新的键盘,因为序列Ctrl-D; julia<Enter>即将用完。 看起来我可能没有必要。

放在发行说明中可能有点奥秘,但是应该指出,现在理解是collect -ing 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有用

这真的固定fixed

是否仍计划将此解决方案反向移植到0.5.x?

没有。

@ rapus95 ,这是一个非常具有侵略性的更改,如果弄错了它,可能会使许多用户的

我很高兴看到这个问题的进展! 如果这还使编译器能够优化https://groups.google.com/forum/#!topic/julia -users / OBs0fmNmjCU中描述的情况,那将是惊人的。

哇。 多么美好的时刻!

嗨,大家好!

是否有可能将此修补程序反向移植到0.5系列? 还是仅在变为0.6时起作用?

这是一个重大变化,仅适用于0.6

很难修复这个问题。 旧习惯很难改掉,所以我有时会不必要地重新启动和/或重建julia,但是当我记得它是解决问题的完整游戏规则。

此页面是否有帮助?
0 / 5 - 0 等级