* [PATCH] Move slow path out of 'scm_get_byte_or_eof' et al
@ 2013-04-02 9:59 Mark H Weaver
2013-04-02 12:43 ` Mike Gran
0 siblings, 1 reply; 6+ messages in thread
From: Mark H Weaver @ 2013-04-02 9:59 UTC (permalink / raw)
To: guile-devel
[-- Attachment #1: Type: text/plain, Size: 62 bytes --]
Andy suggested this. Okay to push to stable-2.0?
Mark
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: [PATCH] Move slow path out of 'scm_get_byte_or_eof' et al --]
[-- Type: text/x-diff, Size: 4233 bytes --]
From 0b70eba61ee26654f442199e7cc49838c5b0030e Mon Sep 17 00:00:00 2001
From: Mark H Weaver <mhw@netris.org>
Date: Tue, 2 Apr 2013 05:33:24 -0400
Subject: [PATCH] Move slow path out of 'scm_get_byte_or_eof' et al.
Suggested by Andy Wingo.
* libguile/inline.h (scm_get_byte_or_eof, scm_peek_byte_or_eof): Keep
only the fast path here, with fallback to 'scm_i_get_byte_or_eof' and
'scm_i_peek_byte_or_eof'.
* libguile/ports.c (scm_i_get_byte_or_eof, scm_i_peek_byte_or_eof): New
internal functions.
* libguile/ports.h (scm_i_get_byte_or_eof, scm_i_peek_byte_or_eof): Add
prototypes.
---
libguile/inline.h | 44 ++++++++++----------------------------------
libguile/ports.c | 41 +++++++++++++++++++++++++++++++++++++++++
libguile/ports.h | 2 ++
3 files changed, 53 insertions(+), 34 deletions(-)
diff --git a/libguile/inline.h b/libguile/inline.h
index 88ba7f7..17d8a0c 100644
--- a/libguile/inline.h
+++ b/libguile/inline.h
@@ -96,50 +96,26 @@ scm_is_string (SCM x)
SCM_INLINE_IMPLEMENTATION int
scm_get_byte_or_eof (SCM port)
{
- int c;
scm_t_port *pt = SCM_PTAB_ENTRY (port);
- if (pt->rw_active == SCM_PORT_WRITE)
- /* may be marginally faster than calling scm_flush. */
- scm_ptobs[SCM_PTOBNUM (port)].flush (port);
-
- if (pt->rw_random)
- pt->rw_active = SCM_PORT_READ;
-
- if (pt->read_pos >= pt->read_end)
- {
- if (SCM_UNLIKELY (scm_fill_input (port) == EOF))
- return EOF;
- }
-
- c = *(pt->read_pos++);
-
- return c;
+ if (SCM_LIKELY ((pt->rw_active == SCM_PORT_READ || !pt->rw_random)
+ && pt->read_pos < pt->read_end))
+ return *pt->read_pos++;
+ else
+ return scm_i_get_byte_or_eof (port);
}
/* Like `scm_get_byte_or_eof' but does not change PORT's `read_pos'. */
SCM_INLINE_IMPLEMENTATION int
scm_peek_byte_or_eof (SCM port)
{
- int c;
scm_t_port *pt = SCM_PTAB_ENTRY (port);
- if (pt->rw_active == SCM_PORT_WRITE)
- /* may be marginally faster than calling scm_flush. */
- scm_ptobs[SCM_PTOBNUM (port)].flush (port);
-
- if (pt->rw_random)
- pt->rw_active = SCM_PORT_READ;
-
- if (pt->read_pos >= pt->read_end)
- {
- if (SCM_UNLIKELY (scm_fill_input (port) == EOF))
- return EOF;
- }
-
- c = *pt->read_pos;
-
- return c;
+ if (SCM_LIKELY ((pt->rw_active == SCM_PORT_READ || !pt->rw_random)
+ && pt->read_pos < pt->read_end))
+ return *pt->read_pos;
+ else
+ return scm_i_peek_byte_or_eof (port);
}
SCM_INLINE_IMPLEMENTATION void
diff --git a/libguile/ports.c b/libguile/ports.c
index becdbed..76ec83f 100644
--- a/libguile/ports.c
+++ b/libguile/ports.c
@@ -1439,6 +1439,47 @@ scm_fill_input (SCM port)
return scm_ptobs[SCM_PTOBNUM (port)].fill_input (port);
}
+/* Slow-path fallback for 'scm_get_byte_or_eof' in inline.h */
+int
+scm_i_get_byte_or_eof (SCM port)
+{
+ scm_t_port *pt = SCM_PTAB_ENTRY (port);
+
+ if (pt->rw_active == SCM_PORT_WRITE)
+ scm_flush (port);
+
+ if (pt->rw_random)
+ pt->rw_active = SCM_PORT_READ;
+
+ if (pt->read_pos >= pt->read_end)
+ {
+ if (SCM_UNLIKELY (scm_fill_input (port) == EOF))
+ return EOF;
+ }
+
+ return *pt->read_pos++;
+}
+
+/* Slow-path fallback for 'scm_peek_byte_or_eof' in inline.h */
+int
+scm_i_peek_byte_or_eof (SCM port)
+{
+ scm_t_port *pt = SCM_PTAB_ENTRY (port);
+
+ if (pt->rw_active == SCM_PORT_WRITE)
+ scm_flush (port);
+
+ if (pt->rw_random)
+ pt->rw_active = SCM_PORT_READ;
+
+ if (pt->read_pos >= pt->read_end)
+ {
+ if (SCM_UNLIKELY (scm_fill_input (port) == EOF))
+ return EOF;
+ }
+
+ return *pt->read_pos;
+}
/* scm_lfwrite
*
diff --git a/libguile/ports.h b/libguile/ports.h
index 53d5081..54bf595 100644
--- a/libguile/ports.h
+++ b/libguile/ports.h
@@ -328,6 +328,8 @@ scm_i_default_port_conversion_handler (void);
/* Use HANDLER as the default conversion strategy for future ports. */
SCM_INTERNAL void
scm_i_set_default_port_conversion_handler (scm_t_string_failed_conversion_handler);
+SCM_INTERNAL int scm_i_get_byte_or_eof (SCM port);
+SCM_INTERNAL int scm_i_peek_byte_or_eof (SCM port);
SCM_API SCM scm_port_conversion_strategy (SCM port);
SCM_API SCM scm_set_port_conversion_strategy_x (SCM port, SCM behavior);
--
1.7.10.4
^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH] Move slow path out of 'scm_get_byte_or_eof' et al
2013-04-02 9:59 [PATCH] Move slow path out of 'scm_get_byte_or_eof' et al Mark H Weaver
@ 2013-04-02 12:43 ` Mike Gran
2013-04-02 15:59 ` Ludovic Courtès
2013-04-02 17:54 ` Mark H Weaver
0 siblings, 2 replies; 6+ messages in thread
From: Mike Gran @ 2013-04-02 12:43 UTC (permalink / raw)
To: Mark H Weaver, guile-devel@gnu.org
> From: Mark H Weaver <mhw@netris.org>
>
>Andy suggested this. Okay to push to stable-2.0?
Hi Mark,
I can't imagine any possible way that this patch could make
performance worse, but, it is in the very core of the ports.
I don't think you can get away without at least a bit of
profiling.
-Mike
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH] Move slow path out of 'scm_get_byte_or_eof' et al
2013-04-02 12:43 ` Mike Gran
@ 2013-04-02 15:59 ` Ludovic Courtès
2013-04-02 17:54 ` Mark H Weaver
1 sibling, 0 replies; 6+ messages in thread
From: Ludovic Courtès @ 2013-04-02 15:59 UTC (permalink / raw)
To: guile-devel
Mike Gran <spk121@yahoo.com> skribis:
>> From: Mark H Weaver <mhw@netris.org>
>>
>>Andy suggested this. Okay to push to stable-2.0?
>
> Hi Mark,
>
> I can't imagine any possible way that this patch could make
> performance worse, but, it is in the very core of the ports.
>
> I don't think you can get away without at least a bit of
> profiling.
Yes, it’s most likely the Right Thing, but figures to confirm this would
be nice.
Ludo’.
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH] Move slow path out of 'scm_get_byte_or_eof' et al
2013-04-02 12:43 ` Mike Gran
2013-04-02 15:59 ` Ludovic Courtès
@ 2013-04-02 17:54 ` Mark H Weaver
2013-04-02 18:10 ` Mike Gran
2013-04-03 8:12 ` Ludovic Courtès
1 sibling, 2 replies; 6+ messages in thread
From: Mark H Weaver @ 2013-04-02 17:54 UTC (permalink / raw)
To: Mike Gran; +Cc: guile-devel@gnu.org
[-- Attachment #1: Type: text/plain, Size: 2957 bytes --]
Mike Gran <spk121@yahoo.com> writes:
> I can't imagine any possible way that this patch could make
> performance worse, but, it is in the very core of the ports.
>
> I don't think you can get away without at least a bit of
> profiling.
Fair enough. First of all, this patch reduces the size of the built
libguile.so by about 42 kilobytes, and libguile.a by about 96 kilobytes.
As for performance: the updated patches (attached below) slows things
down by about one quarter of one percent on my machine. The specific
benchmark I did was to call 'read-string' on an 11 megabyte ASCII text
file, with the port-encoding set to UTF-8. In this case, all the reads
are done using 'scm_get_byte_or_eof' (called from 'get_utf8_codepoint').
So I think this is an acceptable cost for such a great reduction in code
size. What do you think?
Thanks for nudging me to do the measurement. To be honest, the original
patch I posted slowed things down by 4.5%, which I found extremely
surprising. I fixed it by adding an internal 'static' version of
'scm_fill_input'. So for those who doubt the cost of function calls
though the shared library PLT (which, within a shared library, is
*every* function call to a non-static function), let this be a lesson.
That's the only thing that changed here, and it made more than a 4%
difference, even though the procedure call in question was done only
once per 4 kilobyte buffer!
Here's the raw data:
before:
12767060 libguile-2.0.a
6999271 libguile-2.0.so.22.6.0
after:
12671162 libguile-2.0.a (96 kbytes smaller)
6956989 libguile-2.0.so.22.6.0 (42 kbytes smaller)
test file (the contents of 'psyntax.scm' repeated many times):
-rw-r--r-- 1 mhw mhw 11503780 Apr 2 12:46 /home/mhw/BIG-FILE
before:
scheme@(guile-user)> ,time (define s (call-with-input-file "/home/mhw/BIG-FILE" read-string))
;; 2.953214s real time, 2.951423s run time. 0.027767s spent in GC.
scheme@(guile-user)> ,time (define s (call-with-input-file "/home/mhw/BIG-FILE" read-string))
;; 2.942170s real time, 2.940581s run time. 0.011873s spent in GC.
scheme@(guile-user)> ,time (define s (call-with-input-file "/home/mhw/BIG-FILE" read-string))
;; 2.954309s real time, 2.950164s run time. 0.011952s spent in GC.
avg: 2.9473893333333336
after:
scheme@(guile-user)> ,time (define s (call-with-input-file "/home/mhw/BIG-FILE" read-string))
;; 2.964777s real time, 2.957597s run time. 0.012116s spent in GC.
scheme@(guile-user)> ,time (define s (call-with-input-file "/home/mhw/BIG-FILE" read-string))
;; 2.971099s real time, 2.968656s run time. 0.012020s spent in GC.
scheme@(guile-user)> ,time (define s (call-with-input-file "/home/mhw/BIG-FILE" read-string))
;; 2.942377s real time, 2.940015s run time. 0.012085s spent in GC.
avg: 2.9554226666666663
0.27% slower
I've attached the new patches. Okay to push now?
Thanks,
Mark
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: [PATCH 1/2] Add a static version of 'scm_fill_input' to ports.c --]
[-- Type: text/x-diff, Size: 3593 bytes --]
From 187fa0b9e7ff9b2d6204517a9daa9009245c7511 Mon Sep 17 00:00:00 2001
From: Mark H Weaver <mhw@netris.org>
Date: Tue, 2 Apr 2013 13:33:14 -0400
Subject: [PATCH 1/2] Add a static version of 'scm_fill_input' to ports.c.
* libguile/ports.c (scm_i_fill_input): New static function, containing
the code that was previously in 'scm_fill_input'.
(scm_fill_input): Simply call 'scm_i_fill_input'.
(scm_c_read): Use 'scm_i_fill_input'.
---
libguile/ports.c | 24 +++++++++++++++---------
1 file changed, 15 insertions(+), 9 deletions(-)
diff --git a/libguile/ports.c b/libguile/ports.c
index becdbed..13a993e 100644
--- a/libguile/ports.c
+++ b/libguile/ports.c
@@ -1419,8 +1419,8 @@ scm_getc (SCM port)
/* this should only be called when the read buffer is empty. it
tries to refill the read buffer. it returns the first char from
the port, which is either EOF or *(pt->read_pos). */
-int
-scm_fill_input (SCM port)
+static int
+scm_i_fill_input (SCM port)
{
scm_t_port *pt = SCM_PTAB_ENTRY (port);
@@ -1439,6 +1439,12 @@ scm_fill_input (SCM port)
return scm_ptobs[SCM_PTOBNUM (port)].fill_input (port);
}
+int
+scm_fill_input (SCM port)
+{
+ return scm_i_fill_input (port);
+}
+
/* scm_lfwrite
*
@@ -1547,8 +1553,8 @@ scm_c_read (SCM port, void *buffer, size_t size)
if (size == 0)
return n_read;
- /* Now we will call scm_fill_input repeatedly until we have read the
- requested number of bytes. (Note that a single scm_fill_input
+ /* Now we will call scm_i_fill_input repeatedly until we have read the
+ requested number of bytes. (Note that a single scm_i_fill_input
call does not guarantee to fill the whole of the port's read
buffer.) */
if (pt->read_buf_size <= 1 && pt->encoding == NULL)
@@ -1556,12 +1562,12 @@ scm_c_read (SCM port, void *buffer, size_t size)
/* The port that we are reading from is unbuffered - i.e. does
not have its own persistent buffer - but we have a buffer,
provided by our caller, that is the right size for the data
- that is wanted. For the following scm_fill_input calls,
+ that is wanted. For the following scm_i_fill_input calls,
therefore, we use the buffer in hand as the port's read
buffer.
We need to make sure that the port's normal (1 byte) buffer
- is reinstated in case one of the scm_fill_input () calls
+ is reinstated in case one of the scm_i_fill_input () calls
throws an exception; we use the scm_dynwind_* API to achieve
that.
@@ -1578,9 +1584,9 @@ scm_c_read (SCM port, void *buffer, size_t size)
scm_dynwind_rewind_handler (swap_buffer, &psb, SCM_F_WIND_EXPLICITLY);
scm_dynwind_unwind_handler (swap_buffer, &psb, SCM_F_WIND_EXPLICITLY);
- /* Call scm_fill_input until we have all the bytes that we need,
+ /* Call scm_i_fill_input until we have all the bytes that we need,
or we hit EOF. */
- while (pt->read_buf_size && (scm_fill_input (port) != EOF))
+ while (pt->read_buf_size && (scm_i_fill_input (port) != EOF))
{
pt->read_buf_size -= (pt->read_end - pt->read_pos);
pt->read_pos = pt->read_buf = pt->read_end;
@@ -1604,7 +1610,7 @@ scm_c_read (SCM port, void *buffer, size_t size)
that a custom port implementation's entry points (in
particular, fill_input) can rely on the buffer always being
the same as they first set up. */
- while (size && (scm_fill_input (port) != EOF))
+ while (size && (scm_i_fill_input (port) != EOF))
{
n_available = min (size, pt->read_end - pt->read_pos);
memcpy (buffer, pt->read_pos, n_available);
--
1.7.10.4
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #3: [PATCH 2/2] Move slow path out of 'scm_get_byte_or_eof' et al --]
[-- Type: text/x-diff, Size: 4220 bytes --]
From 8a2b596579185cd0f4d35da478f447e529d81a80 Mon Sep 17 00:00:00 2001
From: Mark H Weaver <mhw@netris.org>
Date: Tue, 2 Apr 2013 05:33:24 -0400
Subject: [PATCH 2/2] Move slow path out of 'scm_get_byte_or_eof' et al.
Suggested by Andy Wingo.
* libguile/inline.h (scm_get_byte_or_eof, scm_peek_byte_or_eof): Keep
only the fast path here, with fallback to 'scm_i_get_byte_or_eof' and
'scm_i_peek_byte_or_eof'.
* libguile/ports.c (scm_i_get_byte_or_eof, scm_i_peek_byte_or_eof):
New internal functions.
* libguile/ports.h (scm_i_get_byte_or_eof, scm_i_peek_byte_or_eof): Add
prototypes.
---
libguile/inline.h | 44 ++++++++++----------------------------------
libguile/ports.c | 42 ++++++++++++++++++++++++++++++++++++++++++
libguile/ports.h | 2 ++
3 files changed, 54 insertions(+), 34 deletions(-)
diff --git a/libguile/inline.h b/libguile/inline.h
index 88ba7f7..17d8a0c 100644
--- a/libguile/inline.h
+++ b/libguile/inline.h
@@ -96,50 +96,26 @@ scm_is_string (SCM x)
SCM_INLINE_IMPLEMENTATION int
scm_get_byte_or_eof (SCM port)
{
- int c;
scm_t_port *pt = SCM_PTAB_ENTRY (port);
- if (pt->rw_active == SCM_PORT_WRITE)
- /* may be marginally faster than calling scm_flush. */
- scm_ptobs[SCM_PTOBNUM (port)].flush (port);
-
- if (pt->rw_random)
- pt->rw_active = SCM_PORT_READ;
-
- if (pt->read_pos >= pt->read_end)
- {
- if (SCM_UNLIKELY (scm_fill_input (port) == EOF))
- return EOF;
- }
-
- c = *(pt->read_pos++);
-
- return c;
+ if (SCM_LIKELY ((pt->rw_active == SCM_PORT_READ || !pt->rw_random)
+ && pt->read_pos < pt->read_end))
+ return *pt->read_pos++;
+ else
+ return scm_i_get_byte_or_eof (port);
}
/* Like `scm_get_byte_or_eof' but does not change PORT's `read_pos'. */
SCM_INLINE_IMPLEMENTATION int
scm_peek_byte_or_eof (SCM port)
{
- int c;
scm_t_port *pt = SCM_PTAB_ENTRY (port);
- if (pt->rw_active == SCM_PORT_WRITE)
- /* may be marginally faster than calling scm_flush. */
- scm_ptobs[SCM_PTOBNUM (port)].flush (port);
-
- if (pt->rw_random)
- pt->rw_active = SCM_PORT_READ;
-
- if (pt->read_pos >= pt->read_end)
- {
- if (SCM_UNLIKELY (scm_fill_input (port) == EOF))
- return EOF;
- }
-
- c = *pt->read_pos;
-
- return c;
+ if (SCM_LIKELY ((pt->rw_active == SCM_PORT_READ || !pt->rw_random)
+ && pt->read_pos < pt->read_end))
+ return *pt->read_pos;
+ else
+ return scm_i_peek_byte_or_eof (port);
}
SCM_INLINE_IMPLEMENTATION void
diff --git a/libguile/ports.c b/libguile/ports.c
index 13a993e..ee14ca5 100644
--- a/libguile/ports.c
+++ b/libguile/ports.c
@@ -1445,6 +1445,48 @@ scm_fill_input (SCM port)
return scm_i_fill_input (port);
}
+/* Slow-path fallback for 'scm_get_byte_or_eof' in inline.h */
+int
+scm_i_get_byte_or_eof (SCM port)
+{
+ scm_t_port *pt = SCM_PTAB_ENTRY (port);
+
+ if (pt->rw_active == SCM_PORT_WRITE)
+ scm_flush (port);
+
+ if (pt->rw_random)
+ pt->rw_active = SCM_PORT_READ;
+
+ if (pt->read_pos >= pt->read_end)
+ {
+ if (SCM_UNLIKELY (scm_i_fill_input (port) == EOF))
+ return EOF;
+ }
+
+ return *pt->read_pos++;
+}
+
+/* Slow-path fallback for 'scm_peek_byte_or_eof' in inline.h */
+int
+scm_i_peek_byte_or_eof (SCM port)
+{
+ scm_t_port *pt = SCM_PTAB_ENTRY (port);
+
+ if (pt->rw_active == SCM_PORT_WRITE)
+ scm_flush (port);
+
+ if (pt->rw_random)
+ pt->rw_active = SCM_PORT_READ;
+
+ if (pt->read_pos >= pt->read_end)
+ {
+ if (SCM_UNLIKELY (scm_i_fill_input (port) == EOF))
+ return EOF;
+ }
+
+ return *pt->read_pos;
+}
+
/* scm_lfwrite
*
diff --git a/libguile/ports.h b/libguile/ports.h
index 53d5081..54bf595 100644
--- a/libguile/ports.h
+++ b/libguile/ports.h
@@ -328,6 +328,8 @@ scm_i_default_port_conversion_handler (void);
/* Use HANDLER as the default conversion strategy for future ports. */
SCM_INTERNAL void
scm_i_set_default_port_conversion_handler (scm_t_string_failed_conversion_handler);
+SCM_INTERNAL int scm_i_get_byte_or_eof (SCM port);
+SCM_INTERNAL int scm_i_peek_byte_or_eof (SCM port);
SCM_API SCM scm_port_conversion_strategy (SCM port);
SCM_API SCM scm_set_port_conversion_strategy_x (SCM port, SCM behavior);
--
1.7.10.4
^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH] Move slow path out of 'scm_get_byte_or_eof' et al
2013-04-02 17:54 ` Mark H Weaver
@ 2013-04-02 18:10 ` Mike Gran
2013-04-03 8:12 ` Ludovic Courtès
1 sibling, 0 replies; 6+ messages in thread
From: Mike Gran @ 2013-04-02 18:10 UTC (permalink / raw)
To: Mark H Weaver; +Cc: guile-devel@gnu.org
> From: Mark H Weaver <mhw@netris.org>
> Thanks for nudging me to do the measurement. To be honest, the original
> patch I posted slowed things down by 4.5%, which I found extremely
> surprising. I fixed it by adding an internal 'static' version of
> 'scm_fill_input'. So for those who doubt the cost of function calls
> though the shared library PLT (which, within a shared library, is
> *every* function call to a non-static function), let this be a lesson.
> That's the only thing that changed here, and it made more than a 4%
> difference, even though the procedure call in question was done only
> once per 4 kilobyte buffer!
> I've attached the new patches. Okay to push now?
I don't think I get a vote, but, I vote yes anyway.
-Mike
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH] Move slow path out of 'scm_get_byte_or_eof' et al
2013-04-02 17:54 ` Mark H Weaver
2013-04-02 18:10 ` Mike Gran
@ 2013-04-03 8:12 ` Ludovic Courtès
1 sibling, 0 replies; 6+ messages in thread
From: Ludovic Courtès @ 2013-04-03 8:12 UTC (permalink / raw)
To: guile-devel
Mark H Weaver <mhw@netris.org> skribis:
> Fair enough. First of all, this patch reduces the size of the built
> libguile.so by about 42 kilobytes, and libguile.a by about 96 kilobytes.
>
> As for performance: the updated patches (attached below) slows things
> down by about one quarter of one percent on my machine. The specific
> benchmark I did was to call 'read-string' on an 11 megabyte ASCII text
> file, with the port-encoding set to UTF-8. In this case, all the reads
> are done using 'scm_get_byte_or_eof' (called from 'get_utf8_codepoint').
OK. Interesting.
> I've attached the new patches. Okay to push now?
Yes!
Thanks for the analysis,
Ludo’.
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2013-04-03 8:12 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2013-04-02 9:59 [PATCH] Move slow path out of 'scm_get_byte_or_eof' et al Mark H Weaver
2013-04-02 12:43 ` Mike Gran
2013-04-02 15:59 ` Ludovic Courtès
2013-04-02 17:54 ` Mark H Weaver
2013-04-02 18:10 ` Mike Gran
2013-04-03 8:12 ` Ludovic Courtès
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).