]> git.ipfire.org Git - thirdparty/gettext.git/commitdiff
Use OpenMP to speed up msgmerge on multiprocessor systems.
authorBruno Haible <bruno@clisp.org>
Thu, 27 Jul 2006 12:20:10 +0000 (12:20 +0000)
committerBruno Haible <bruno@clisp.org>
Tue, 23 Jun 2009 10:13:43 +0000 (12:13 +0200)
NEWS
gettext-tools/ChangeLog
gettext-tools/configure.ac
gettext-tools/m4/ChangeLog
gettext-tools/m4/Makefile.am
gettext-tools/src/ChangeLog
gettext-tools/src/Makefile.am
gettext-tools/src/msgmerge.c

diff --git a/NEWS b/NEWS
index 86cb0fbea02e91fe545973e4529e30586ae07886..7ab8639ff5e786f79f438b8fa319f764526675de 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,6 @@
+* msgmerge is faster now on CPUs with multiple execution units, if compiled
+  with GCC 4.2 or newer.
+\f
 Version 0.15 - July 2006
 
 * GUI program support:
index 0360515c061705f33ec2ef78b195cf2801d526ba..54cede050c292c47e25c045be540e225b5df8ca5 100644 (file)
@@ -1,3 +1,7 @@
+2006-07-23  Bruno Haible  <bruno@clisp.org>
+
+       * configure.ac: Invoke gt_OPENMP.
+
 2006-07-23  Bruno Haible  <bruno@clisp.org>
 
        * configure.ac: Invoke gl_LOCK and gl_TLS.
index 76553bba872f77d423f4e834d3047233069c78c4..b5d7c4a1e350525034facff7a3a330ab440b08f5 100644 (file)
@@ -127,6 +127,9 @@ if test "$MSGMERGE_LIBM" = "?"; then
 fi
 AC_SUBST([MSGMERGE_LIBM])
 
+dnl Test for compiler flags needed to support OpenMP.
+gt_OPENMP
+
 dnl Checks for header files.
 AC_CHECK_HEADERS(limits.h malloc.h pwd.h string.h unistd.h utime.h values.h)
 gl_STDARG_H
index 0ccf2871eb0972ad5d219cbb0df803c9bfe91561..82a874cf88f9f6f6daee06da570bd3f5b5309722 100644 (file)
@@ -1,3 +1,8 @@
+2006-07-23  Bruno Haible  <bruno@clisp.org>
+
+       * openmp.m4: New file.
+       * Makefile.am (EXTRA_DIST): Add it.
+
 2006-07-23  Bruno Haible  <bruno@clisp.org>
 
        * tls.m4: New file, from gnulib.
index 03f38194f72f7276702d50f04de06263c50ca6f6..a3cdf48bf0ba48626a8983f90c6f8ab14a8c2688 100644 (file)
@@ -78,6 +78,7 @@ memchr.m4 \
 minmax.m4 \
 mkdtemp.m4 \
 onceonly.m4 \
+openmp.m4 \
 pathmax.m4 \
 quote.m4 \
 quotearg.m4 \
index 554747626c63fb384aa6ceeb25615e9f67bc7b6b..5f1d3606c0db002359d3ff680a7c05f1055eaa98 100644 (file)
@@ -1,3 +1,16 @@
+2006-07-23  Bruno Haible  <bruno@clisp.org>
+
+       Exploit CPUs with multiple execution units.
+       * msgmerge.c: Include lock.h.
+       (struct definitions_ty): Add 'findex_init_lock' field.
+       (definitions_init): Initialize it.
+       (definitions_init_findex): Ensure findex is initialized by the
+       first thread who attempts so.
+       (match_domain): Split the main loop into two. Parallelize the first
+       loop using OpenMP pragmas.
+       * Makefile.am (msgmerge_CFLAGS): New variable.
+       (msgmerge_LDADD): Add OPENMP_CFLAGS.
+
 2006-07-25  Bruno Haible  <bruno@clisp.org>
 
        * Makefile.msvc: Remove file.
index c70bcf2ccaecb3d539e2d8b67ca38f9a471ba737..ea767673ac29837c1bb176dd37d11abee24008ea 100644 (file)
@@ -253,13 +253,16 @@ libgettextpo_la_SOURCES += ../woe32dll/gettextpo-exports.c
 libgettextpo_la_LDFLAGS += -Wl,--export-all-symbols
 endif
 
