diff --git a/configure.ac b/configure.ac
index fcd56a5069d772bc3c7dc5e85053a3dccf7dd9c9..7cdb7088624a8e7415f844cd5316bf2e60a0abfc 100644
--- a/configure.ac
+++ b/configure.ac
@@ -90,6 +90,9 @@ if test "x$enable_mulredc_svoboda" = xyes; then
   GMP_DEFINE([MULREDC_SVOBODA], 1)
 fi
 
+AC_ARG_ENABLE([mmap],
+[AS_HELP_STRING([--enable-mmap], [use mmap() to load batch s files (EXPERIMENTAL. Expect bugs. Please report them.)])])
+
 AC_ARG_ENABLE([valgrind-client],
 [AS_HELP_STRING([--enable-valgrind-client], [enable Valgrind client check requests [[default=no]]])],[],[])
 if test "x$enable_valgrind" = xyes; then
@@ -379,7 +382,7 @@ AC_FUNC_ALLOCA
 m4_version_prereq([2.70],[AC_CHECK_INCLUDES_DEFAULT],[AC_HEADER_STDC])
 AC_PROG_EGREP
 
-AC_CHECK_HEADERS([math.h limits.h malloc.h strings.h sys/time.h unistd.h io.h signal.h fcntl.h])
+AC_CHECK_HEADERS([math.h limits.h malloc.h strings.h stdint.h inttypes.h endian.h sys/time.h unistd.h io.h signal.h fcntl.h])
 AC_CHECK_HEADERS([windows.h psapi.h], [], [],
    [[#ifdef HAVE_WINDOWS_H
      # include <windows.h>
@@ -387,6 +390,11 @@ AC_CHECK_HEADERS([windows.h psapi.h], [], [],
      ]])
 AC_CHECK_HEADERS([ctype.h sys/types.h sys/resource.h aio.h])
 
+if test "x$enable_mmap" = xyes; then
+  AC_CHECK_HEADERS([sys/mman.h])
+fi
+
+
 dnl Checks for library functions that are not in GMP
 AC_FUNC_STRTOD
 
@@ -405,6 +413,10 @@ AC_CHECK_FUNCS([isspace isdigit isxdigit], [], [AC_MSG_ERROR([required function
 AC_CHECK_FUNCS([time ctime], [], [AC_MSG_ERROR([required function missing])])
 AC_CHECK_FUNCS([gethostname gettimeofday getrusage memmove signal fcntl fileno setvbuf fallocate aio_read aio_init])
 
+if test "x$enable_mmap" = xyes; then
+  AC_CHECK_FUNCS([mmap])
+fi
+
 dnl Test for some Windows-specific functions that are available under MinGW
 dnl FIXME: which win32 library contains these functions?
 dnl AC_CHECK_FUNCS([GetCurrentProcess GetProcessTimes])
diff --git a/ecm-ecm.h b/ecm-ecm.h
index c44126c1d6412dbc35fc064b160eae5b06a81346..54e26918fd638700d71f36d950b744ee5b85010f 100644
--- a/ecm-ecm.h
+++ b/ecm-ecm.h
@@ -31,6 +31,7 @@ http://www.gnu.org/licenses/ or write to the Free Software Foundation, Inc.,
 #define ASSERT(expr)   do {} while (0)
 #endif
 
+#include <stdint.h>
 #include "ecm.h"
 
 /* Structure for candidate usage.  This is much more powerful than using a
@@ -121,8 +122,9 @@ int  read_resumefile_line (int *, mpz_t, mpz_t, mpcandi_t *,
 int write_resumefile (char *, int, ecm_params params,
 		      mpcandi_t *, const mpz_t, const mpz_t, const mpz_t,
 		      const char *);
-int write_s_in_file (char *, mpz_t);
-int read_s_from_file (mpz_t, char *, double); 
+int write_s_in_file (const char *, mpz_t, int, uint64_t);
+int read_s_from_file (mpz_t, const char *, int, double); 
+void free_s_data(int, mpz_t);
 
 /* main.c */
 int kbnc_z (double *k, unsigned long *b, unsigned long *n, signed long *c,
diff --git a/main.c b/main.c
index 0a7d62e1f0bd3032e4af7b9de7e8c608f2abf1fb..5613bd3e12b959d7be5691bb886eb80e04df6e28 100644
--- a/main.c
+++ b/main.c
@@ -135,6 +135,10 @@ usage (void)
 
     printf ("  -bsaves file With -param 1-3, save stage 1 exponent in file.\n");
     printf ("  -bloads file With -param 1-3, load stage 1 exponent from file.\n");
+#ifdef HAVE_MMAP
+    printf ("  -bsavems file With -param 1-3, save stage 1 exponent in file (file format for -bloadms).\n");
+    printf ("  -bloadms file With -param 1-3, mmap stage 1 exponent from file.\n");
+#endif
 #ifdef WITH_GPU
     printf ("  -gpu         Use CGBN for computations stage 1.\n");
     printf ("  -gpudevice n Use device n to execute GPU code (by default, "
@@ -398,6 +402,8 @@ main (int argc, char *argv[])
   int param = ECM_PARAM_DEFAULT; /* automatic choice */
   char *savefile_s = NULL;
   char *loadfile_s = NULL;
+  int save_s_mmap = 0,     /* Do we write s in mmap file format? */
+      load_s_mmap = 0;     /* Do we mmap s data from the file? */
 #ifdef HAVE_GWNUM
   double gw_k = 0.0;       /* set default values for gwnum poly k*b^n+c */
   unsigned long gw_b = 0;  /* set default values for gwnum poly k*b^n+c */
@@ -540,16 +546,50 @@ main (int argc, char *argv[])
         }
       else if ((argc > 2) && (strcmp (argv[1], "-bsaves") == 0))
         {
+          if (savefile_s != NULL) {
+              fprintf (stderr, "Multiple -bsaves or -bsavems options were given\n");
+              exit (EXIT_FAILURE);
+          }
           savefile_s = argv[2];
+          save_s_mmap = 0;
           argv += 2;
           argc -= 2;
         }
       else if ((argc > 2) && (strcmp (argv[1], "-bloads") == 0))
         {
+          if (loadfile_s != NULL) {
+              fprintf (stderr, "Multiple -bloads or -bloadms options were given\n");
+              exit (EXIT_FAILURE);
+          }
           loadfile_s = argv[2];
+          load_s_mmap = 0;
           argv += 2;
           argc -= 2;
         }
+#ifdef HAVE_MMAP
+      else if ((argc > 2) && (strcmp (argv[1], "-bsavems") == 0))
+        {
+          if (savefile_s != NULL) {
+              fprintf (stderr, "Multiple -bsaves or -bsavems options were given\n");
+              exit (EXIT_FAILURE);
+          }
+          savefile_s = argv[2];
+          save_s_mmap = 1;
+          argv += 2;
+          argc -= 2;
+        }
+      else if ((argc > 2) && (strcmp (argv[1], "-bloadms") == 0))
+        {
+          if (loadfile_s != NULL) {
+              fprintf (stderr, "Multiple -bloads or -bloadms options were given\n");
+              exit (EXIT_FAILURE);
+          }
+          loadfile_s = argv[2];
+          load_s_mmap = 1;
+          argv += 2;
+          argc -= 2;
+        }
+#endif
       else if (strcmp (argv[1], "-h") == 0 || strcmp (argv[1], "--help") == 0)
         {
           usage ();
@@ -1389,7 +1429,7 @@ main (int argc, char *argv[])
               exit (EXIT_FAILURE);
             }
           params->batch_last_B1_used = B1;
-          if (read_s_from_file (params->batch_s, loadfile_s, B1))
+          if (read_s_from_file (params->batch_s, loadfile_s, load_s_mmap, B1))
             {
               fprintf (stderr, "Error while reading s from file\n");
               exit (EXIT_FAILURE);
@@ -1595,7 +1635,12 @@ main (int argc, char *argv[])
       /* Save the batch exponent s if requested */
       if (savefile_s != NULL)
         {
-          int ret = write_s_in_file (savefile_s, params->batch_s);
+          int ret = write_s_in_file (savefile_s, params->batch_s, save_s_mmap,
+                                     (uint64_t) B1);
+          if (ret == 0) {
+              fprintf(stderr, "Error writing s to file %s\n", savefile_s);
+              exit(EXIT_FAILURE);
+          }
           if (verbose >= OUTPUT_VERBOSE && ret > 0)
               printf ("Saved batch product (of %u bytes) in %s\n", ret, 
                       savefile_s);
@@ -1634,6 +1679,8 @@ main (int argc, char *argv[])
   mpz_clear (sigma);
 
   mpgocandi_t_free (&go);
+  if (loadfile_s != NULL)
+    free_s_data (load_s_mmap, params->batch_s);
   ecm_clear (params);
 
   /* exit 0 if a factor was found for the last input, except if we exit due
diff --git a/resume.c b/resume.c
index 0eac292146020976861c3744d69353b762f5dfe3..986fffa7b71ed22a813e41cdf6f6f7855217d08d 100644
--- a/resume.c
+++ b/resume.c
@@ -23,11 +23,18 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
 */
 
+#include "config.h"
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <time.h>
 #include <math.h>
 #include <string.h>
+#include <endian.h>
+#include <inttypes.h>
+#ifdef HAVE_MMAP
+#include <sys/mman.h>
+#endif
 #if !defined (_MSC_VER)
 #include <unistd.h>
 #endif
@@ -47,6 +54,11 @@ SOFTWARE.
    Returns the number of matching characters that were read. 
 */
 
+#define S_FILE_MAGIC 0xe2ad5e97UL
+/* This is a prime p with 2 a generator of (Z/pZ)* */
+#define S_FILE_CHECKSUM_MODULUS 4294967291UL
+
+
 static int 
 facceptstr (FILE *fd, char *s)
 {
@@ -654,12 +666,74 @@ write_resumefile (char *fn, int method, ecm_params params,
   return 0;
 }
 
+typedef struct {
+  uint32_t magic;
+  unsigned char version,
+                mmapped, /* mmapped data uses host endianness */
+                is_le,   /* Is this machine little_endian? */
+                dummy;   /* Always 0 for word alignment */
+  uint32_t checksum;     /* s % S_FILE_CHECKSUM_MODULUS */
+  uint64_t B1;
+} s_file_header_t;
+
+/* Wrapper around fread(). Prints error message on error. If close_on_error
+ * is non-zero, also closes the stream.
+ * Returns 0 on success and 1 on error.
+ */
+int
+fread_perror(void * restrict data,
+             size_t size, size_t nmemb,
+             FILE *restrict stream,
+             const char *restrict fn,
+             const char *restrict data_description,
+             int close_on_error)
+{
+  size_t members_read = fread(data, size, nmemb, stream);
+  if (members_read != nmemb) {
+      fprintf(stderr, "Could not read %s from file %s\n", data_description, fn);
+      perror("");
+      if (close_on_error)
+        fclose(stream);
+      return 1;
+  }
+  return 0;
+}
+
+
+/* Wrapper around fwrite(). Prints error message on error. If close_on_error
+ * is non-zero, also closes the stream.
+ * Returns 0 on success and 1 on error.
+ */
+int
+fwrite_perror(const void * restrict data,
+              size_t size, size_t nmemb,
+              FILE *restrict stream,
+              const char *restrict fn,
+              const char *restrict data_description,
+              int close_on_error)
+{
+  size_t members_written = fwrite(data, size, nmemb, stream);
+  if (members_written != nmemb) {
+      fprintf(stderr, "Could not write %s to file %s\n", data_description, fn);
+      perror("");
+      if (close_on_error)
+        fclose(stream);
+      return 1;
+  }
+  return 0;
+}
+
+size_t
+compute_filesize(const size_t nr_limbs)
+{
+  return sizeof(s_file_header_t) + sizeof(int) + nr_limbs * sizeof(mp_limb_t);
+}
 
 /* For the batch mode */
 /* Write the batch exponent s in a file */
 /* Return the number of bytes written */
 int
-write_s_in_file (char *fn, mpz_t s)
+write_s_in_file (const char *fn, mpz_t s, int want_mmap, uint64_t B1)
 {
   FILE *file;
   int ret = 0;
@@ -671,6 +745,20 @@ write_s_in_file (char *fn, mpz_t s)
       exit (EXIT_FAILURE);
     }
 #endif
+#ifndef HAVE_MMAP
+  if (want_mmap != 0) {
+    fprintf(stderr, "Refusing to write batch s data to file %s in mmap() "
+            "format because mmap() is not supported on this system. Please "
+            "check the the configure log.", fn);
+    return 0;
+  }
+#endif
+
+  if (mpz_sgn(s) <= 0) {
+    fprintf(stderr, "Error, s is %s. This should never happen.",
+            mpz_sgn(s) == 0 ? "zero" : "negative");
+    return 0;
+  }
   
   file = fopen (fn, "wb");
   if (file == NULL)
@@ -679,8 +767,26 @@ write_s_in_file (char *fn, mpz_t s)
       return 0;
     }
   
-  ret = mpz_out_raw (file, s);
+  const uint32_t magic = htole32(S_FILE_MAGIC);
+  const unsigned char mmapped = want_mmap ? 1 : 0;
+  const unsigned char is_le = htole32(1) == 1 ? 1 : 0;
+  const uint32_t checksum =  mpz_fdiv_ui(s, S_FILE_CHECKSUM_MODULUS);
+  const uint32_t B1le = htole64(B1);
+  const s_file_header_t header = {magic, 1, mmapped, is_le, 0, checksum, B1le};
+  if (fwrite_perror(&header, sizeof(s_file_header_t), 1, file, fn, "header", 1))
+      return 0;
   
+  if (want_mmap) {
+    if (fwrite_perror(&s->_mp_size, sizeof(int), 1, file, fn, "size", 1))
+      return 0;
+    if (fwrite_perror(s->_mp_d, sizeof(mp_limb_t), s->_mp_size, file, fn, "limbs", 1))
+      return 0;
+    ret = compute_filesize(s->_mp_size);
+  } else {
+    ret = mpz_out_raw (file, s);
+  }
+  /* gmp_printf("Wrote %Zx to %s\n", s, fn); */
+
   fclose (file);
   return ret;
 }
@@ -688,7 +794,7 @@ write_s_in_file (char *fn, mpz_t s)
 /* For the batch mode */
 /* read the batch exponent s from a file */
 int
-read_s_from_file (mpz_t s, char *fn, double B1) 
+read_s_from_file (mpz_t s, const char *fn, int want_mmap, double B1)
 {
   FILE *file;
   mpz_t tmp, tmp2;
@@ -703,22 +809,117 @@ read_s_from_file (mpz_t s, char *fn, double B1)
     }
 #endif
   
+#ifndef HAVE_MMAP
+  if (want_mmap != 0) {
+    fprintf(stderr, "Cannot mmap() batch s data from file %s because mmap() "
+            "is not supported on this system. Please check the the configure "
+            "log.", fn);
+  }
+#endif
+
   file = fopen (fn, "rb");
   if (file == NULL)
     {
       fprintf (stderr, "Could not open file %s for reading\n", fn);
       return 1;
     }
- 
-  ret = mpz_inp_raw (s, file);
-  if (ret == 0)
-    {
-      fprintf (stderr, "read_s_from_file: 0 bytes read from %s\n", fn);
+
+  const unsigned char host_is_le = htole32(1) == 1 ? 1 : 0;
+  s_file_header_t header;
+  if (fread_perror(&header, sizeof(s_file_header_t), 1, file, fn, "header", 1))
+      return 1;
+  header.magic = le32toh(header.magic);
+  header.B1 = le64toh(header.B1);
+  if (header.magic != S_FILE_MAGIC) {
+      fprintf(stderr, "Wrong magic number in file %s; is this a batch "
+              "product save file? Note that the file format changed after "
+              "version 7.0; if this is an old file, please re-create it.\n",
+              fn);
+      fclose(file);
+      return 1;
+  }
+  if (header.mmapped != want_mmap) {
+      fprintf(stderr, "File %s uses %smmapped data but we want to read "
+              "%smmaped data.\n",
+              fn, header.mmapped ? "" : "non-", want_mmap ? "" : "non-");
+      fclose(file);
       return 1;
+  }
+  if (header.B1 != (unsigned long) B1) {
+      fprintf(stderr, "Wrong B1 value in file %s; it has stored data for B1 = %"
+              PRIu64 " but this run uses B1 = %" PRIu64 "\n",
+              fn, header.B1, (uint64_t) B1);
+      fclose(file);
+      return 1;
+  }
+
+#ifdef HAVE_MMAP
+  if (want_mmap != 0) {
+    /* printf("Using mmap() for s data\n"); */
+
+    if (header.is_le != host_is_le) {
+        fprintf(stderr, "Cannot mmap because file %s has wrong endianness for"
+                "this system. File uses %s, but host is %s\n",
+                fn, header.is_le ? "little-endian" : "big-endian",
+                host_is_le ? "little-endian" : "big-endian");
+        fclose(file);
+        return 1;
+    }
+
+    int fd = fileno(file);
+    if (fd == -1) {
+        fprintf (stderr, "Could not get file descriptor for file %s\n", fn);
+        fclose(file);
+        return 1;
+    }
+    
+    int nr_limbs;
+    if (fread_perror(&nr_limbs, sizeof(int), 1, file, fn, "size", 1))
+        return 1;
+    if (nr_limbs < 0) {
+        fprintf(stderr, "Error, nr_limbs in file %s is negative (%d). "
+                "This should never happen.", fn, nr_limbs);
     }
 
+    size_t filesize = compute_filesize(nr_limbs);
+    const int prot = PROT_READ;
+    const int flags = MAP_SHARED;
+    const off_t offset = 0;
+    char *s_data = mmap(NULL, filesize, prot, flags, fd, offset);
+    if (s_data == MAP_FAILED) {
+        fprintf (stderr, "mmap(NULL, length=%zu, prot=%d, flags=%d, fd=%d, offset=%zu) failed for file %s\n", 
+                (size_t) filesize, prot, flags, fd, (size_t) offset, fn);
+        perror("");
+        fclose(file);
+        return 1;
+    }
+    mpz_clear (s);
+    s->_mp_size = nr_limbs;
+    s->_mp_alloc = s->_mp_size;
+    s->_mp_d = (mp_limb_t *) (s_data + sizeof(s_file_header_t) + sizeof(int));
+  }
+#endif
+
+  if (want_mmap == 0) {
+    ret = mpz_inp_raw (s, file);
+    if (ret == 0)
+      {
+        fprintf (stderr, "read_s_from_file: 0 bytes read from %s\n", fn);
+        fclose(file);
+        return 1;
+      }
+  }
   fclose (file);
-          
+
+  /* gmp_printf("Read %Zx from %s\n", s, fn); */
+  const uint32_t checksum =  mpz_fdiv_ui(s, S_FILE_CHECKSUM_MODULUS);
+  if (checksum != header.checksum) {
+     fprintf(stderr, "Checksum in file %s is wrong. File header has %" PRIu32
+       " but checksum over file data is %" PRIu32 "\n",
+       fn, header.checksum, checksum);
+    return 1;
+  }
+
   /* Some elementaty check that it correspond to the actual B1 */
   mpz_init (tmp);
   mpz_init (tmp2);
@@ -760,3 +961,17 @@ read_s_from_file (mpz_t s, char *fn, double B1)
   return 0;
 }
 
+void
+free_s_data(int want_mmap, mpz_t s)
+{
+#ifdef HAVE_MMAP
+  const int nr_limbs = s->_mp_size;
+  const size_t filesize = sizeof(s_file_header_t) +
+                          sizeof(int) +
+                          (size_t)nr_limbs * sizeof(mp_limb_t);
+  if (want_mmap) {
+    munmap(s->_mp_d, filesize);
+    mpz_init(s);
+  }
+#endif
+}