Наши партнеры

UnixForum





Библиотека сайта rus-linux.net

На главную -> MyLDP -> Электронные книги по ОС Linux
Цилюрик О.И. Модули ядра Linux
Назад Вперед

Приложение Г: Тесты распределителя памяти

Возможности динамического выделения памяти детально обсуждались ранее. Но в литературе и обсуждениях фигурируют самые разнообразные и противоречивые цифры и рекомендации по использованию (или не использованию) механизмов kmalloc(), vmalloc(), __get_free_pages(). Проделаем некоторые грубые оценки на различных компьютерах, с различными объёмами реальной RAM и с установленными Linux различных версий ядра. Для этого используем подготовленные тесты (архив mtest.tgz):

memmax.c :

	#include <linux/module.h> 
	#include <linux/slab.h> 
	#include <linux/vmalloc.h> 
	
	static int mode = 0; // выделение памяти: 0 - kmalloc(), 1 - __get_free_pages(), 2 - vmalloc() 
	module_param( mode, int, S_IRUGO ); 
	
	char *mfun[] = { "kmalloc", "__get_free_pages", "vmalloc" }; 
	
	static int __init init( void ) { 
	   static char *kbuf; 
	   static unsigned long order, size; 
	   if( mode < 0 || mode > 2 ) {  
	      printk( KERN_ERR "illegal mode value\n" ); 
	      return -1; 
	   } 
	   for( size = PAGE_SIZE, order = 0; ; order++, size *= 2 ) { 
	      char msg[ 120 ]; 
	      sprintf( msg, "order=%2ld, pages=%6ld, size=%9ld - %s ",
	               order, size / PAGE_SIZE, size, mfun[ mode ] ); 
	      switch( mode ) { 
	         case 0: 
	            kbuf = (char *)kmalloc( (size_t)size, GFP_KERNEL ); 
	            break; 
	         case 1: 
	            kbuf = (char *)__get_free_pages( GFP_KERNEL, order ); 
	            break; 
	         case 2: 
	            kbuf = (char *)vmalloc( size ); 
	            break; 
	      } 
	      strcat( msg, kbuf ? "OK\n" : "failed\n" ); 
	      printk( KERN_INFO "%s", msg ); 
	      if( !kbuf ) break; 
	      switch( mode ) { 
	         case 0: 
	            kfree( kbuf ); 
	            break; 
	         case 1: 
	            free_pages( (unsigned long)kbuf, order ); 
	            break; 
	         case 2: 
	            vfree( kbuf ); 
	            break; 
	      } 
	   } 
	   return -1; 
	} 
	module_init( init ); 
	
	MODULE_AUTHOR( "Oleg Tsiliuric <olej@front.ru>" ); 
	MODULE_DESCRIPTION( "memory allocation size test" ); 
	MODULE_LICENSE( "GPL v2" ); 

По 3-м экземплярам компьютеров с Linux указываются ниже перед результатами тестирования: а). версия ядра, б). объём установленной оперативной памяти.

$ uname -r

	2.6.32.9-70.fc12.i686.PAE

$ cat /proc/meminfo | grep MemTotal

	MemTotal:        2053828 kB

$ sudo insmod memmax.ko mode=0

	insmod: error inserting 'memmax.ko': -1 Operation not permitted

$ dmesg | tail -n100 | grep order

	order= 0, pages=     1, size=    4096 - kmalloc OK
	order= 1, pages=     2, size=    8192 - kmalloc OK
	order= 2, pages=     4, size=   16384 - kmalloc OK
	order= 3, pages=     8, size=   32768 - kmalloc OK
	order= 4, pages=    16, size=   65536 - kmalloc OK
	order= 5, pages=    32, size=  131072 - kmalloc OK
	order= 6, pages=    64, size=  262144 - kmalloc OK
	order= 7, pages=   128, size=  524288 - kmalloc OK
	order= 8, pages=   256, size= 1048576 - kmalloc OK
	order= 9, pages=   512, size= 2097152 - kmalloc OK
	order=10, pages=  1024, size= 4194304 - kmalloc OK
	order=11, pages=  2048, size= 8388608 - kmalloc failed

$ sudo insmod memmax.ko mode=1

	insmod: error inserting 'memmax.ko': -1 Operation not permitted

