diff --git a/src/kernel/default_converter.cpp b/src/kernel/default_converter.cpp index 84519f2c0..71211114b 100644 --- a/src/kernel/default_converter.cpp +++ b/src/kernel/default_converter.cpp @@ -432,6 +432,25 @@ bool default_converter::is_def_eq_proof_irrel(expr const & t, expr const & s, co return false; } +bool default_converter::failed_before(expr const & t, expr const & s) const { + if (t.hash() < s.hash()) { + return m_failure_cache.find(mk_pair(t, s)) != m_failure_cache.end(); + } else if (t.hash() > s.hash()) { + return m_failure_cache.find(mk_pair(s, t)) != m_failure_cache.end(); + } else { + return + m_failure_cache.find(mk_pair(t, s)) != m_failure_cache.end() || + m_failure_cache.find(mk_pair(s, t)) != m_failure_cache.end(); + } +} + +void default_converter::cache_failure(expr const & t, expr const & s) { + if (t.hash() <= s.hash()) + m_failure_cache.insert(mk_pair(t, s)); + else + m_failure_cache.insert(mk_pair(s, t)); +} + pair default_converter::is_def_eq_core(expr const & t, expr const & s) { check_system("is_definitionally_equal"); constraint_seq cs; @@ -480,11 +499,14 @@ pair default_converter::is_def_eq_core(expr const & t, exp // skip the delta-reduction step. // If the flag use_conv_opt() is not true, then we skip this optimization constraint_seq tmp_cs; - if (!is_opaque(*d_t) && d_t->use_conv_opt() && - is_def_eq(const_levels(get_app_fn(t_n)), const_levels(get_app_fn(s_n)), tmp_cs) && - is_def_eq_args(t_n, s_n, tmp_cs)) { - cs += tmp_cs; - return to_bcs(true, cs); + if (!is_opaque(*d_t) && d_t->use_conv_opt() && !failed_before(t_n, s_n)) { + if (is_def_eq(const_levels(get_app_fn(t_n)), const_levels(get_app_fn(s_n)), tmp_cs) && + is_def_eq_args(t_n, s_n, tmp_cs)) { + cs += tmp_cs; + return to_bcs(true, cs); + } else { + cache_failure(t_n, s_n); + } } } } diff --git a/src/kernel/default_converter.h b/src/kernel/default_converter.h index 2300645e2..219c5d1ea 100644 --- a/src/kernel/default_converter.h +++ b/src/kernel/default_converter.h @@ -5,22 +5,26 @@ Released under Apache 2.0 license as described in the file LICENSE. Author: Leonardo de Moura */ #pragma once +#include #include "util/lbool.h" #include "kernel/justification.h" #include "kernel/environment.h" #include "kernel/converter.h" #include "kernel/expr_maps.h" #include "kernel/equiv_manager.h" +#include "kernel/expr_pair.h" namespace lean { /** \breif Converter used in the kernel */ class default_converter : public converter { protected: + typedef std::unordered_set expr_pair_set; environment m_env; bool m_memoize; expr_struct_map m_whnf_core_cache; expr_struct_map> m_whnf_cache; equiv_manager m_eqv_manager; + expr_pair_set m_failure_cache; // The two auxiliary fields are set when the public methods whnf and is_def_eq are invoked. // The goal is to avoid to keep carrying them around. @@ -66,6 +70,9 @@ protected: bool is_def_eq_app(expr const & t, expr const & s, constraint_seq & cs); bool is_def_eq_proof_irrel(expr const & t, expr const & s, constraint_seq & cs); + bool failed_before(expr const & t, expr const & s) const; + void cache_failure(expr const & t, expr const & s); + pair is_prop(expr const & e); pair whnf(expr const & e_prime); pair is_def_eq_core(expr const & t, expr const & s); diff --git a/src/kernel/expr_pair.h b/src/kernel/expr_pair.h new file mode 100644 index 000000000..7c3c16601 --- /dev/null +++ b/src/kernel/expr_pair.h @@ -0,0 +1,27 @@ +/* +Copyright (c) 2015 Microsoft Corporation. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. + +Author: Leonardo de Moura +*/ +#pragma once +#include "kernel/expr.h" +namespace lean { +typedef pair expr_pair; +/** \brief Functional object for hashing expression pairs. */ +struct expr_pair_hash { + unsigned operator()(expr_pair const & p) const { return hash(p.first.hash(), p.second.hash()); } +}; +/** \brief Functional object for hashing expression pairs (based on their allocation time). */ +struct expr_pair_hash_alloc { + unsigned operator()(expr_pair const & p) const { return hash(p.first.hash_alloc(), p.second.hash_alloc()); } +}; +/** \brief Functional object for comparing expression pairs. */ +struct expr_pair_eq { + bool operator()(expr_pair const & p1, expr_pair const & p2) const { return p1.first == p2.first && p1.second == p2.second; } +}; +/** \brief Functional object for comparing expression pairs using pointer equality. */ +struct expr_pair_eqp { + bool operator()(expr_pair const & p1, expr_pair const & p2) const { return is_eqp(p1.first, p2.first) && is_eqp(p1.second, p2.second); } +}; +} diff --git a/src/library/expr_pair.h b/src/library/expr_pair.h index d2806adbe..34e4ac176 100644 --- a/src/library/expr_pair.h +++ b/src/library/expr_pair.h @@ -7,27 +7,10 @@ Author: Leonardo de Moura #pragma once #include #include "util/hash.h" -#include "kernel/expr.h" +#include "kernel/expr_pair.h" #include "library/expr_lt.h" namespace lean { -typedef pair expr_pair; -/** \brief Functional object for hashing expression pairs. */ -struct expr_pair_hash { - unsigned operator()(expr_pair const & p) const { return hash(p.first.hash(), p.second.hash()); } -}; -/** \brief Functional object for hashing expression pairs (based on their allocation time). */ -struct expr_pair_hash_alloc { - unsigned operator()(expr_pair const & p) const { return hash(p.first.hash_alloc(), p.second.hash_alloc()); } -}; -/** \brief Functional object for comparing expression pairs. */ -struct expr_pair_eq { - bool operator()(expr_pair const & p1, expr_pair const & p2) const { return p1.first == p2.first && p1.second == p2.second; } -}; -/** \brief Functional object for comparing expression pairs using pointer equality. */ -struct expr_pair_eqp { - bool operator()(expr_pair const & p1, expr_pair const & p2) const { return is_eqp(p1.first, p2.first) && is_eqp(p1.second, p2.second); } -}; inline bool is_lt(expr_pair const & p1, expr_pair const & p2, bool use_hash) { return is_lt(p1.first, p2.first, use_hash) || (p1.first == p2.first && is_lt(p1.second, p2.second, use_hash)); } diff --git a/src/shell/CMakeLists.txt b/src/shell/CMakeLists.txt index 22e9d2903..a383ae080 100644 --- a/src/shell/CMakeLists.txt +++ b/src/shell/CMakeLists.txt @@ -49,6 +49,9 @@ add_test(NAME "issue_597" add_test(NAME "issue_616" WORKING_DIRECTORY "${LEAN_SOURCE_DIR}/../tests/lean/extra" COMMAND bash "./issue_616.sh" "${CMAKE_CURRENT_BINARY_DIR}/lean") +add_test(NAME "normalizer_perf" + WORKING_DIRECTORY "${LEAN_SOURCE_DIR}/../tests/lean/extra" + COMMAND bash "./timeout.sh" "${CMAKE_CURRENT_BINARY_DIR}/lean" "1" "slow1.lean") # LEAN TESTS file(GLOB LEANTESTS "${LEAN_SOURCE_DIR}/../tests/lean/*.lean") diff --git a/tests/lean/extra/slow1.lean b/tests/lean/extra/slow1.lean new file mode 100644 index 000000000..b0c462d93 --- /dev/null +++ b/tests/lean/extra/slow1.lean @@ -0,0 +1,11 @@ +open nat + +definition f (a : nat) : nat := a +definition g (a : nat) : nat := zero + +example (a b : nat) : +@eq nat +(g (f (f (f (f (f (f (f (f (f (f (f (f (f (f (f (f (f (f (f (f (f a)))))))))))))))))))))) +(g (f (f (f (f (f (f (f (f (f (f (f (f (f (f (f (f (f (f (f (f (f b)))))))))))))))))))))) +:= +@eq.refl nat (g (f (f (f (f (f (f (f (f (f (f (f (f (f (f (f (f (f (f (f (f (f a)))))))))))))))))))))) diff --git a/tests/lean/extra/timeout.sh b/tests/lean/extra/timeout.sh new file mode 100755 index 000000000..8e1d006ee --- /dev/null +++ b/tests/lean/extra/timeout.sh @@ -0,0 +1,16 @@ +#!/bin/sh +# Execute lean with timeout +set -e +if [ $# -ne 3 ]; then + echo "Usage: timeout.sh [lean-executable-path] [timeout] [benchmark]" + exit 1 +fi +LEAN=$1 +TIMEOUT=$2 +BENCH=$3 +export LEAN_PATH=../../../library:. +ulimit -t $2 +if ! $LEAN $BENCH; then + echo "Failed to execute $BENCH in $TIMEOUT second(s)" + exit 1 +fi