我正在开发一个需要快速运行且正确的软件包.我想只编写一个函数,但有两个“函数”版本:一个在检测到任何有趣的业务时立即停止,另一个尽可能快地运行.我的想法是在随机的输入样本上运行严格的函数版本,如果没有任何失败,请在整个输入集上运行快速版本.我绝对没有计算能力来对每一次输入进行每次检查,即使我可以睡得更轻松. (我对这个已经是解决验证问题的错误方法持开放态度,所以如果你有一个更好的组织方法,请随意建议,而不是对下面的具体改进.)
到目前为止,我已经解决了这个问题的临时解决方案,如下所示:
module Foo
export some_function
const using_removeable_assertions = true
macro RemoveablyAssert(the_condition)
if using_removeable_assertions
return esc(:($the_condition || error("Assertion failed.")))
else
return esc("")
end
end
function some_function(x::Float64,y::Float64)
#Say this should only be called on positive numbers
@RemoveablyAssert x>0
@RemoveablyAssert y>0
(x + y/2.0)::Float64
end
end
using Foo
这样可行,
julia> some_function(1.0,1.0)
1.5
julia> some_function(1.0,-1.0)
ERROR: Assertion failed.
in some_function at none:18
如果我重新加载我手动更改const的模块using_removeable_assertions = false,那么它不仅会跳过这些检查,
julia> some_function(1.0,1.0)
1.5
julia> some_function(1.0,-1.0)
0.5
但事实上,正如预期的那样,本机代码与我从未放过这些行的相同功能相同:
julia> @code_native some_function(1.0,1.0)
.section __TEXT,__text,regular,pure_instructions
Filename: none
Source line: 19
pushq %rbp
movq %rsp, %rbp
movabsq $13174486592, %rax ## imm = 0x31142B640
Source line: 19
vmulsd (%rax), %xmm1, %xmm1
vaddsd %xmm0, %xmm1, %xmm0
popq %rbp
ret
例如.如果我定义
function some_function_2(x::Float64,y::Float64)
(x + y/2.0)::Float64
end
然后
julia> @code_native some_function_2(1.0,1.0)
.section __TEXT,__text,regular,pure_instructions
Filename: none
Source line: 2
pushq %rbp
movq %rsp, %rbp
movabsq $13174493536, %rax ## imm = 0x31142D160
Source line: 2
vmulsd (%rax), %xmm1, %xmm1
vaddsd %xmm0, %xmm1, %xmm0
popq %rbp
ret
所以:这些是解决方案的两个理想属性,
>打开/关闭一系列严格的健全性检查应该与翻转开关一样简单
>在“关闭”的情况下,本机代码应该完全清除对检查的任何引用;特别是,应该没有
关闭情况下的性能差异
但我想要的解决方案是手动少一点,更习惯或标准.
注意,手动定义每个函数的两个版本是不切实际的,因为维护它们会很麻烦.所以我考虑使用另一个为每个some_function生成some_function_safe的宏,然后只是手动运行安全检查,但我希望有一个更好的解决方案,类似于julia的命令行参数,但具体到模块.例如.使用[safemode = yes] Foo.
相关地,我想知道是否有一个很好的方法来处理多个检查“级别”,除了一些类似的黑客,例如定义全局“安全级别”,然后使用宏有条件地注入检查,前提是全局安全级别超过特定检查级别.如果上面的[safemode = yes]想法可以实现,那么肯定也可以实现,但我想知道是否有更好的方法来组织这些类型的检查.
(FWIW,我认为这些检查是对一个大型测试套件的补充,而不是替代它.单独的测试套件不会让我睡不着觉.另外,虽然我的例子是参数验证,但我想用这些检查不仅仅是前后条件,但也适用于所有类型的健全性检查,例如检查方法体中的不变量,并针对我已经制定和修复的错误进行特定检查,等等)
最佳答案 我为Julia的工作做了一些相似的事情,我有一个宏可以被完全删除,或者如果编译进去,检查一个我可以设置的掩码来控制哪些实际被检查.
就单元测试而言,我们一直在使用FactCheck.jl包.
我们计划设置代码进行代码覆盖率测试(我们不能将我们的内部代码放在GitHub上,设置起来很简单),我认为这对于单元测试来说是至关重要的,以确保合理的代码.
如果您的代码在GitHub上,我认为从工作服或codecov.io添加代码覆盖支持是微不足道的.