$ dmesg | tail -n100 | grep order

	order= 0, pages=     1, size=    4096 - __get_free_pages OK
	order= 1, pages=     2, size=    8192 - __get_free_pages OK
	order= 2, pages=     4, size=   16384 - __get_free_pages OK
	order= 3, pages=     8, size=   32768 - __get_free_pages OK
	order= 4, pages=    16, size=   65536 - __get_free_pages OK
	order= 5, pages=    32, size=  131072 - __get_free_pages OK
	order= 6, pages=    64, size=  262144 - __get_free_pages OK
	order= 7, pages=   128, size=  524288 - __get_free_pages OK
	order= 8, pages=   256, size= 1048576 - __get_free_pages OK
	order= 9, pages=   512, size= 2097152 - __get_free_pages OK
	order=10, pages=  1024, size= 4194304 - __get_free_pages OK
	order=11, pages=  2048, size= 8388608 - __get_free_pages failed

$ sudo insmod memmax.ko mode=2

	insmod: error inserting 'memmax.ko': -1 Operation not permitted

$ dmesg | tail -n100 | grep order

	order= 0, pages=     1, size=    4096 - vmalloc OK
	order= 1, pages=     2, size=    8192 - vmalloc OK
	order= 2, pages=     4, size=   16384 - vmalloc OK
	order= 3, pages=     8, size=   32768 - vmalloc OK
	order= 4, pages=    16, size=   65536 - vmalloc OK
	order= 5, pages=    32, size=  131072 - vmalloc OK
	order= 6, pages=    64, size=  262144 - vmalloc OK
	order= 7, pages=   128, size=  524288 - vmalloc OK
	order= 8, pages=   256, size= 1048576 - vmalloc OK
	order= 9, pages=   512, size= 2097152 - vmalloc OK
	order=10, pages=  1024, size= 4194304 - vmalloc OK
	order=11, pages=  2048, size= 8388608 - vmalloc OK
	order=12, pages=  4096, size=16777216 - vmalloc OK
	order=13, pages=  8192, size=33554432 - vmalloc OK
	order=14, pages= 16384, size=67108864 - vmalloc failed

$ uname -r

	2.6.18-92.el5

$ cat /proc/meminfo | grep MemTotal

	MemTotal:       255600 kB

$ sudo /sbin/insmod memmax.ko mode=0

	insmod: error inserting 'memmax.ko': -1 Operation not permitted

$ dmesg | tail -n100 | grep order

	EXT3-fs: mounted filesystem with ordered data mode.
	order= 0, pages=     1, size=    4096 - kmalloc OK
	order= 1, pages=     2, size=    8192 - kmalloc OK
	order= 2, pages=     4, size=   16384 - kmalloc OK
	order= 3, pages=     8, size=   32768 - kmalloc OK
	order= 4, pages=    16, size=   65536 - kmalloc OK
	order= 5, pages=    32, size=  131072 - kmalloc OK
	order= 6, pages=    64, size=  262144 - kmalloc failed

$ sudo /sbin/insmod memmax.ko mode=1

insmod: error inserting 'memmax.ko': -1 Operation not permitted

$ dmesg | tail -n100 | grep order

	order= 0, pages=     1, size=    4096 - __get_free_pages OK
	order= 1, pages=     2, size=    8192 - __get_free_pages OK
	order= 2, pages=     4, size=   16384 - __get_free_pages OK
	order= 3, pages=     8, size=   32768 - __get_free_pages OK
	order= 4, pages=    16, size=   65536 - __get_free_pages OK
	order= 5, pages=    32, size=  131072 - __get_free_pages OK
	order= 6, pages=    64, size=  262144 - __get_free_pages OK
	order= 7, pages=   128, size=  524288 - __get_free_pages OK
	order= 8, pages=   256, size= 1048576 - __get_free_pages OK
	order= 9, pages=   512, size= 2097152 - __get_free_pages OK
	order=10, pages=  1024, size= 4194304 - __get_free_pages OK
	order=11, pages=  2048, size= 8388608 - __get_free_pages failed

$ sudo /sbin/insmod memmax.ko mode=2

	insmod: error inserting 'memmax.ko': -1 Operation not permitted

