Re: [PATCH v2 0/5] mm: support parallel free of memory

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Tue, Mar 21, 2017 at 07:54:37AM -0700, Dave Hansen wrote:
> On 03/16/2017 02:07 AM, Michal Hocko wrote:
> > On Wed 15-03-17 14:38:34, Tim Chen wrote:
> >> max_active:   time
> >> 1             8.9s   ±0.5%
> >> 2             5.65s  ±5.5%
> >> 4             4.84s  ±0.16%
> >> 8             4.77s  ±0.97%
> >> 16            4.85s  ±0.77%
> >> 32            6.21s  ±0.46%
> > 
> > OK, but this will depend on the HW, right? Also now that I am looking at
> > those numbers more closely. This was about unmapping 320GB area and
> > using 4 times more CPUs you managed to half the run time. Is this really
> > worth it? Sure if those CPUs were idle then this is a clear win but if
> > the system is moderately busy then it doesn't look like a clear win to
> > me.
> 
> This still suffers from zone lock contention.  It scales much better if
> we are freeing memory from more than one zone.  We would expect any
> other generic page allocator scalability improvements to really help
> here, too.
> 
> Aaron, could you make sure to make sure that the memory being freed is
> coming from multiple NUMA nodes?  It might also be interesting to boot
> with a fake NUMA configuration with a *bunch* of nodes to see what the
> best case looks like when zone lock contention isn't even in play where
> one worker would be working on its own zone.

This fake NUMA configuration thing is great for this purpose, I didn't
know we have this support in kernel.

So I added numa=fake=128 and also wrote a new test program(attached)
that mmap() 321G memory and made sure they are distributed equally in
107 nodes, i.e. 3G on each node. This is achieved by using mbind before
touching the memory on each node.

Then I enlarged the max_gather_batch_count to 1543 so that during zap,
3G memory is sent to a kworker for free instead of the default 1G. In
this way, each kworker should be working on a different node.

With this change, time to free the 321G memory is reduced to:

	3.23s ±13.7%  (about 70% decrease)

Lock contention is 1.81%:

        19.60%  [kernel.kallsyms]  [k] release_pages
        13.30%  [kernel.kallsyms]  [k] unmap_page_range
        13.18%  [kernel.kallsyms]  [k] free_pcppages_bulk
         8.34%  [kernel.kallsyms]  [k] __mod_zone_page_state
         7.75%  [kernel.kallsyms]  [k] page_remove_rmap
         7.37%  [kernel.kallsyms]  [k] free_hot_cold_page
         6.06%  [kernel.kallsyms]  [k] free_pages_and_swap_cache
         3.53%  [kernel.kallsyms]  [k] __list_del_entry_valid
         3.09%  [kernel.kallsyms]  [k] __list_add_valid
         1.81%  [kernel.kallsyms]  [k] native_queued_spin_lock_slowpath
         1.79%  [kernel.kallsyms]  [k] uncharge_list
         1.69%  [kernel.kallsyms]  [k] mem_cgroup_update_lru_size
         1.60%  [kernel.kallsyms]  [k] vm_normal_page
         1.46%  [kernel.kallsyms]  [k] __dec_node_state
         1.41%  [kernel.kallsyms]  [k] __mod_node_page_state
         1.20%  [kernel.kallsyms]  [k] __tlb_remove_page_size
         0.85%  [kernel.kallsyms]  [k] mem_cgroup_page_lruvec

>From 'vmstat 1', the runnable process peaked at 6 during munmap():
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 0  0      0 189114560      0 761292    0    0     0     0   70  146  0  0 100  0  0
 3  0      0 189099008      0 759932    0    0     0     0 2536  382  0  0 100  0  0
 6  0      0 274378848      0 759972    0    0     0     0 11332  249  0  3 97  0  0
 5  0      0 374426592      0 759972    0    0     0     0 13576  196  0  3 97  0  0
 4  0      0 474990144      0 759972    0    0     0     0 13250  227  0  3 97  0  0
 0  0      0 526039296      0 759972    0    0     0     0 6799  246  0  2 98  0  0
^C

This appears to be the best result from this approach.
#include <numaif.h>
#include <sys/mman.h>
#include <sys/time.h>
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

#define BITS_PER_LONG 64
#define MAX_THREADS 256

static void *p;
static int maxnode;
static size_t per_node_len;

pthread_t threads[MAX_THREADS];

void *alloc_memory(void *arg)
{
	size_t i;
	int v, r, ret;
	unsigned long nid = (unsigned long)arg;
	unsigned long nodemask[4];
	void *q = (char *)p + per_node_len * nid;

	nodemask[0] = nodemask[1] = nodemask[2] = nodemask[3] = 0;
	v = nid / BITS_PER_LONG;
	r = nid % BITS_PER_LONG;
	nodemask[v] |= 1UL << r;
	printf("node=%d, nodemask=0x%llx 0x%llx 0x%llx 0x%llx\n", nid,
			nodemask[0], nodemask[1], nodemask[2], nodemask[3]);
	ret = mbind(q, per_node_len, MPOL_BIND, nodemask, maxnode, 0);
	if (ret == -1) {
		perror("mbind");
		return (void *)-1;
	}
	for (i = 0; i < per_node_len; i += 0x1000)
		*((char *)q + i) = nid;

	return NULL;
}

int main(int argc, char *argv[])
{
	int ret, node_nr;
	struct timeval t1, t2;
	unsigned long i, deltaus;

	if (argc != 3) {
		fprintf(stderr, "usage: ./node_alloc node_nr per_node_len_in_MB\n");
		return 0;
	}

	node_nr = atoi(argv[1]);
	per_node_len = atoi(argv[2]);
	printf("node_nr=%d, per_node_len=%dMB\n", node_nr, per_node_len);
	per_node_len <<= 20;
	printf("per_node_len=0x%lx\n", per_node_len);
	
	p = mmap(NULL, node_nr * per_node_len, PROT_READ | PROT_WRITE,
			MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
	if (p == MAP_FAILED) {
		perror("mmap");
		return -1;
	}

	maxnode = node_nr + 1;
	for (i = 0; i < node_nr; i++)
		pthread_create(&threads[i], NULL, alloc_memory, (void *)i);

	for (i = 0; i < node_nr; i++)
		pthread_join(threads[i], NULL);

	printf("allocation done, press enter to start free\n");
	getchar();

	gettimeofday(&t1, NULL);
	munmap(p, node_nr * per_node_len);
	gettimeofday(&t2, NULL);
	deltaus = (t2.tv_sec - t1.tv_sec) * 1000000 + t2.tv_usec - t1.tv_usec;
	printf("time spent: %fs\n", (float)deltaus / 1000000);

	return 0;
}

[Index of Archives]     [Linux ARM Kernel]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [IETF Annouce]     [Bugtraq]     [Linux OMAP]     [Linux MIPS]     [eCos]     [Asterisk Internet PBX]     [Linux API]
  Powered by Linux