+# Compile-time flags for particular source files.
+msgmerge_CFLAGS = $(AM_CFLAGS) $(OPENMP_CFLAGS)
+
 # Link dependencies.
 # INTL_MACOSX_LIBS is needed because the programs depend on libintl.la
 # but libtool doesn't put -Wl,-framework options into .la files.
 # For msginit, it is also needed because of localename.c.
 msgcmp_LDADD = libgettextsrc.la @INTL_MACOSX_LIBS@
 msgfmt_LDADD = libgettextsrc.la @INTL_MACOSX_LIBS@
-msgmerge_LDADD = libgettextsrc.la @INTL_MACOSX_LIBS@ @MSGMERGE_LIBM@
+msgmerge_LDADD = libgettextsrc.la @INTL_MACOSX_LIBS@ @MSGMERGE_LIBM@ $(OPENMP_CFLAGS)
 msgunfmt_LDADD = libgettextsrc.la @INTL_MACOSX_LIBS@
 xgettext_LDADD = $(LIBUNINAME) libgettextsrc.la @INTL_MACOSX_LIBS@ @LTLIBEXPAT@
 msgattrib_LDADD = libgettextsrc.la @INTL_MACOSX_LIBS@
index 42391304b67d7df4248f31d6cd5599799d22fb4f..4fb86a6d3bf36f3208a9e7564721655ecd7acf36 100644 (file)
@@ -51,6 +51,7 @@
 #include "msgl-iconv.h"
 #include "msgl-equal.h"
 #include "msgl-fsearch.h"
+#include "lock.h"
 #include "plural-count.h"
 #include "backupfile.h"
 #include "copy-file.h"
@@ -594,6 +595,9 @@ struct definitions_ty
   /* A fuzzy index of the compendiums, for speed when doing fuzzy searches.
      Used only if use_fuzzy_matching is true and compendiums != NULL.  */
   message_fuzzy_index_ty *findex;
+  /* A once-only execution guard for the initialization of the fuzzy index.
+     Needed for OpenMP.  */
+  gl_lock_t findex_init_lock;
   /* The canonical encoding of the compendiums.  */
   const char *canon_charset;
 };
@@ -601,11 +605,14 @@ struct definitions_ty
 static inline void
 definitions_init (definitions_ty *definitions, const char *canon_charset)
 {
+  gl_lock_define (static, fresh_lock)
+
   definitions->lists = message_list_list_alloc ();
   message_list_list_append (definitions->lists, NULL);
   if (compendiums != NULL)
     message_list_list_append_list (definitions->lists, compendiums);
   definitions->findex = NULL;
+  memcpy (&definitions->findex_init_lock, &fresh_lock, sizeof (gl_lock_t));
   definitions->canon_charset = canon_charset;
 }
 
@@ -614,24 +621,30 @@ definitions_init (definitions_ty *definitions, const char *canon_charset)
 static inline void
 definitions_init_findex (definitions_ty *definitions)
 {
-  /* Combine all the compendium message lists into a single one.  Don't
-     bother checking for duplicates.  */
-  message_list_ty *all_compendium;
-  size_t i;
-
-  all_compendium = message_list_alloc (false);
-  for (i = 0; i < compendiums->nitems; i++)
+  /* Protect against concurrent execution.  */
+  gl_lock_lock (definitions->findex_init_lock);
+  if (definitions->findex == NULL)
     {
-      message_list_ty *mlp = compendiums->item[i];
-      size_t j;
+      /* Combine all the compendium message lists into a single one.  Don't
+        bother checking for duplicates.  */
+      message_list_ty *all_compendium;
+      size_t i;
 
-      for (j = 0; j < mlp->nitems; j++)
-       message_list_append (all_compendium, mlp->item[j]);
-    }
+      all_compendium = message_list_alloc (false);
+      for (i = 0; i < compendiums->nitems; i++)
+       {
+         message_list_ty *mlp = compendiums->item[i];
+         size_t j;
+
+         for (j = 0; j < mlp->nitems; j++)
+           message_list_append (all_compendium, mlp->item[j]);
+       }
 
-  /* Create the fuzzy index from it.  */
-  definitions->findex =
-    message_fuzzy_index_alloc (all_compendium, definitions->canon_charset);
+      /* Create the fuzzy index from it.  */
+      definitions->findex =
+       message_fuzzy_index_alloc (all_compendium, definitions->canon_charset);
+    }
+  gl_lock_unlock (definitions->findex_init_lock);
 }
 
 /* Return the current list of non-compendium messages.  */
