Skip to content

Commit e5b46c7

Browse files
author
Darrick J. Wong
committed
xfs: speed up xfarray sort by sorting xfile page contents directly
If all the records in an xfarray subset live within the same memory page, we can short-circuit even more quicksort recursion by mapping that page into the local CPU and using the kernel's heapsort function to sort the subset. On the author's computer, this reduces the runtime by another 15% on a 500,000 element array. Signed-off-by: Darrick J. Wong <[email protected]> Reviewed-by: Kent Overstreet <[email protected]> Reviewed-by: Dave Chinner <[email protected]>
1 parent 137db33 commit e5b46c7

File tree

3 files changed

+121
-0
lines changed

3 files changed

+121
-0
lines changed

fs/xfs/scrub/trace.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -869,6 +869,26 @@ TRACE_EVENT(xfarray_isort,
869869
__entry->hi - __entry->lo)
870870
);
871871

872+
TRACE_EVENT(xfarray_pagesort,
873+
TP_PROTO(struct xfarray_sortinfo *si, uint64_t lo, uint64_t hi),
874+
TP_ARGS(si, lo, hi),
875+
TP_STRUCT__entry(
876+
__field(unsigned long, ino)
877+
__field(unsigned long long, lo)
878+
__field(unsigned long long, hi)
879+
),
880+
TP_fast_assign(
881+
__entry->ino = file_inode(si->array->xfile->file)->i_ino;
882+
__entry->lo = lo;
883+
__entry->hi = hi;
884+
),
885+
TP_printk("xfino 0x%lx lo %llu hi %llu elts %llu",
886+
__entry->ino,
887+
__entry->lo,
888+
__entry->hi,
889+
__entry->hi - __entry->lo)
890+
);
891+
872892
TRACE_EVENT(xfarray_qsort,
873893
TP_PROTO(struct xfarray_sortinfo *si, uint64_t lo, uint64_t hi),
874894
TP_ARGS(si, lo, hi),

fs/xfs/scrub/xfarray.c

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -545,6 +545,87 @@ xfarray_isort(
545545
return xfile_obj_store(si->array->xfile, scratch, len, lo_pos);
546546
}
547547

548+
/* Grab a page for sorting records. */
549+
static inline int
550+
xfarray_sort_get_page(
551+
struct xfarray_sortinfo *si,
552+
loff_t pos,
553+
uint64_t len)
554+
{
555+
int error;
556+
557+
error = xfile_get_page(si->array->xfile, pos, len, &si->xfpage);
558+
if (error)
559+
return error;
560+
561+
/*
562+
* xfile pages must never be mapped into userspace, so we skip the
563+
* dcache flush when mapping the page.
564+
*/
565+
si->page_kaddr = kmap_local_page(si->xfpage.page);
566+
return 0;
567+
}
568+
569+
/* Release a page we grabbed for sorting records. */
570+
static inline int
571+
xfarray_sort_put_page(
572+
struct xfarray_sortinfo *si)
573+
{
574+
if (!si->page_kaddr)
575+
return 0;
576+
577+
kunmap_local(si->page_kaddr);
578+
si->page_kaddr = NULL;
579+
580+
return xfile_put_page(si->array->xfile, &si->xfpage);
581+
}
582+
583+
/* Decide if these records are eligible for in-page sorting. */
584+
static inline bool
585+
xfarray_want_pagesort(
586+
struct xfarray_sortinfo *si,
587+
xfarray_idx_t lo,
588+
xfarray_idx_t hi)
589+
{
590+
pgoff_t lo_page;
591+
pgoff_t hi_page;
592+
loff_t end_pos;
593+
594+
/* We can only map one page at a time. */
595+
lo_page = xfarray_pos(si->array, lo) >> PAGE_SHIFT;
596+
end_pos = xfarray_pos(si->array, hi) + si->array->obj_size - 1;
597+
hi_page = end_pos >> PAGE_SHIFT;
598+
599+
return lo_page == hi_page;
600+
}
601+
602+
/* Sort a bunch of records that all live in the same memory page. */
603+
STATIC int
604+
xfarray_pagesort(
605+
struct xfarray_sortinfo *si,
606+
xfarray_idx_t lo,
607+
xfarray_idx_t hi)
608+
{
609+
void *startp;
610+
loff_t lo_pos = xfarray_pos(si->array, lo);
611+
uint64_t len = xfarray_pos(si->array, hi - lo);
612+
int error = 0;
613+
614+
trace_xfarray_pagesort(si, lo, hi);
615+
616+
xfarray_sort_bump_loads(si);
617+
error = xfarray_sort_get_page(si, lo_pos, len);
618+
if (error)
619+
return error;
620+
621+
xfarray_sort_bump_heapsorts(si);
622+
startp = si->page_kaddr + offset_in_page(lo_pos);
623+
sort(startp, hi - lo + 1, si->array->obj_size, si->cmp_fn, NULL);
624+
625+
xfarray_sort_bump_stores(si);
626+
return xfarray_sort_put_page(si);
627+
}
628+
548629
/* Return a pointer to the xfarray pivot record within the sortinfo struct. */
549630
static inline void *xfarray_sortinfo_pivot(struct xfarray_sortinfo *si)
550631
{
@@ -699,6 +780,10 @@ xfarray_qsort_push(
699780
* 4. For small sets, load the records into the scratchpad and run heapsort on
700781
* them because that is very fast. In the author's experience, this yields
701782
* a ~10% reduction in runtime.
783+
*
784+
* If a small set is contained entirely within a single xfile memory page,
785+
* map the page directly and run heap sort directly on the xfile page
786+
* instead of using the load/store interface. This halves the runtime.
702787
*/
703788

704789
/*
@@ -744,6 +829,18 @@ xfarray_sort(
744829
continue;
745830
}
746831

832+
/*
833+
* If directly mapping the page and sorting can solve our
834+
* problems, we're done.
835+
*/
836+
if (xfarray_want_pagesort(si, lo, hi)) {
837+
error = xfarray_pagesort(si, lo, hi);
838+
if (error)
839+
goto out_free;
840+
si->stack_depth--;
841+
continue;
842+
}
843+
747844
/* If insertion sort can solve our problems, we're done. */
748845
if (xfarray_want_isort(si, lo, hi)) {
749846
error = xfarray_isort(si, lo, hi);

fs/xfs/scrub/xfarray.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,10 @@ struct xfarray_sortinfo {
8080
/* XFARRAY_SORT_* flags; see below. */
8181
unsigned int flags;
8282

83+
/* Cache a page here for faster access. */
84+
struct xfile_page xfpage;
85+
void *page_kaddr;
86+
8387
#ifdef DEBUG
8488
/* Performance statistics. */
8589
uint64_t loads;

0 commit comments

Comments
 (0)