$ dmesg | tail -n100 | grep order

	order= 0, pages=     1, size=    4096 - vmalloc OK
	order= 1, pages=     2, size=    8192 - vmalloc OK
	order= 2, pages=     4, size=   16384 - vmalloc OK
	order= 3, pages=     8, size=   32768 - vmalloc OK
	order= 4, pages=    16, size=   65536 - vmalloc OK
	order= 5, pages=    32, size=  131072 - vmalloc OK
	order= 6, pages=    64, size=  262144 - vmalloc OK
	order= 7, pages=   128, size=  524288 - vmalloc OK
	order= 8, pages=   256, size= 1048576 - vmalloc OK
	order= 9, pages=   512, size= 2097152 - vmalloc OK
	order=10, pages=  1024, size= 4194304 - vmalloc OK
	order=11, pages=  2048, size= 8388608 - vmalloc OK
	order=12, pages=  4096, size=16777216 - vmalloc OK
	order=13, pages=  8192, size=33554432 - vmalloc OK
	order=14, pages= 16384, size=67108864 - vmalloc OK
	order=15, pages= 32768, size=134217728 - vmalloc OK
	order=16, pages= 65536, size=268435456 - vmalloc failed

$ uname -r

	2.6.35.13-92.fc14.x86_64

$ cat /proc/meminfo | grep MemTotal

	MemTotal:        4047192 kB

$ sudo /sbin/insmod memmax.ko mode=0

	insmod: error inserting 'memmax.ko': -1 Operation not permitted

$ dmesg | tail -n100 | grep order

	[1747955.216447] order= 0, pages=    1, size=     4096 - kmalloc OK
	[1747955.216452] order= 1, pages=    2, size=     8192 - kmalloc OK
	[1747955.216456] order= 2, pages=    4, size=    16384 - kmalloc OK
	[1747955.216460] order= 3, pages=    8, size=    32768 - kmalloc OK
	[1747955.216465] order= 4, pages=   16, size=    65536 - kmalloc OK
	[1747955.216469] order= 5, pages=   32, size=   131072 - kmalloc OK
	[1747955.216475] order= 6, pages=   64, size=   262144 - kmalloc OK
	[1747955.216481] order= 7, pages=  128, size=   524288 - kmalloc OK
	[1747955.216495] order= 8, pages=  256, size=  1048576 - kmalloc OK
	[1747955.216519] order= 9, pages=  512, size=  2097152 - kmalloc OK
	[1747955.325561] order=10, pages= 1024, size=  4194304 - kmalloc OK
	[1747955.325695] order=11, pages= 2048, size=  8388608 - kmalloc failed

$ sudo /sbin/insmod memmax.ko mode=1

	insmod: error inserting 'memmax.ko': -1 Operation not permitted

$ dmesg | tail -n100 | grep order

	[1748395.522702] order= 0, pages=    1, size=     4096 - __get_free_pages OK
	[1748395.522708] order= 1, pages=    2, size=     8192 - __get_free_pages OK
	[1748395.522712] order= 2, pages=    4, size=    16384 - __get_free_pages OK
	[1748395.522716] order= 3, pages=    8, size=    32768 - __get_free_pages OK
	[1748395.522720] order= 4, pages=   16, size=    65536 - __get_free_pages OK
	[1748395.522725] order= 5, pages=   32, size=   131072 - __get_free_pages OK
	[1748395.522730] order= 6, pages=   64, size=   262144 - __get_free_pages OK
	[1748395.522737] order= 7, pages=  128, size=   524288 - __get_free_pages OK
	[1748395.522745] order= 8, pages=  256, size=  1048576 - __get_free_pages OK
	[1748395.522759] order= 9, pages=  512, size=  2097152 - __get_free_pages OK
	[1748395.522777] order=10, pages= 1024, size=  4194304 - __get_free_pages OK
	[1748395.522788] order=11, pages= 2048, size=  8388608 - __get_free_pages failed

$ sudo /sbin/insmod memmax.ko mode=2

	insmod: error inserting 'memmax.ko': -1 Operation not permitted