@@ -1042,6 +1055,7 @@ match_domain (const char *fn1, const char *fn2,
   message_ty *header_entry;
   unsigned long int nplurals;
   char *untranslated_plural_msgstr;
+  struct search_result { message_ty *found; bool fuzzy; } *search_results;
   size_t j;
 
   header_entry =
@@ -1050,22 +1064,72 @@ match_domain (const char *fn1, const char *fn2,
   untranslated_plural_msgstr = (char *) xmalloc (nplurals);
   memset (untranslated_plural_msgstr, '\0', nplurals);
 
-  for (j = 0; j < refmlp->nitems; j++, (*processed)++)
-    {
-      message_ty *refmsg;
-      message_ty *defmsg;
-
-      /* Because merging can take a while we print something to signal
-        we are not dead.  */
-      if (!quiet && verbosity_level <= 1 && *processed % DOT_FREQUENCY == 0)
-       fputc ('.', stderr);
+  /* Most of the time is spent in definitions_search_fuzzy.
+     Perform it in a separate loop that can be parallelized by an OpenMP
+     capable compiler.  */
+  search_results =
+    (struct search_result *)
+    xmalloc (refmlp->nitems * sizeof (struct search_result));
+  {
+    long int nn = refmlp->nitems;
+    long int jj;
+
+    /* Tell the OpenMP capable compiler to distribute this loop across
+       several threads.  The schedule is dynamic, because for some messages
+       the loop body can be executed very quickly, whereas for others it takes
+       a long time.  */
+    #ifdef _OPENMP
+    # pragma omp parallel for schedule(dynamic)
+    #endif
+    for (jj = 0; jj < nn; jj++)
+      {
+       message_ty *refmsg = refmlp->item[jj];
+       message_ty *defmsg;
+
+       /* Because merging can take a while we print something to signal
+          we are not dead.  */
+       if (!quiet && verbosity_level <= 1 && *processed % DOT_FREQUENCY == 0)
+         fputc ('.', stderr);
+       #ifdef _OPENMP
+       # pragma omp atomic
+       #endif
+       (*processed)++;
+
+       /* See if it is in the other file.  */
+       defmsg =
+         definitions_search (definitions, refmsg->msgctxt, refmsg->msgid);
+       if (defmsg != NULL)
+         {
+           search_results[jj].found = defmsg;
+           search_results[jj].fuzzy = false;
+         }
+       else if (!is_header (refmsg)
+                /* If the message was not defined at all, try to find a very
+                   similar message, it could be a typo, or the suggestion may
+                   help.  */
+                && use_fuzzy_matching
+                && ((defmsg =
+                       definitions_search_fuzzy (definitions,
+                                                 refmsg->msgctxt,
+                                                 refmsg->msgid)) != NULL))
+         {
+           search_results[jj].found = defmsg;
+           search_results[jj].fuzzy = true;
+         }
+       else
+         search_results[jj].found = NULL;
+      }
+  }
 
-      refmsg = refmlp->item[j];
+  for (j = 0; j < refmlp->nitems; j++)
+    {
+      message_ty *refmsg = refmlp->item[j];
 
-      /* See if it is in the other file.  */
-      defmsg = definitions_search (definitions, refmsg->msgctxt, refmsg->msgid);
-      if (defmsg)
+      /* See if it is in the other file.
+        This used definitions_search.  */
+      if (search_results[j].found != NULL && !search_results[j].fuzzy)
        {
+         message_ty *defmsg = search_results[j].found;
          /* Merge the reference with the definition: take the #. and
             #: comments from the reference, take the # comments from
             the definition, take the msgstr from the definition.  Add
@@ -1083,13 +1147,11 @@ match_domain (const char *fn1, const char *fn2,
        {
          /* If the message was not defined at all, try to find a very
             similar message, it could be a typo, or the suggestion may
-            help.  */
-         if (use_fuzzy_matching
-             && ((defmsg =
-                    definitions_search_fuzzy (definitions,
-                                              refmsg->msgctxt,
-                                              refmsg->msgid)) != NULL))
+            help.  This search assumed use_fuzzy_matching and used
+            definitions_search_fuzzy.  */
+         if (search_results[j].found != NULL && search_results[j].fuzzy)
            {
+             message_ty *defmsg = search_results[j].found;
              message_ty *mp;
 
              if (verbosity_level > 1)
@@ -1158,6 +1220,8 @@ this message is used but not defined in %s"), fn1);
        }
     }
 
+  free (search_results);
+
   /* Now postprocess the problematic merges.  This is needed because we
      want the result to pass the "msgfmt -c -v" check.  */
   {