Re: qsort can get very slow
Urs Janßen <[email protected]> Sun, 19 Feb 2012 15:42:02 +0100
| Newsgroups | gmane.network.tin.devel |
|---|---|
| Message-ID | <[email protected]> |
--AqsLC8rIMeq19msA
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline
why ever the attachment was missing from the last mail, here it is...
--AqsLC8rIMeq19msA
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment; filename="heap.diff"
diff -Nurp --exclude='.bzr*' tin-2.1.1/configure.in tin-2.1.1/configure.in
--- tin-2.1.1/configure.in 2012-01-01 02:58:15.040563751 +0100
+++ tin-2.1.1/configure.in 2012-02-18 16:33:26.979840653 +0100
@@ -1009,6 +1009,7 @@ AC_CHECK_FUNCS(\
getcwd \
gettimeofday \
getwd \
+ heapsort \
inet_aton \
is_xterm \
isascii \
@@ -1231,6 +1232,7 @@ CF_CHECK_DECL(\
getcwd \
getopt \
getwd \
+ heapsort \
ioctl \
isascii \
kill \
diff -Nurp --exclude='.bzr*' tin-2.1.1/include/autoconf.hin tin-2.1.1/include/autoconf.hin
--- tin-2.1.1/include/autoconf.hin 2011-12-24 16:17:44.000000000 +0100
+++ tin-2.1.1/include/autoconf.hin 2012-02-18 16:34:34.222055460 +0100
@@ -260,6 +260,7 @@
# undef HAVE_GETSERVBYNAME
# undef HAVE_GETTIMEOFDAY
# undef HAVE_GETWD
+# undef HAVE_HEAPSORT
# undef HAVE_ICONV
# undef HAVE_INET_ADDR
# undef HAVE_INET_ATON
@@ -367,6 +368,7 @@
# undef DECL_GETHOSTNAME
# undef DECL_GETOPT
# undef DECL_GETWD
+# undef DECL_HEAPSORT
# undef DECL_INET_ADDR
# undef DECL_INET_ATON
# undef DECL_INET_NTOA
diff -Nurp --exclude='.bzr*' tin-2.1.1/include/extern.h tin-2.1.1/include/extern.h
--- tin-2.1.1/include/extern.h 2011-12-24 16:17:25.000000000 +0100
+++ tin-2.1.1/include/extern.h 2012-02-18 16:36:22.114445742 +0100
@@ -156,6 +156,9 @@
#ifdef DECL_GETWD
extern char *getwd(char *);
#endif /* DECL_GETWD */
+#ifdef DECL_HEAPSORT
+ extern int heapsort(void *, size_t, size_t, int (*)(t_comptype*, t_comptype*));
+#endif /* DECL_HEAPSORT */
#ifdef DECL_INET_ADDR
extern unsigned long inet_addr(const char *);
#endif /* DECL_INET_ADDR */
diff -Nurp --exclude='.bzr*' tin-2.1.1/include/proto.h tin-2.1.1/include/proto.h
--- tin-2.1.1/include/proto.h 2011-12-24 16:17:25.000000000 +0100
+++ tin-2.1.1/include/proto.h 2012-02-18 16:37:03.272698318 +0100
@@ -242,6 +242,11 @@ extern const char *get_host_name(void);
extern char *build_sender(void);
#endif /* !FORGERY */
+/* heapsort.c */
+#ifndef HAVE_HEAPSORT
+ extern int heapsort(void *, size_t, size_t, int (*)(const void *, const void *));
+#endif /* !HAVE_HEAPSORT */
+
/* inews.c */
extern t_bool submit_news_file(char *name, struct t_group *group, char *a_message_id);
extern void get_from_name(char *from_name, struct t_group *thisgrp);
diff -Nurp --exclude='.bzr*' tin-2.1.1/include/tin.h tin-2.1.1/include/tin.h
--- tin-2.1.1/include/tin.h 2011-12-24 16:17:26.000000000 +0100
+++ tin-2.1.1/include/tin.h 2012-02-18 16:58:23.579313913 +0100
@@ -2002,6 +2002,9 @@ typedef int (*t_compfunc)(t_comptype, t_
#define _CDECL
+/* set to (void)heapsort or qsort */
+#define tin_sort (void)heapsort
+
/* Seperator between dir part of path & the filename */
#define DIRSEP '/'
diff -Nurp --exclude='.bzr*' tin-2.1.1/src/Makefile.in tin-2.1.1/src/Makefile.in
--- tin-2.1.1/src/Makefile.in 2011-12-24 16:19:35.000000000 +0100
+++ tin-2.1.1/src/Makefile.in 2012-02-18 01:56:55.469171566 +0100
@@ -153,6 +153,7 @@ CFILES = \
$(SRCDIR)/group.c \
$(SRCDIR)/hashstr.c \
$(SRCDIR)/header.c \
+ $(SRCDIR)/heapsort.c \
$(SRCDIR)/help.c \
$(SRCDIR)/inews.c \
$(SRCDIR)/init.c \
@@ -229,6 +230,7 @@ OFILES = @ALLOCA@ $(EXTRAOBJS) \
$(OBJDIR)/group$o \
$(OBJDIR)/hashstr$o \
$(OBJDIR)/header$o \
+ $(OBJDIR)/heapsort$o \
$(OBJDIR)/help$o \
$(OBJDIR)/inews$o \
$(OBJDIR)/init$o \
@@ -526,6 +528,7 @@ $(OBJDIR)/group$o : $(SRCDIR)/group.c $
$(OBJDIR)/hashstr$o : $(SRCDIR)/hashstr.c $(TIN_DEP)
$(OBJDIR)/header$o : $(SRCDIR)/header.c $(TIN_DEP) \
$(INCDIR)/tnntp.h
+$(OBJDIR)/heapsort$o : $(SRCDIR)/heapsort.c $(TIN_DEP)
$(OBJDIR)/help$o : $(SRCDIR)/help.c $(TIN_DEP) \
$(INCDIR)/keymap.h
$(OBJDIR)/inews$o : $(SRCDIR)/inews.c $(TIN_DEP) \
diff -Nurp --exclude='.bzr*' tin-2.1.1/src/art.c tin-2.1.1/src/art.c
--- tin-2.1.1/src/art.c 2011-12-24 16:19:18.000000000 +0100
+++ tin-2.1.1/src/art.c 2012-02-18 16:31:46.600592238 +0100
@@ -52,7 +52,7 @@
/*
* TODO: fixup to remove CURR_GROUP dependency in all sort funcs
*/
-#define SortBy(func) qsort(arts, (size_t) top_art, sizeof(struct t_article), func);
+#define SortBy(func) tin_sort(arts, (size_t) top_art, sizeof(struct t_article), func);
int top_art = 0; /* # of articles in arts[] */
@@ -161,7 +161,7 @@ find_base(
/*
- * Longword comparison routine for the qsort()
+ * Longword comparison routine for the tin_sort()
*/
static int
base_comp(
@@ -362,7 +362,7 @@ setup_hard_base(
}
}
CLOSEDIR(d);
- qsort((char *) base, (size_t) grpmenu.max, sizeof(t_artnum), base_comp);
+ tin_sort((char *) base, (size_t) grpmenu.max, sizeof(t_artnum), base_comp);
}
}
@@ -1304,15 +1304,15 @@ sort_base(
switch (sort_threads_type) {
case SORT_THREADS_BY_SCORE_DESCEND:
case SORT_THREADS_BY_SCORE_ASCEND:
- qsort(base, (size_t) grpmenu.max, sizeof(t_artnum), score_comp_base);
+ tin_sort(base, (size_t) grpmenu.max, sizeof(t_artnum), score_comp_base);
break;
case SORT_THREADS_BY_LAST_POSTING_DATE_DESCEND:
- qsort(base, (size_t) grpmenu.max, sizeof(t_artnum), last_date_comp_base_desc);
+ tin_sort(base, (size_t) grpmenu.max, sizeof(t_artnum), last_date_comp_base_desc);
break;
case SORT_THREADS_BY_LAST_POSTING_DATE_ASCEND:
- qsort(base, (size_t) grpmenu.max, sizeof(t_artnum), last_date_comp_base_asc);
+ tin_sort(base, (size_t) grpmenu.max, sizeof(t_artnum), last_date_comp_base_asc);
break;
}
}
diff -Nurp --exclude='.bzr*' tin-2.1.1/src/heapsort.c tin-2.1.1/src/heapsort.c
--- tin-2.1.1/src/heapsort.c 1970-01-01 01:00:00.000000000 +0100
+++ tin-2.1.1/src/heapsort.c 2012-02-19 12:57:51.507353218 +0100
@@ -0,0 +1,196 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Ronnie Kon at Mindcraft Inc., Kevin Lew and Elmer Yglesias.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if 0
+# include <sys/cdefs.h>
+# include <errno.h>
+# include <stddef.h>
+# include <stdlib.h>
+#else
+# ifndef TIN_H
+# include "tin.h"
+# else
+# endif /* !TIN_H */
+#endif /* 0 */
+
+#ifndef HAVE_HEAPSORT
+/*
+ * Swap two areas of size number of bytes. Although qsort(3) permits random
+ * blocks of memory to be sorted, sorting pointers is almost certainly the
+ * common case (and, were it not, could easily be made so). Regardless, it
+ * isn't worth optimizing; the SWAP's get sped up by the cache, and pointer
+ * arithmetic gets lost in the time required for comparison function calls.
+ */
+#define SWAP(a, b, count, size, tmp) { \
+ count = size; \
+ do { \
+ tmp = *a; \
+ *a++ = *b; \
+ *b++ = tmp; \
+ } while (--count); \
+}
+
+/* Copy one block of size size to another. */
+#define COPY(a, b, count, size, tmp1, tmp2) { \
+ count = size; \
+ tmp1 = a; \
+ tmp2 = b; \
+ do { \
+ *tmp1++ = *tmp2++; \
+ } while (--count); \
+}
+
+/*
+ * Build the list into a heap, where a heap is defined such that for
+ * the records K1 ... KN, Kj/2 >= Kj for 1 <= j/2 <= j <= N.
+ *
+ * There two cases. If j == nmemb, select largest of Ki and Kj. If
+ * j < nmemb, select largest of Ki, Kj and Kj+1.
+ */
+#define CREATE(initval, nmemb, par_i, child_i, par, child, size, count, tmp) { \
+ for (par_i = initval; (child_i = par_i * 2) <= nmemb; \
+ par_i = child_i) { \
+ child = abase + child_i * size; \
+ if (child_i < nmemb && compar(child, child + size) < 0) { \
+ child += size; \
+ ++child_i; \
+ } \
+ par = abase + par_i * size; \
+ if (compar(child, par) <= 0) \
+ break; \
+ SWAP(par, child, count, size, tmp); \
+ } \
+}
+
+/*
+ * Select the top of the heap and 'heapify'. Since by far the most expensive
+ * action is the call to the compar function, a considerable optimization
+ * in the average case can be achieved due to the fact that k, the displaced
+ * elememt, is ususally quite small, so it would be preferable to first
+ * heapify, always maintaining the invariant that the larger child is copied
+ * over its parent's record.
+ *
+ * Then, starting from the *bottom* of the heap, finding k's correct place,
+ * again maintianing the invariant. As a result of the invariant no element
+ * is 'lost' when k is assigned its correct place in the heap.
+ *
+ * The time savings from this optimization are on the order of 15-20% for the
+ * average case. See Knuth, Vol. 3, page 158, problem 18.
+ *
+ * XXX Don't break the #define SELECT line, below. Reiser cpp gets upset.
+ */
+#define SELECT(par_i, child_i, nmemb, par, child, size, k, count, tmp1, tmp2) { \
+ for (par_i = 1; (child_i = par_i * 2) <= nmemb; par_i = child_i) { \
+ child = abase + child_i * size; \
+ if (child_i < nmemb && compar(child, child + size) < 0) { \
+ child += size; \
+ ++child_i; \
+ } \
+ par = abase + par_i * size; \
+ COPY(par, child, count, size, tmp1, tmp2); \
+ } \
+ for (;;) { \
+ child_i = par_i; \
+ par_i = child_i / 2; \
+ child = abase + child_i * size; \
+ par = abase + par_i * size; \
+ if (child_i == 1 || compar(k, par) < 0) { \
+ COPY(child, k, count, size, tmp1, tmp2); \
+ break; \
+ } \
+ COPY(child, par, count, size, tmp1, tmp2); \
+ } \
+}
+
+/*
+ * Heapsort -- Knuth, Vol. 3, page 145. Runs in O (N lg N), both average
+ * and worst. While heapsort is faster than the worst case of quicksort,
+ * the BSD quicksort does median selection so that the chance of finding
+ * a data set that will trigger the worst case is nonexistent. Heapsort's
+ * only advantage over quicksort is that it requires little additional memory.
+ */
+int
+heapsort(
+ void *vbase,
+ size_t nmemb,
+ size_t size,
+ int (*compar)(const void *, const void *))
+{
+ size_t cnt, i, j, l;
+ char tmp, *tmp1, *tmp2;
+ char *abase, *k, *p, *t;
+
+ if (nmemb <= 1)
+ return (0);
+
+ if (!size) {
+ errno = EINVAL;
+ return (-1);
+ }
+
+#if 0
+ if ((k = malloc(size)) == NULL)
+ return (-1);
+#else
+ k = my_malloc(size);
+#endif /* 0 */
+
+ /*
+ * Items are numbered from 1 to nmemb, so offset from size bytes
+ * below the starting address.
+ */
+ abase = (char *)vbase - size;
+
+ for (l = nmemb / 2 + 1; --l;)
+ CREATE(l, nmemb, i, j, t, p, size, cnt, tmp);
+
+ /*
+ * For each element of the heap, save the largest element into its
+ * final slot, save the displaced element (k), then recreate the
+ * heap.
+ */
+ while (nmemb > 1) {
+ COPY(k, abase + nmemb * size, cnt, size, tmp1, tmp2);
+ COPY(abase + nmemb * size, abase + size, cnt, size, tmp1, tmp2);
+ --nmemb;
+ SELECT(i, j, nmemb, t, p, size, k, cnt, tmp1, tmp2);
+ }
+ free(k);
+ return (0);
+}
+#else
+static void has_heapsort(void);
+static void has_heapsort( /* ANSI C requires non-empty source file */
+ void)
+{
+}
+#endif /* !HAVE_HEAPSORT */
diff -Nurp --exclude='.bzr*' tin-2.1.1/src/select.c tin-2.1.1/src/select.c
--- tin-2.1.1/src/select.c 2011-12-24 16:19:20.000000000 +0100
+++ tin-2.1.1/src/select.c 2012-02-18 16:37:55.892692182 +0100
@@ -813,7 +813,7 @@ yank_active_file(
/*
- * Sort active[] and associated qsort() helper function
+ * Sort active[] and associated tin_sort() helper function
*/
static int
active_comp(
@@ -876,7 +876,7 @@ sort_active_file(
{
save_restore_curr_group(TRUE);
- qsort(active, (size_t) num_active, sizeof(struct t_group), active_comp);
+ tin_sort(active, (size_t) num_active, sizeof(struct t_group), active_comp);
group_rehash(yanked_out);
selmenu.curr = save_restore_curr_group(FALSE);
--AqsLC8rIMeq19msA--