$ dmesg | tail -n100 | grep order

	[1747830.678358] order= 0, pages=    1, size=     4096 - vmalloc OK
	[1747830.678445] order= 1, pages=    2, size=     8192 - vmalloc OK
	[1747830.678496] order= 2, pages=    4, size=    16384 - vmalloc OK
	[1747830.678552] order= 3, pages=    8, size=    32768 - vmalloc OK
	[1747830.678607] order= 4, pages=   16, size=    65536 - vmalloc OK
	[1747830.678667] order= 5, pages=   32, size=   131072 - vmalloc OK
	[1747830.678745] order= 6, pages=   64, size=   262144 - vmalloc OK
	[1747830.678848] order= 7, pages=  128, size=   524288 - vmalloc OK
	[1747830.679015] order= 8, pages=  256, size=  1048576 - vmalloc OK
	[1747830.679312] order= 9, pages=  512, size=  2097152 - vmalloc OK
	[1747830.679932] order=10, pages= 1024, size=  4194304 - vmalloc OK
	[1747830.681139] order=11, pages= 2048, size=  8388608 - vmalloc OK
	[1747830.683463] order=12, pages= 4096, size= 16777216 - vmalloc OK
	[1747830.688677] order=13, pages= 8192, size= 33554432 - vmalloc OK
	[1747830.697957] order=14, pages= 16384, size= 67108864 - vmalloc OK
	[1747830.712238] order=15, pages= 32768, size=134217728 - vmalloc OK
	[1747830.742639] order=16, pages= 65536, size=268435456 - vmalloc OK
	[1747830.810859] order=17, pages=131072, size=536870912 - vmalloc OK
	[1747831.040146] order=18, pages=262144, size=1073741824 - vmalloc OK
	[1747831.636957] order=19, pages=524288, size=2147483648 - vmalloc OK
	[1747831.784385] order=20, pages=1048576, size=4294967296 - vmalloc failed

Обратите внимание!: тест показывает не максимально возможный размер блока, который тот или иной механизм выделения памяти способен разместить (и такой тест несложно соорудить из показанного), а грубо оценивает блок, который уже нельзя разместить.

Следующая вещь, которая явно требует оценивания — это порядок временных затрат на выделение блока при использовании того или иного механизма. Код такого модуля-теста показан ниже:

memtim.c :

	#include <linux/module.h> 
	#include <linux/slab.h> 
	#include <linux/vmalloc.h> 
	#include <asm/msr.h>
	#include <linux/sched.h> 
	
	static long size = 1000; 
	module_param( size, long, 0 ); 
	
	#define CYCLES 1024                      // число циклов накопления 
	
	static int __init init( void ) { 
	   int i; 
	   unsigned long order = 1, psize;
	   unsigned long long calibr = 0; 
	   const char *mfun[] = { "kmalloc", "__get_free_pages", "vmalloc" }; 
	   for( psize = PAGE_SIZE; psize < size; order++, psize *= 2 ); 
	   printk( KERN_INFO "size = %ld order = %ld(%ld)\n", size, order, psize ); 
	   for( i = 0; i < CYCLES; i++ ) {       // калибровка времени выполнения rdtscll() 
	      unsigned long long t1, t2; 
	      schedule();                       // обеспечивает лучшую повторяемость
	      rdtscll( t1 ); 
	      rdtscll( t2 ); 
	      calibr += ( t2 - t1 ); 
	   } 
	   calibr = calibr / CYCLES; 
	   printk( KERN_INFO "calibr=%lld\n", calibr  ); 
	   for( i = 0; i < sizeof( mfun ) / sizeof( mfun[ 0 ] ); i++ ) { 
	      char *kbuf; 
	      char msg[ 120 ]; 
	      int j; 
	      unsigned long long suma = 0;
	      sprintf( msg, "proc. cycles for allocate %s : ", mfun[ i ] ); 
	      for( j = 0; j < CYCLES; j++ ) {    // циклы накопления измерений 
	         unsigned long long t1, t2; 
	         schedule();                    // обеспечивает лучшую повторяемость
	         rdtscll( t1 ); 
	         switch( i ) { 
	            case 0: 
	               kbuf = (char *)kmalloc( (size_t)size, GFP_KERNEL ); 
	               break; 
	            case 1: 
	               kbuf = (char *)__get_free_pages( GFP_KERNEL, order ); 
	               break; 
	            case 2: 
	               kbuf = (char *)vmalloc( size ); 
	               break; 
	         } 
	         if( !kbuf ) break; 
	         rdtscll( t2 ); 
	         suma += ( t2 - t1 - calibr ); 
	         switch( i ) { 
	            case 0: 
	               kfree( kbuf ); 
	               break; 
	            case 1: 
	               free_pages( (unsigned long)kbuf, order ); 
	               break; 
	            case 2: 
	               vfree( kbuf ); 
	               break; 
	         } 
	      } 
	      if( kbuf ) 
	         sprintf( ( msg + strlen( msg ) ), "%lld", ( suma / CYCLES ) ); 
	      else 
	         strcat( msg, "failed" ); 
	      printk( KERN_INFO "%s\n", msg ); 
	   } 
	   return -1; 
	} 
	module_init( init ); 
	MODULE_AUTHOR( "Oleg Tsiliuric <olej@front.ru>" ); 
	MODULE_DESCRIPTION( "memory allocation speed test" ); 
	MODULE_LICENSE( "GPL v2" ); 

