comparison liboctave/oct-sort.cc @ 9362:2ebf3ca62add

use a smarter algorithm for default lookup
author Jaroslav Hajek <highegg@gmail.com>
date Thu, 18 Jun 2009 15:06:35 +0200
parents 9fd5c56ce57a
children da465c405d84
comparison
equal deleted inserted replaced
9361:a00e219c402d 9362:2ebf3ca62add
1740 retval = is_sorted_rows (data, rows, cols, compare); 1740 retval = is_sorted_rows (data, rows, cols, compare);
1741 1741
1742 return retval; 1742 return retval;
1743 } 1743 }
1744 1744
1745 // A helper function (just to avoid repeating expressions).
1746 template <class T, class Comp>
1747 inline octave_idx_type
1748 lookup_impl (const T *data, octave_idx_type lo,
1749 octave_idx_type hi, const T& val, Comp comp)
1750 {
1751 while (lo < hi)
1752 {
1753 octave_idx_type mid = lo + ((hi-lo) >> 1);
1754 if (comp (val, data[mid]))
1755 hi = mid;
1756 else
1757 lo = mid + 1;
1758 }
1759
1760 return lo;
1761 }
1762
1745 1763
1746 template <class T> template <class Comp> 1764 template <class T> template <class Comp>
1747 octave_idx_type 1765 octave_idx_type
1748 octave_sort<T>::lookup (const T *data, octave_idx_type nel, 1766 octave_sort<T>::lookup (const T *data, octave_idx_type nel,
1749 const T& value, Comp comp) 1767 const T& value, Comp comp)
1750 { 1768 {
1751 return std::upper_bound (data, data + nel, value, comp) - data; 1769 return lookup_impl (data, 0, nel, value, comp);
1752 } 1770 }
1753 1771
1754 template <class T> 1772 template <class T>
1755 octave_idx_type 1773 octave_idx_type
1756 octave_sort<T>::lookup (const T *data, octave_idx_type nel, 1774 octave_sort<T>::lookup (const T *data, octave_idx_type nel,
1772 retval = lookup (data, nel, value, std::ptr_fun (compare)); 1790 retval = lookup (data, nel, value, std::ptr_fun (compare));
1773 1791
1774 return retval; 1792 return retval;
1775 } 1793 }
1776 1794
1777 // a unary functor that checks whether a value is outside [a,b) range 1795 // A recursively splitting lookup of a sorted array in another sorted array.
1778 template<class T, class Comp> 1796 template <class T> template <class Comp>
1779 class out_of_range_pred : public std::unary_function<T, bool> 1797 void
1780 { 1798 octave_sort<T>::lookup_merge (const T *data, octave_idx_type lo, octave_idx_type hi,
1781 public: 1799 const T *values, octave_idx_type nvalues,
1782 out_of_range_pred (const T& aa, const T& bb, Comp c) 1800 octave_idx_type *idx, Comp comp)
1783 : a (aa), b (bb), comp (c) { } 1801 {
1784 bool operator () (const T& x) { return comp (x, a) || ! comp (x, b); } 1802 // Fake entry.
1785 1803 begin:
1786 private: 1804
1787 T a, b; 1805 if (hi - lo <= nvalues + 16)
1788 Comp comp; 1806 {
1789 }; 1807 // Do a linear merge.
1790 1808 octave_idx_type i = lo, j = 0;
1791 // a unary functor that checks whether a value is < a 1809 while (j != nvalues && i < hi)
1792 template<class T, class Comp> 1810 {
1793 class less_than_pred : public std::unary_function<T, bool> 1811 if (comp (values[j], data[i]))
1794 { 1812 idx[j++] = i;
1795 typedef typename ref_param<T>::type param_type; 1813 else
1796 public: 1814 i++;
1797 less_than_pred (param_type aa, Comp c) 1815 }
1798 : a (aa), comp (c) { } 1816 while (j != nvalues)
1799 bool operator () (const T& x) { return comp (x, a); } 1817 idx[j++] = i;
1800 1818 }
1801 private: 1819 else
1802 T a; 1820 {
1803 Comp comp; 1821 // Use the ordering to split the table; look-up recursively.
1804 }; 1822 switch (nvalues)
1805 1823 {
1806 // a unary functor that checks whether a value is >= a 1824 case 0:
1807 template<class T, class Comp> 1825 break;
1808 class greater_or_equal_pred : public std::unary_function<T, bool> 1826 case 1:
1809 { 1827 idx[0] = lookup_impl (data, lo, hi, values[0], comp);
1810 public: 1828 break;
1811 greater_or_equal_pred (const T& aa, Comp c) 1829 case 2:
1812 : a (aa), comp (c) { } 1830 idx[0] = lookup_impl (data, lo, hi, values[0], comp);
1813 bool operator () (const T& x) { return ! comp (x, a); } 1831 idx[1] = lookup_impl (data, idx[0], hi, values[1], comp);
1814 1832 break;
1815 private: 1833 case 3:
1816 T a; 1834 idx[1] = lookup_impl (data, lo, hi, values[1], comp);
1817 Comp comp; 1835 idx[0] = lookup_impl (data, lo, idx[1], values[0], comp);
1818 }; 1836 idx[2] = lookup_impl (data, idx[1], hi, values[2], comp);
1819 1837 break;
1820 // conveniently constructs the above functors. 1838 case 4:
1821 // NOTE: with SGI extensions, this one can be written as 1839 idx[2] = lookup_impl (data, lo, hi, values[2], comp);
1822 // compose2 (logical_and<bool>(), bind2nd (less<T>(), a), 1840 idx[0] = lookup_impl (data, lo, idx[2], values[0], comp);
1823 // not1 (bind2nd (less<T>(), b))) 1841 idx[1] = lookup_impl (data, idx[0], idx[2], values[1], comp);
1824 template<class T, class Comp> 1842 idx[3] = lookup_impl (data, idx[2], hi, values[3], comp);
1825 inline out_of_range_pred<T, Comp> 1843 break;
1826 out_of_range (const T& a, 1844 case 5:
1827 const T& b, Comp comp) 1845 idx[2] = lookup_impl (data, lo, hi, values[2], comp);
1828 { 1846 idx[0] = lookup_impl (data, lo, idx[2], values[0], comp);
1829 return out_of_range_pred<T, Comp> (a, b, comp); 1847 idx[1] = lookup_impl (data, idx[0], idx[2], values[1], comp);
1830 } 1848 idx[3] = lookup_impl (data, idx[2], hi, values[3], comp);
1831 1849 idx[4] = lookup_impl (data, idx[3], hi, values[4], comp);
1832 // Note: these could be written as 1850 break;
1833 // std::not1 (std::bind2nd (comp, *cur)) 1851 default:
1834 // and 1852 {
1835 // std::bind2nd (comp, *(cur-1))); 1853 // This is a bit tricky. We do not want a normal recursion
1836 // but that doesn't work for functions with reference parameters in g++ 4.3. 1854 // because we split by idx[k], and there's no guaranteed descend
1837 template<class T, class Comp> 1855 // ratio; hence the worst-case scenario would be a linear stack
1838 inline less_than_pred<T, Comp> 1856 // growth, which is dangerous. Instead, we recursively run the
1839 less_than (const T& a, Comp comp) 1857 // smaller half, and simulate a tail call for the rest using
1840 { 1858 // goto.
1841 return less_than_pred<T, Comp> (a, comp); 1859 octave_idx_type k = nvalues / 2;
1842 } 1860 idx[k] = lookup_impl (data, lo, hi, values[k], comp);
1843 template<class T, class Comp> 1861 if (idx[k] - lo <= hi - idx[k])
1844 inline greater_or_equal_pred<T, Comp> 1862 {
1845 greater_or_equal (const T& a, Comp comp) 1863 // The smaller portion is run recursively.
1846 { 1864 lookup_merge (data, idx[k], k, values, k, idx, comp);
1847 return greater_or_equal_pred<T, Comp> (a, comp); 1865 // Simulate a tail call.
1848 } 1866 lo = idx[k];
1849 1867 values += k; nvalues -= k; idx += k;
1868 goto begin;
1869 }
1870 else
1871 {
1872 // The smaller portion is run recursively.
1873 lookup_merge (data, idx[k], hi,
1874 values + k, nvalues - k, idx, comp);
1875 // Simulate a tail call.
1876 hi = idx[k];
1877 nvalues = k;
1878 goto begin;
1879 }
1880 break;
1881 }
1882 }
1883 }
1884 }
1850 1885
1851 template <class T> template <class Comp> 1886 template <class T> template <class Comp>
1852 void 1887 void
1853 octave_sort<T>::lookup (const T *data, octave_idx_type nel, 1888 octave_sort<T>::lookup (const T *data, octave_idx_type nel,
1854 const T *values, octave_idx_type nvalues, 1889 const T *values, octave_idx_type nvalues,
1857 if (nel == 0) 1892 if (nel == 0)
1858 // the trivial case of empty table 1893 // the trivial case of empty table
1859 std::fill_n (idx, nvalues, offset); 1894 std::fill_n (idx, nvalues, offset);
1860 else 1895 else
1861 { 1896 {
1862 const T *vcur = values; 1897 octave_idx_type vlo = 0;
1863 const T *vend = values + nvalues; 1898 int nbadruns = 0;
1864 1899 while (vlo != nvalues && nbadruns < 16)
1865 const T *cur = data;
1866 const T *end = data + nel;
1867
1868 while (vcur != vend)
1869 { 1900 {
1870 // determine the enclosing interval for next value, trying 1901 octave_idx_type vhi;
1871 // ++cur as a special case; 1902
1872 if (cur == end || comp (*vcur, *cur)) 1903 // Determine a sorted run.
1873 cur = std::upper_bound (data, cur, *vcur, comp); 1904 for (vhi = vlo + 1; vhi != nvalues; vhi++)
1874 else
1875 { 1905 {
1876 ++cur; 1906 if (! comp (values[vhi-1], values[vhi]))
1877 if (cur != end && ! comp (*vcur, *cur)) 1907 break;
1878 cur = std::upper_bound (cur + 1, end, *vcur, comp);
1879 } 1908 }
1880 1909
1881 octave_idx_type vidx = cur - data + offset; 1910 // Do a recursive lookup.
1882 // store index of the current interval. 1911 lookup_merge (data - offset, offset, nel + offset,
1883 *(idx++) = vidx; 1912 values + vlo, vhi - vlo, idx + vlo, comp);
1884 ++vcur; 1913
1885 1914 if (vhi - vlo <= 2)
1886 // find first value not in current subrange 1915 nbadruns++;
1887 const T *vnew; 1916 else if (vhi - vlo >= 16)
1888 if (cur != end) 1917 nbadruns = 0;
1889 if (cur != data) 1918
1890 // inner interval 1919 vlo = vhi;
1891 vnew = std::find_if (vcur, vend,
1892 out_of_range (*(cur-1), *cur, comp));
1893 else
1894 // special case: lowermost range (-Inf, min)
1895 vnew = std::find_if (vcur, vend, greater_or_equal (*cur, comp));
1896 else
1897 // special case: uppermost range [max, Inf)
1898 vnew = std::find_if (vcur, vend, less_than (*(cur-1), comp));
1899
1900 // store index of the current interval.
1901 std::fill_n (idx, vnew - vcur, vidx);
1902 idx += (vnew - vcur);
1903 vcur = vnew;
1904 } 1920 }
1921
1922 // The optimistic loop terminated, do the rest normally.
1923 for (; vlo != nvalues; vlo++)
1924 idx[vlo] = lookup_impl (data, 0, nel, values[vlo], comp) + offset;
1905 } 1925 }
1906 } 1926 }
1907 1927
1908 template <class T> 1928 template <class T>
1909 void 1929 void
1929 void 1949 void
1930 octave_sort<T>::lookupm (const T *data, octave_idx_type nel, 1950 octave_sort<T>::lookupm (const T *data, octave_idx_type nel,
1931 const T *values, octave_idx_type nvalues, 1951 const T *values, octave_idx_type nvalues,
1932 octave_idx_type *idx, Comp comp) 1952 octave_idx_type *idx, Comp comp)
1933 { 1953 {
1934 const T *end = data + nel;
1935 for (octave_idx_type i = 0; i < nvalues; i++) 1954 for (octave_idx_type i = 0; i < nvalues; i++)
1936 { 1955 {
1937 const T *ptr = std::lower_bound (data, end, values[i], comp); 1956 octave_idx_type j = lookup_impl (data, 0, nel, values[i], comp);
1938 if (ptr != end && ! comp (values[i], *ptr)) 1957 idx[i] = (j != 0 && comp (data[j-1], values[i])) ? -1 : j-1;
1939 idx[i] = ptr - data;
1940 else
1941 idx[i] = -1;
1942 } 1958 }
1943 } 1959 }
1944 1960
1945 template <class T> 1961 template <class T>
1946 void 1962 void
1966 void 1982 void
1967 octave_sort<T>::lookupb (const T *data, octave_idx_type nel, 1983 octave_sort<T>::lookupb (const T *data, octave_idx_type nel,
1968 const T *values, octave_idx_type nvalues, 1984 const T *values, octave_idx_type nvalues,
1969 bool *match, Comp comp) 1985 bool *match, Comp comp)
1970 { 1986 {
1971 const T *end = data + nel;
1972 for (octave_idx_type i = 0; i < nvalues; i++) 1987 for (octave_idx_type i = 0; i < nvalues; i++)
1973 match[i] = std::binary_search (data, end, values[i], comp); 1988 {
1989 octave_idx_type j = lookup_impl (data, 0, nel, values[i], comp);
1990 match[i] = (j != 0 && ! comp (data[j-1], values[i]));
1991 }
1974 } 1992 }
1975 1993
1976 template <class T> 1994 template <class T>
1977 void 1995 void
1978 octave_sort<T>::lookupb (const T *data, octave_idx_type nel, 1996 octave_sort<T>::lookupb (const T *data, octave_idx_type nel,