1
Fork 0
mirror of https://git.savannah.gnu.org/git/guile.git synced 2025-04-30 03:40:34 +02:00
guile/libguile/array-map.c
Daniel Llorens 31e9f8b974 Speed up for multi-arg cases of scm_ramap functions
This patch results in a 20%-40% speedup in the > 1 argument cases of
the following microbenchmarks:

(define A (make-shared-array #0(1) (const '()) #e1e7))
; 1, 2, 3 arguments.
(define a 0) ,time (array-for-each (lambda (b) (set! a (+ a b))) A)
(define a 0) ,time (array-for-each (lambda (b c) (set! a (+ a b c))) A A)
(define a 0) ,time (array-for-each (lambda (b c d) (set! a (+ a b c d))) A A A)

(define A (make-shared-array (make-array 1) (const '()) #e1e7))
(define B (make-shared-array #0(1) (const '()) #e1e7))
; 1, 2, 3 arguments.
,time (array-map! A + B)
,time (array-map! A + B B)
,time (array-map! A + B B B)

* libguile/array-map.c (scm_ramap): Note on cproc arguments.

  (rafill): Assume that dst's lbnd is 0.

  (racp): Assume that src's lbnd is 0.

  (ramap): Assume that ra0's lbnd is 0. When there're more than two
  arguments, compute the array handles before the loop. Allocate the arg
  list once and reuse it in the loop.

  (rafe): Do as in ramap(), when there's more than one argument.

  (AREF, ASET): Remove.
2016-11-23 11:49:35 +01:00

637 lines
18 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* Copyright (C) 1996, 1998, 2000, 2001, 2004, 2005, 2006, 2008, 2009,
* 2010, 2011, 2012, 2013, 2014, 2015 Free Software Foundation, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 3 of
* the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "libguile/_scm.h"
#include "libguile/strings.h"
#include "libguile/arrays.h"
#include "libguile/smob.h"
#include "libguile/chars.h"
#include "libguile/eq.h"
#include "libguile/eval.h"
#include "libguile/feature.h"
#include "libguile/vectors.h"
#include "libguile/bitvectors.h"
#include "libguile/srfi-4.h"
#include "libguile/generalized-arrays.h"
#include "libguile/validate.h"
#include "libguile/array-map.h"
/* The WHAT argument for `scm_gc_malloc ()' et al. */
static const char vi_gc_hint[] = "array-indices";
static SCM
make1array (SCM v, ssize_t inc)
{
SCM a = scm_i_make_array (1);
SCM_I_ARRAY_SET_BASE (a, 0);
SCM_I_ARRAY_DIMS (a)->lbnd = 0;
SCM_I_ARRAY_DIMS (a)->ubnd = scm_c_array_length (v) - 1;
SCM_I_ARRAY_DIMS (a)->inc = inc;
SCM_I_ARRAY_SET_V (a, v);
return a;
}
/* Linear index of not-unrolled index set. */
static size_t
cindk (SCM ra, ssize_t *ve, int kend)
{
if (SCM_I_ARRAYP (ra))
{
int k;
size_t i = SCM_I_ARRAY_BASE (ra);
for (k = 0; k < kend; ++k)
i += (ve[k] - SCM_I_ARRAY_DIMS (ra)[k].lbnd) * SCM_I_ARRAY_DIMS (ra)[k].inc;
return i;
}
else
return 0; /* this is BASE */
}
/* array mapper: apply cproc to each dimension of the given arrays?.
int (*cproc) (); procedure to call on unrolled arrays?
cproc (dest, source list) or
cproc (dest, data, source list).
SCM data; data to give to cproc or unbound.
SCM ra0; destination array.
SCM lra; list of source arrays.
const char *what; caller, for error reporting. */
#define LBND(ra, k) SCM_I_ARRAY_DIMS (ra)[k].lbnd
#define UBND(ra, k) SCM_I_ARRAY_DIMS (ra)[k].ubnd
/* scm_ramapc() always calls cproc with rank-1 arrays created by
make1array. cproc (rafe, ramap, rafill, racp) can assume that the
dims[0].lbnd of these arrays is always 0. */
int
scm_ramapc (void *cproc_ptr, SCM data, SCM ra0, SCM lra, const char *what)
{
int (*cproc) () = cproc_ptr;
SCM z, va0, lva, *plva;
int k, kmax, kroll;
ssize_t *vi, inc;
size_t len;
/* Prepare reference argument. */
if (SCM_I_ARRAYP (ra0))
{
kmax = SCM_I_ARRAY_NDIM (ra0)-1;
inc = kmax < 0 ? 0 : SCM_I_ARRAY_DIMS (ra0)[kmax].inc;
va0 = make1array (SCM_I_ARRAY_V (ra0), inc);
/* Find unroll depth */
for (kroll = max(0, kmax); kroll > 0; --kroll)
{
inc *= (UBND (ra0, kroll) - LBND (ra0, kroll) + 1);
if (inc != SCM_I_ARRAY_DIMS (ra0)[kroll-1].inc)
break;
}
}
else
{
kroll = kmax = 0;
va0 = ra0 = make1array (ra0, 1);
}
/* Prepare rest arguments. */
lva = SCM_EOL;
plva = &lva;
for (z = lra; !scm_is_null (z); z = SCM_CDR (z))
{
SCM va1, ra1 = SCM_CAR (z);
if (SCM_I_ARRAYP (ra1))
{
if (kmax != SCM_I_ARRAY_NDIM (ra1) - 1)
scm_misc_error (what, "array shape mismatch: ~S", scm_list_1 (ra0));
inc = kmax < 0 ? 0 : SCM_I_ARRAY_DIMS (ra1)[kmax].inc;
va1 = make1array (SCM_I_ARRAY_V (ra1), inc);
/* Check unroll depth. */
for (k = kmax; k > kroll; --k)
{
ssize_t l0 = LBND (ra0, k), u0 = UBND (ra0, k);
if (l0 < LBND (ra1, k) || u0 > UBND (ra1, k))
scm_misc_error (what, "array shape mismatch: ~S", scm_list_1 (ra0));
inc *= (u0 - l0 + 1);
if (inc != SCM_I_ARRAY_DIMS (ra1)[k-1].inc)
{
kroll = k;
break;
}
}
/* Check matching of not-unrolled axes. */
for (; k>=0; --k)
if (LBND (ra0, k) < LBND (ra1, k) || UBND (ra0, k) > UBND (ra1, k))
scm_misc_error (what, "array shape mismatch: ~S", scm_list_1 (ra0));
}
else
{
if (kmax != 0)
scm_misc_error (what, "array shape mismatch: ~S", scm_list_1 (ra0));
va1 = make1array (ra1, 1);
if (LBND (ra0, 0) < 0 /* LBND (va1, 0) */ || UBND (ra0, 0) > UBND (va1, 0))
scm_misc_error (what, "array shape mismatch: ~S", scm_list_1 (ra0));
}
*plva = scm_cons (va1, SCM_EOL);
plva = SCM_CDRLOC (*plva);
}
/* Check emptiness of not-unrolled axes. */
for (k = 0; k < kroll; ++k)
if (0 == (UBND (ra0, k) - LBND (ra0, k) + 1))
return 1;
/* Set unrolled size. */
for (len = 1; k <= kmax; ++k)
len *= (UBND (ra0, k) - LBND (ra0, k) + 1);
UBND (va0, 0) = len - 1;
for (z = lva; !scm_is_null (z); z = SCM_CDR (z))
UBND (SCM_CAR (z), 0) = len - 1;
/* Set starting indices and go. */
vi = scm_gc_malloc_pointerless (sizeof(ssize_t) * kroll, vi_gc_hint);
for (k = 0; k < kroll; ++k)
vi[k] = LBND (ra0, k);
do
{
if (k == kroll)
{
SCM y = lra;
SCM_I_ARRAY_SET_BASE (va0, cindk (ra0, vi, kroll));
for (z = lva; !scm_is_null (z); z = SCM_CDR (z), y = SCM_CDR (y))
SCM_I_ARRAY_SET_BASE (SCM_CAR (z), cindk (SCM_CAR (y), vi, kroll));
if (! (SCM_UNBNDP (data) ? cproc (va0, lva) : cproc (va0, data, lva)))
return 0;
--k;
}
else if (vi[k] < UBND (ra0, k))
{
++vi[k];
++k;
}
else
{
vi[k] = LBND (ra0, k) - 1;
--k;
}
}
while (k >= 0);
return 1;
}
#undef UBND
#undef LBND
static int
rafill (SCM dst, SCM fill)
{
size_t n = SCM_I_ARRAY_DIMS (dst)->ubnd + 1;
size_t i = SCM_I_ARRAY_BASE (dst);
ssize_t inc = SCM_I_ARRAY_DIMS (dst)->inc;
scm_t_array_handle h;
dst = SCM_I_ARRAY_V (dst);
scm_array_get_handle (dst, &h);
for (; n-- > 0; i += inc)
h.vset (h.vector, i, fill);
scm_array_handle_release (&h);
return 1;
}
SCM_DEFINE (scm_array_fill_x, "array-fill!", 2, 0, 0,
(SCM ra, SCM fill),
"Store @var{fill} in every element of array @var{ra}. The value\n"
"returned is unspecified.")
#define FUNC_NAME s_scm_array_fill_x
{
scm_ramapc (rafill, fill, ra, SCM_EOL, FUNC_NAME);
return SCM_UNSPECIFIED;
}
#undef FUNC_NAME
static int
racp (SCM src, SCM dst)
{
size_t i_s, i_d, n;
ssize_t inc_s, inc_d;
scm_t_array_handle h_s, h_d;
dst = SCM_CAR (dst);
i_s = SCM_I_ARRAY_BASE (src);
i_d = SCM_I_ARRAY_BASE (dst);
n = (SCM_I_ARRAY_DIMS (src)->ubnd + 1);
inc_s = SCM_I_ARRAY_DIMS (src)->inc;
inc_d = SCM_I_ARRAY_DIMS (dst)->inc;
src = SCM_I_ARRAY_V (src);
dst = SCM_I_ARRAY_V (dst);
scm_array_get_handle (src, &h_s);
scm_array_get_handle (dst, &h_d);
if (h_s.element_type == SCM_ARRAY_ELEMENT_TYPE_SCM
&& h_d.element_type == SCM_ARRAY_ELEMENT_TYPE_SCM)
{
SCM const * el_s = h_s.elements;
SCM * el_d = h_d.writable_elements;
for (; n-- > 0; i_s += inc_s, i_d += inc_d)
el_d[i_d] = el_s[i_s];
}
else
for (; n-- > 0; i_s += inc_s, i_d += inc_d)
h_d.vset (h_d.vector, i_d, h_s.vref (h_s.vector, i_s));
scm_array_handle_release (&h_d);
scm_array_handle_release (&h_s);
return 1;
}
SCM_REGISTER_PROC(s_array_copy_in_order_x, "array-copy-in-order!", 2, 0, 0, scm_array_copy_x);
SCM_DEFINE (scm_array_copy_x, "array-copy!", 2, 0, 0,
(SCM src, SCM dst),
"@deffnx {Scheme Procedure} array-copy-in-order! src dst\n"
"Copy every element from vector or array @var{src} to the\n"
"corresponding element of @var{dst}. @var{dst} must have the\n"
"same rank as @var{src}, and be at least as large in each\n"
"dimension. The order is unspecified.")
#define FUNC_NAME s_scm_array_copy_x
{
scm_ramapc (racp, SCM_UNDEFINED, src, scm_cons (dst, SCM_EOL), FUNC_NAME);
return SCM_UNSPECIFIED;
}
#undef FUNC_NAME
static int
ramap (SCM ra0, SCM proc, SCM ras)
{
size_t i0 = SCM_I_ARRAY_BASE (ra0);
ssize_t inc0 = SCM_I_ARRAY_DIMS (ra0)->inc;
size_t n = SCM_I_ARRAY_DIMS (ra0)->ubnd + 1;
scm_t_array_handle h0;
ra0 = SCM_I_ARRAY_V (ra0);
scm_array_get_handle (ra0, &h0);
if (scm_is_null (ras))
for (; n--; i0 += inc0)
h0.vset (h0.vector, i0, scm_call_0 (proc));
else
{
SCM ra1 = SCM_CAR (ras);
size_t i1 = SCM_I_ARRAY_BASE (ra1);
ssize_t inc1 = SCM_I_ARRAY_DIMS (ra1)->inc;
scm_t_array_handle h1;
ra1 = SCM_I_ARRAY_V (ra1);
scm_array_get_handle (ra1, &h1);
ras = SCM_CDR (ras);
if (scm_is_null (ras))
for (; n--; i0 += inc0, i1 += inc1)
h0.vset (h0.vector, i0, scm_call_1 (proc, h1.vref (h1.vector, i1)));
else
{
scm_t_array_handle *hs;
size_t restn = scm_ilength (ras);
SCM args = SCM_EOL;
SCM *p = &args;
SCM **sa = scm_gc_malloc (sizeof(SCM *) * restn, vi_gc_hint);
for (size_t k = 0; k < restn; ++k)
{
*p = scm_cons (SCM_UNSPECIFIED, SCM_EOL);
sa[k] = SCM_CARLOC (*p);
p = SCM_CDRLOC (*p);
}
hs = scm_gc_malloc (sizeof(scm_t_array_handle) * restn, vi_gc_hint);
for (size_t k = 0; k < restn; ++k, ras = scm_cdr (ras))
scm_array_get_handle (scm_car (ras), hs+k);
for (ssize_t i = 0; n--; i0 += inc0, i1 += inc1, ++i)
{
for (size_t k = 0; k < restn; ++k)
*(sa[k]) = scm_array_handle_ref (hs+k, i*hs[k].dims[0].inc);
h0.vset (h0.vector, i0, scm_apply_1 (proc, h1.vref (h1.vector, i1), args));
}
for (size_t k = 0; k < restn; ++k)
scm_array_handle_release (hs+k);
}
scm_array_handle_release (&h1);
}
scm_array_handle_release (&h0);
return 1;
}
SCM_REGISTER_PROC(s_array_map_in_order_x, "array-map-in-order!", 2, 0, 1, scm_array_map_x);
SCM_SYMBOL (sym_b, "b");
SCM_DEFINE (scm_array_map_x, "array-map!", 2, 0, 1,
(SCM ra0, SCM proc, SCM lra),
"@deffnx {Scheme Procedure} array-map-in-order! ra0 proc . lra\n"
"@var{array1}, @dots{} must have the same number of dimensions\n"
"as @var{ra0} and have a range for each index which includes the\n"
"range for the corresponding index in @var{ra0}. @var{proc} is\n"
"applied to each tuple of elements of @var{array1}, @dots{} and\n"
"the result is stored as the corresponding element in @var{ra0}.\n"
"The value returned is unspecified. The order of application is\n"
"unspecified.")
#define FUNC_NAME s_scm_array_map_x
{
SCM_VALIDATE_PROC (2, proc);
SCM_VALIDATE_REST_ARGUMENT (lra);
scm_ramapc (ramap, proc, ra0, lra, FUNC_NAME);
return SCM_UNSPECIFIED;
}
#undef FUNC_NAME
static int
rafe (SCM ra0, SCM proc, SCM ras)
{
size_t i0 = SCM_I_ARRAY_BASE (ra0);
ssize_t inc0 = SCM_I_ARRAY_DIMS (ra0)->inc;
size_t n = SCM_I_ARRAY_DIMS (ra0)->ubnd + 1;
scm_t_array_handle h0;
ra0 = SCM_I_ARRAY_V (ra0);
scm_array_get_handle (ra0, &h0);
if (scm_is_null (ras))
for (; n--; i0 += inc0)
scm_call_1 (proc, h0.vref (h0.vector, i0));
else
{
scm_t_array_handle *hs;
size_t restn = scm_ilength (ras);
SCM args = SCM_EOL;
SCM *p = &args;
SCM **sa = scm_gc_malloc (sizeof(SCM *) * restn, vi_gc_hint);
for (size_t k = 0; k < restn; ++k)
{
*p = scm_cons (SCM_UNSPECIFIED, SCM_EOL);
sa[k] = SCM_CARLOC (*p);
p = SCM_CDRLOC (*p);
}
hs = scm_gc_malloc (sizeof(scm_t_array_handle) * restn, vi_gc_hint);
for (size_t k = 0; k < restn; ++k, ras = scm_cdr (ras))
scm_array_get_handle (scm_car (ras), hs+k);
for (ssize_t i = 0; n--; i0 += inc0, ++i)
{
for (size_t k = 0; k < restn; ++k)
*(sa[k]) = scm_array_handle_ref (hs+k, i*hs[k].dims[0].inc);
scm_apply_1 (proc, h0.vref (h0.vector, i0), args);
}
for (size_t k = 0; k < restn; ++k)
scm_array_handle_release (hs+k);
}
scm_array_handle_release (&h0);
return 1;
}
SCM_DEFINE (scm_array_for_each, "array-for-each", 2, 0, 1,
(SCM proc, SCM ra0, SCM lra),
"Apply @var{proc} to each tuple of elements of @var{ra0} @dots{}\n"
"in row-major order. The value returned is unspecified.")
#define FUNC_NAME s_scm_array_for_each
{
SCM_VALIDATE_PROC (1, proc);
SCM_VALIDATE_REST_ARGUMENT (lra);
scm_ramapc (rafe, proc, ra0, lra, FUNC_NAME);
return SCM_UNSPECIFIED;
}
#undef FUNC_NAME
static void
array_index_map_1 (SCM ra, SCM proc)
{
scm_t_array_handle h;
ssize_t i, inc;
size_t p;
scm_array_get_handle (ra, &h);
inc = h.dims[0].inc;
for (i = h.dims[0].lbnd, p = h.base; i <= h.dims[0].ubnd; ++i, p += inc)
h.vset (h.vector, p, scm_call_1 (proc, scm_from_ssize_t (i)));
scm_array_handle_release (&h);
}
/* Here we assume that the array is a scm_tc7_array, as that is the only
kind of array in Guile that supports rank > 1. */
static void
array_index_map_n (SCM ra, SCM proc)
{
scm_t_array_handle h;
int k, kmax = SCM_I_ARRAY_NDIM (ra) - 1;
SCM args = SCM_EOL;
SCM *p = &args;
ssize_t *vi = scm_gc_malloc_pointerless (sizeof(ssize_t) * (kmax + 1), vi_gc_hint);
SCM **si = scm_gc_malloc_pointerless (sizeof(SCM *) * (kmax + 1), vi_gc_hint);
for (k = 0; k <= kmax; k++)
{
vi[k] = SCM_I_ARRAY_DIMS (ra)[k].lbnd;
if (vi[k] > SCM_I_ARRAY_DIMS (ra)[k].ubnd)
return;
*p = scm_cons (scm_from_ssize_t (vi[k]), SCM_EOL);
si[k] = SCM_CARLOC (*p);
p = SCM_CDRLOC (*p);
}
scm_array_get_handle (ra, &h);
k = kmax;
do
{
if (k == kmax)
{
size_t i;
vi[kmax] = SCM_I_ARRAY_DIMS (ra)[kmax].lbnd;
i = cindk (ra, vi, kmax+1);
for (; vi[kmax] <= SCM_I_ARRAY_DIMS (ra)[kmax].ubnd; ++vi[kmax])
{
*(si[kmax]) = scm_from_ssize_t (vi[kmax]);
h.vset (h.vector, i, scm_apply_0 (proc, args));
i += SCM_I_ARRAY_DIMS (ra)[kmax].inc;
}
k--;
}
else if (vi[k] < SCM_I_ARRAY_DIMS (ra)[k].ubnd)
{
*(si[k]) = scm_from_ssize_t (++vi[k]);
k++;
}
else
{
vi[k] = SCM_I_ARRAY_DIMS (ra)[k].lbnd - 1;
k--;
}
}
while (k >= 0);
scm_array_handle_release (&h);
}
SCM_DEFINE (scm_array_index_map_x, "array-index-map!", 2, 0, 0,
(SCM ra, SCM proc),
"Apply @var{proc} to the indices of each element of @var{ra} in\n"
"turn, storing the result in the corresponding element. The value\n"
"returned and the order of application are unspecified.\n\n"
"One can implement @var{array-indexes} as\n"
"@lisp\n"
"(define (array-indexes array)\n"
" (let ((ra (apply make-array #f (array-shape array))))\n"
" (array-index-map! ra (lambda x x))\n"
" ra))\n"
"@end lisp\n"
"Another example:\n"
"@lisp\n"
"(define (apl:index-generator n)\n"
" (let ((v (make-uniform-vector n 1)))\n"
" (array-index-map! v (lambda (i) i))\n"
" v))\n"
"@end lisp")
#define FUNC_NAME s_scm_array_index_map_x
{
SCM_VALIDATE_PROC (2, proc);
switch (scm_c_array_rank (ra))
{
case 0:
scm_array_set_x (ra, scm_call_0 (proc), SCM_EOL);
break;
case 1:
array_index_map_1 (ra, proc);
break;
default:
array_index_map_n (ra, proc);
break;
}
return SCM_UNSPECIFIED;
}
#undef FUNC_NAME
static int
array_compare (scm_t_array_handle *hx, scm_t_array_handle *hy,
size_t dim, unsigned long posx, unsigned long posy)
{
if (dim == scm_array_handle_rank (hx))
return scm_is_true (scm_equal_p (scm_array_handle_ref (hx, posx),
scm_array_handle_ref (hy, posy)));
else
{
long incx, incy;
size_t i;
if (hx->dims[dim].lbnd != hy->dims[dim].lbnd
|| hx->dims[dim].ubnd != hy->dims[dim].ubnd)
return 0;
i = hx->dims[dim].ubnd - hx->dims[dim].lbnd + 1;
incx = hx->dims[dim].inc;
incy = hy->dims[dim].inc;
posx += (i - 1) * incx;
posy += (i - 1) * incy;
for (; i > 0; i--, posx -= incx, posy -= incy)
if (!array_compare (hx, hy, dim + 1, posx, posy))
return 0;
return 1;
}
}
SCM
scm_array_equal_p (SCM x, SCM y)
{
scm_t_array_handle hx, hy;
SCM res;
scm_array_get_handle (x, &hx);
scm_array_get_handle (y, &hy);
res = scm_from_bool (hx.ndims == hy.ndims
&& hx.element_type == hy.element_type);
if (scm_is_true (res))
res = scm_from_bool (array_compare (&hx, &hy, 0, 0, 0));
scm_array_handle_release (&hy);
scm_array_handle_release (&hx);
return res;
}
static SCM scm_i_array_equal_p (SCM, SCM, SCM);
SCM_DEFINE (scm_i_array_equal_p, "array-equal?", 0, 2, 1,
(SCM ra0, SCM ra1, SCM rest),
"Return @code{#t} iff all arguments are arrays with the same\n"
"shape, the same type, and have corresponding elements which are\n"
"either @code{equal?} or @code{array-equal?}. This function\n"
"differs from @code{equal?} in that all arguments must be arrays.")
#define FUNC_NAME s_scm_i_array_equal_p
{
if (SCM_UNBNDP (ra0) || SCM_UNBNDP (ra1))
return SCM_BOOL_T;
while (!scm_is_null (rest))
{ if (scm_is_false (scm_array_equal_p (ra0, ra1)))
return SCM_BOOL_F;
ra0 = ra1;
ra1 = scm_car (rest);
rest = scm_cdr (rest);
}
return scm_array_equal_p (ra0, ra1);
}
#undef FUNC_NAME
void
scm_init_array_map (void)
{
#include "libguile/array-map.x"
scm_add_feature (s_scm_array_for_each);
}
/*
Local Variables:
c-file-style: "gnu"
End:
*/