Результаты этого теста я приведу только для одной системы, из-за их объёмности и громоздкости. Вы их можете повторить для своего компьютера и своей версии ядра:

$ uname -r

	2.6.32.9-70.fc12.i686.PAE

$ sudo insmod ./memtim.ko

	insmod: error inserting './memtim.ko': -1 Operation not permitted 

$ dmesg | tail -n4

	size = 1000 order = 1(4096) 
	proc. cycles for allocate kmalloc : 146 
	proc. cycles for allocate __get_free_pages : 438 
	proc. cycles for allocate vmalloc : 210210 

$ sudo insmod ./memtim.ko size=4096

	insmod: error inserting './memtim.ko': -1 Operation not permitted 

$ dmesg | tail -n4

	size = 4096 order = 1(4096) 
	proc. cycles for allocate kmalloc : 181 
	proc. cycles for allocate __get_free_pages : 877 
	proc. cycles for allocate vmalloc : 59626 

$ sudo insmod ./memtim.ko size=65536

	insmod: error inserting './memtim.ko': -1 Operation not permitted 

$ dmesg | tail -n4

	size = 65536 order = 5(65536) 
	proc. cycles for allocate kmalloc : 1157 
	proc. cycles for allocate __get_free_pages : 940 
	proc. cycles for allocate vmalloc : 84129 

$ sudo insmod ./memtim.ko size=262144

	insmod: error inserting './memtim.ko': -1 Operation not permitted 

$ dmesg | tail -n4

	size = 262144 order = 7(262144) 
	proc. cycles for allocate kmalloc : 2151 
	proc. cycles for allocate __get_free_pages : 2382 
	proc. cycles for allocate vmalloc : 52026 

В последнем нашем эксперименте сделаем блок не кратным размеру страницы MMU (чуть-чуть урежем значение из предыдущего запуска):

$ sudo insmod ./memtim.ko size=262000

	insmod: error inserting './memtim.ko': -1 Operation not permitted 

$ dmesg | tail -n4

	size = 262000 order = 7(262144) 
	proc. cycles for allocate kmalloc : 8674 
	proc. cycles for allocate __get_free_pages : 4730 
	proc. cycles for allocate vmalloc : 55612 

- видно, как __get_free_pages() и kmalloc() (что странно для последнего) «впадают в задумчивость», и в разы теряют производительность; практически не замечает этого изменения.

Можно заметить следующее:

  • При распределении малых блоков разница kmalloc() и vmalloc() разительная, и составляет до 3-х порядков:

$ sudo insmod ./memtim.ko size=5

	insmod: error inserting './memtim.ko': -1 Operation not permitted 

$ dmesg | tail -n30 | grep -v audit

	size = 5 order = 1(4096) 
	proc. cycles for allocate kmalloc : 143 
	proc. cycles for allocate __get_free_pages : 890 
	proc. cycles for allocate vmalloc : 152552 
  • При увеличении размеров запрашиваемого блока различия нивелируются, и на больших объёмах не превышают порядка.
  • В этих различиях нет ничего страшного, учитывая ту гибкость и диапазон, которые обеспечивает как раз vmalloc(), если только речь не идёт о быстром получении-удалении малых блоков в динамике.

Предыдущий раздел: Оглавление Следующий раздел:
Приложение В: Пример - открытые VoIP PBX: Asterisk, FreeSwitch, и другие   Источники информации