Mercurial > mplayer.hg
changeset 3393:3624cd351618
runtime cpu detection
author | michael |
---|---|
date | Sun, 09 Dec 2001 15:25:11 +0000 |
parents | 382ee94a716d |
children | 35b18ed357c2 |
files | libvo/aclib.c libvo/aclib_template.c |
diffstat | 2 files changed, 98 insertions(+), 354 deletions(-) [+] |
line wrap: on
line diff
--- a/libvo/aclib.c Sun Dec 09 11:52:29 2001 +0000 +++ b/libvo/aclib.c Sun Dec 09 15:25:11 2001 +0000 @@ -1,12 +1,14 @@ #include "../config.h" +#ifdef USE_FASTMEMCPY -#ifdef USE_FASTMEMCPY /* aclib - advanced C library ;) This file contains functions which improve and expand standard C-library + see aclib_template.c ... this file only contains runtime cpu detection and config options stuff + runtime cpu detection by michael niedermayer (michaelni@gmx.at) is under GPL */ - #include <stddef.h> +#include "../cpudetect.h" #define BLOCK_SIZE 4096 #define CONFUSION_FACTOR 0 @@ -14,353 +16,106 @@ //#define STATISTICS -#ifndef HAVE_SSE2 -/* - P3 processor has only one SSE decoder so can execute only 1 sse insn per - cpu clock, but it has 3 mmx decoders (include load/store unit) - and executes 3 mmx insns per cpu clock. - P4 processor has some chances, but after reading: - http://www.emulators.com/pentium4.htm - I have doubts. Anyway SSE2 version of this code can be written better. -*/ -#undef HAVE_SSE +#ifdef ARCH_X86 +#define CAN_COMPILE_X86_ASM +#endif + +//Note: we have MMX, MMX2, 3DNOW version there is no 3DNOW+MMX2 one +//Plain C versions +//#if !defined (HAVE_MMX) || defined (RUNTIME_CPUDETECT) +//#define COMPILE_C +//#endif + +#ifdef CAN_COMPILE_X86_ASM + +#if (defined (HAVE_MMX) && !defined (HAVE_3DNOW) && !defined (HAVE_MMX2)) || defined (RUNTIME_CPUDETECT) +#define COMPILE_MMX +#endif + +#if defined (HAVE_MMX2) || defined (RUNTIME_CPUDETECT) +#define COMPILE_MMX2 +#endif + +#if (defined (HAVE_3DNOW) && !defined (HAVE_MMX2)) || defined (RUNTIME_CPUDETECT) +#define COMPILE_3DNOW #endif - +#undef HAVE_MMX +#undef HAVE_MMX2 +#undef HAVE_3DNOW +#undef ARCH_X86 /* - This part of code was taken by me from Linux-2.4.3 and slightly modified -for MMX, MMX2, SSE instruction set. I have done it since linux uses page aligned -blocks but mplayer uses weakly ordered data and original sources can not -speedup them. Only using PREFETCHNTA and MOVNTQ together have effect! - ->From IA-32 Intel Architecture Software Developer's Manual Volume 1, - -Order Number 245470: -"10.4.6. Cacheability Control, Prefetch, and Memory Ordering Instructions" - -Data referenced by a program can be temporal (data will be used again) or -non-temporal (data will be referenced once and not reused in the immediate -future). To make efficient use of the processor's caches, it is generally -desirable to cache temporal data and not cache non-temporal data. Overloading -the processor's caches with non-temporal data is sometimes referred to as -"polluting the caches". -The non-temporal data is written to memory with Write-Combining semantics. - -The PREFETCHh instructions permits a program to load data into the processor -at a suggested cache level, so that it is closer to the processors load and -store unit when it is needed. If the data is already present in a level of -the cache hierarchy that is closer to the processor, the PREFETCHh instruction -will not result in any data movement. -But we should you PREFETCHNTA: Non-temporal data fetch data into location -close to the processor, minimizing cache pollution. - -The MOVNTQ (store quadword using non-temporal hint) instruction stores -packed integer data from an MMX register to memory, using a non-temporal hint. -The MOVNTPS (store packed single-precision floating-point values using -non-temporal hint) instruction stores packed floating-point data from an -XMM register to memory, using a non-temporal hint. - -The SFENCE (Store Fence) instruction controls write ordering by creating a -fence for memory store operations. This instruction guarantees that the results -of every store instruction that precedes the store fence in program order is -globally visible before any store instruction that follows the fence. The -SFENCE instruction provides an efficient way of ensuring ordering between -procedures that produce weakly-ordered data and procedures that consume that -data. - -If you have questions please contact with me: Nick Kurshev: nickols_k@mail.ru. +#ifdef COMPILE_C +#undef HAVE_MMX +#undef HAVE_MMX2 +#undef HAVE_3DNOW +#undef ARCH_X86 +#define RENAME(a) a ## _C +#include "aclib_template.c" +#endif */ - -// 3dnow memcpy support from kernel 2.4.2 -// by Pontscho/fresh!mindworkz - -#if defined( HAVE_MMX2 ) || defined( HAVE_3DNOW ) || defined( HAVE_MMX ) - -#undef HAVE_MMX1 -#if defined(HAVE_MMX) && !defined(HAVE_MMX2) && !defined(HAVE_3DNOW) && !defined(HAVE_SSE) -/* means: mmx v.1. Note: Since we added alignment of destinition it speedups - of memory copying on PentMMX, Celeron-1 and P2 upto 12% versus - standard (non MMX-optimized) version. - Note: on K6-2+ it speedups memory copying upto 25% and - on K7 and P3 about 500% (5 times). */ -#define HAVE_MMX1 -#endif - - -#undef HAVE_K6_2PLUS -#if !defined( HAVE_MMX2) && defined( HAVE_3DNOW) -#define HAVE_K6_2PLUS -#endif - -/* for small memory blocks (<256 bytes) this version is faster */ -#define small_memcpy(to,from,n)\ -{\ -register unsigned long int dummy;\ -__asm__ __volatile__(\ - "rep; movsb"\ - :"=&D"(to), "=&S"(from), "=&c"(dummy)\ -/* It's most portable way to notify compiler */\ -/* that edi, esi and ecx are clobbered in asm block. */\ -/* Thanks to A'rpi for hint!!! */\ - :"0" (to), "1" (from),"2" (n)\ - : "memory");\ -} - -#ifdef HAVE_SSE -#define MMREG_SIZE 16 -#else -#define MMREG_SIZE 64 //8 -#endif - -/* Small defines (for readability only) ;) */ -#ifdef HAVE_K6_2PLUS -#define PREFETCH "prefetch" -/* On K6 femms is faster of emms. On K7 femms is directly mapped on emms. */ -#define EMMS "femms" -#else -#define PREFETCH "prefetchnta" -#define EMMS "emms" -#endif - -#ifdef HAVE_MMX2 -#define MOVNTQ "movntq" -#else -#define MOVNTQ "movq" -#endif - -#ifdef HAVE_MMX1 -#define MIN_LEN 0x800 /* 2K blocks */ -#else -#define MIN_LEN 0x40 /* 64-byte blocks */ +//MMX versions +#ifdef COMPILE_MMX +#undef RENAME +#define HAVE_MMX +#undef HAVE_MMX2 +#undef HAVE_3DNOW +#define ARCH_X86 +#define RENAME(a) a ## _MMX +#include "aclib_template.c" #endif -void * fast_memcpy(void * to, const void * from, size_t len) -{ - void *retval; - size_t i; - retval = to; -#ifdef STATISTICS - { - static int freq[33]; - static int t=0; - int i; - for(i=0; len>(1<<i); i++); - freq[i]++; - t++; - if(1024*1024*1024 % t == 0) - for(i=0; i<32; i++) - printf("freq < %8d %4d\n", 1<<i, freq[i]); - } -#endif -#ifndef HAVE_MMX1 - /* PREFETCH has effect even for MOVSB instruction ;) */ - __asm__ __volatile__ ( - PREFETCH" (%0)\n" - PREFETCH" 64(%0)\n" - PREFETCH" 128(%0)\n" - PREFETCH" 192(%0)\n" - PREFETCH" 256(%0)\n" - : : "r" (from) ); +//MMX2 versions +#ifdef COMPILE_MMX2 +#undef RENAME +#define HAVE_MMX +#define HAVE_MMX2 +#undef HAVE_3DNOW +#define ARCH_X86 +#define RENAME(a) a ## _MMX2 +#include "aclib_template.c" #endif - if(len >= MIN_LEN) - { - register unsigned long int delta; - /* Align destinition to MMREG_SIZE -boundary */ - delta = ((unsigned long int)to)&(MMREG_SIZE-1); - if(delta) - { - delta=MMREG_SIZE-delta; - len -= delta; - small_memcpy(to, from, delta); - } - i = len >> 6; /* len/64 */ - len&=63; - /* - This algorithm is top effective when the code consequently - reads and writes blocks which have size of cache line. - Size of cache line is processor-dependent. - It will, however, be a minimum of 32 bytes on any processors. - It would be better to have a number of instructions which - perform reading and writing to be multiple to a number of - processor's decoders, but it's not always possible. - */ -#ifdef HAVE_SSE /* Only P3 (may be Cyrix3) */ - if(((unsigned long)from) & 15) - /* if SRC is misaligned */ - for(; i>0; i--) - { - __asm__ __volatile__ ( - PREFETCH" 320(%0)\n" - "movups (%0), %%xmm0\n" - "movups 16(%0), %%xmm1\n" - "movups 32(%0), %%xmm2\n" - "movups 48(%0), %%xmm3\n" - "movntps %%xmm0, (%1)\n" - "movntps %%xmm1, 16(%1)\n" - "movntps %%xmm2, 32(%1)\n" - "movntps %%xmm3, 48(%1)\n" - :: "r" (from), "r" (to) : "memory"); - ((const unsigned char *)from)+=64; - ((unsigned char *)to)+=64; - } - else - /* - Only if SRC is aligned on 16-byte boundary. - It allows to use movaps instead of movups, which required data - to be aligned or a general-protection exception (#GP) is generated. - */ - for(; i>0; i--) - { - __asm__ __volatile__ ( - PREFETCH" 320(%0)\n" - "movaps (%0), %%xmm0\n" - "movaps 16(%0), %%xmm1\n" - "movaps 32(%0), %%xmm2\n" - "movaps 48(%0), %%xmm3\n" - "movntps %%xmm0, (%1)\n" - "movntps %%xmm1, 16(%1)\n" - "movntps %%xmm2, 32(%1)\n" - "movntps %%xmm3, 48(%1)\n" - :: "r" (from), "r" (to) : "memory"); - ((const unsigned char *)from)+=64; - ((unsigned char *)to)+=64; - } -#else - // Align destination at BLOCK_SIZE boundary - for(; ((int)to & (BLOCK_SIZE-1)) && i>0; i--) - { - __asm__ __volatile__ ( -#ifndef HAVE_MMX1 - PREFETCH" 320(%0)\n" -#endif - "movq (%0), %%mm0\n" - "movq 8(%0), %%mm1\n" - "movq 16(%0), %%mm2\n" - "movq 24(%0), %%mm3\n" - "movq 32(%0), %%mm4\n" - "movq 40(%0), %%mm5\n" - "movq 48(%0), %%mm6\n" - "movq 56(%0), %%mm7\n" - MOVNTQ" %%mm0, (%1)\n" - MOVNTQ" %%mm1, 8(%1)\n" - MOVNTQ" %%mm2, 16(%1)\n" - MOVNTQ" %%mm3, 24(%1)\n" - MOVNTQ" %%mm4, 32(%1)\n" - MOVNTQ" %%mm5, 40(%1)\n" - MOVNTQ" %%mm6, 48(%1)\n" - MOVNTQ" %%mm7, 56(%1)\n" - :: "r" (from), "r" (to) : "memory"); - ((const unsigned char *)from)+=64; - ((unsigned char *)to)+=64; - } -// printf(" %d %d\n", (int)from&1023, (int)to&1023); - // Pure Assembly cuz gcc is a bit unpredictable ;) - if(i>=BLOCK_SIZE/64) - asm volatile( - "xorl %%eax, %%eax \n\t" - ".balign 16 \n\t" - "1: \n\t" - "movl (%0, %%eax), %%ebx \n\t" - "movl 32(%0, %%eax), %%ebx \n\t" - "movl 64(%0, %%eax), %%ebx \n\t" - "movl 96(%0, %%eax), %%ebx \n\t" - "addl $128, %%eax \n\t" - "cmpl %3, %%eax \n\t" - " jb 1b \n\t" - - "xorl %%eax, %%eax \n\t" - - ".balign 16 \n\t" - "2: \n\t" - "movq (%0, %%eax), %%mm0\n" - "movq 8(%0, %%eax), %%mm1\n" - "movq 16(%0, %%eax), %%mm2\n" - "movq 24(%0, %%eax), %%mm3\n" - "movq 32(%0, %%eax), %%mm4\n" - "movq 40(%0, %%eax), %%mm5\n" - "movq 48(%0, %%eax), %%mm6\n" - "movq 56(%0, %%eax), %%mm7\n" - MOVNTQ" %%mm0, (%1, %%eax)\n" - MOVNTQ" %%mm1, 8(%1, %%eax)\n" - MOVNTQ" %%mm2, 16(%1, %%eax)\n" - MOVNTQ" %%mm3, 24(%1, %%eax)\n" - MOVNTQ" %%mm4, 32(%1, %%eax)\n" - MOVNTQ" %%mm5, 40(%1, %%eax)\n" - MOVNTQ" %%mm6, 48(%1, %%eax)\n" - MOVNTQ" %%mm7, 56(%1, %%eax)\n" - "addl $64, %%eax \n\t" - "cmpl %3, %%eax \n\t" - "jb 2b \n\t" - -#if CONFUSION_FACTOR > 0 - // a few percent speedup on out of order executing CPUs - "movl %5, %%eax \n\t" - "2: \n\t" - "movl (%0), %%ebx \n\t" - "movl (%0), %%ebx \n\t" - "movl (%0), %%ebx \n\t" - "movl (%0), %%ebx \n\t" - "decl %%eax \n\t" - " jnz 2b \n\t" +//3DNOW versions +#ifdef COMPILE_3DNOW +#undef RENAME +#define HAVE_MMX +#undef HAVE_MMX2 +#define HAVE_3DNOW +#define ARCH_X86 +#define RENAME(a) a ## _3DNow +#include "aclib_template.c" #endif - "xorl %%eax, %%eax \n\t" - "addl %3, %0 \n\t" - "addl %3, %1 \n\t" - "subl %4, %2 \n\t" - "cmpl %4, %2 \n\t" - " jae 1b \n\t" - : "+r" (from), "+r" (to), "+r" (i) - : "r" (BLOCK_SIZE), "i" (BLOCK_SIZE/64), "i" (CONFUSION_FACTOR) - : "%eax", "%ebx" - ); +#endif // CAN_COMPILE_X86_ASM + - for(; i>0; i--) - { - __asm__ __volatile__ ( -#ifndef HAVE_MMX1 - PREFETCH" 320(%0)\n" +inline void * fast_memcpy(void * to, const void * from, size_t len) +{ +#ifdef RUNTIME_CPUDETECT +#ifdef CAN_COMPILE_X86_ASM + // ordered per speed fasterst first + if(gCpuCaps.hasMMX2) + fast_memcpy_MMX2(to, from, len); + else if(gCpuCaps.has3DNow) + fast_memcpy_3DNow(to, from, len); + else if(gCpuCaps.hasMMX) + fast_memcpy_MMX(to, from, len); + else +#endif //CAN_COMPILE_X86_ASM + memcpy(to, from, len); // prior to mmx we use the standart memcpy +#else +#ifdef HAVE_MMX2 + fast_memcpy_MMX2(to, from, len); +#elif defined (HAVE_3DNOW) + fast_memcpy_3DNow(to, from, len); +#elif defined (HAVE_MMX) + fast_memcpy_MMX(to, from, len); +#else + memcpy(to, from, len); // prior to mmx we use the standart memcpy #endif - "movq (%0), %%mm0\n" - "movq 8(%0), %%mm1\n" - "movq 16(%0), %%mm2\n" - "movq 24(%0), %%mm3\n" - "movq 32(%0), %%mm4\n" - "movq 40(%0), %%mm5\n" - "movq 48(%0), %%mm6\n" - "movq 56(%0), %%mm7\n" - MOVNTQ" %%mm0, (%1)\n" - MOVNTQ" %%mm1, 8(%1)\n" - MOVNTQ" %%mm2, 16(%1)\n" - MOVNTQ" %%mm3, 24(%1)\n" - MOVNTQ" %%mm4, 32(%1)\n" - MOVNTQ" %%mm5, 40(%1)\n" - MOVNTQ" %%mm6, 48(%1)\n" - MOVNTQ" %%mm7, 56(%1)\n" - :: "r" (from), "r" (to) : "memory"); - ((const unsigned char *)from)+=64; - ((unsigned char *)to)+=64; - } -#endif /* Have SSE */ -#ifdef HAVE_MMX2 - /* since movntq is weakly-ordered, a "sfence" - * is needed to become ordered again. */ - __asm__ __volatile__ ("sfence":::"memory"); -#endif -#ifndef HAVE_SSE - /* enables to use FPU */ - __asm__ __volatile__ (EMMS:::"memory"); -#endif - } - /* - * Now do the tail of the block - */ - if(len) small_memcpy(to, from, len); - return retval; +#endif //!RUNTIME_CPUDETECT } - -#endif /* #if defined( HAVE_MMX2 ) || defined( HAVE_3DNOW ) || defined( HAVE_MMX ) */ -#endif /* USE_FASTMEMCPY */ +#endif /* use fastmemcpy */ \ No newline at end of file
--- a/libvo/aclib_template.c Sun Dec 09 11:52:29 2001 +0000 +++ b/libvo/aclib_template.c Sun Dec 09 15:25:11 2001 +0000 @@ -1,19 +1,8 @@ -#include "../config.h" - -#ifdef USE_FASTMEMCPY /* aclib - advanced C library ;) This file contains functions which improve and expand standard C-library */ -#include <stddef.h> - -#define BLOCK_SIZE 4096 -#define CONFUSION_FACTOR 0 -//Feel free to fine-tune the above 2, it might be possible to get some speedup with them :) - -//#define STATISTICS - #ifndef HAVE_SSE2 /* P3 processor has only one SSE decoder so can execute only 1 sse insn per @@ -74,7 +63,6 @@ // 3dnow memcpy support from kernel 2.4.2 // by Pontscho/fresh!mindworkz -#if defined( HAVE_MMX2 ) || defined( HAVE_3DNOW ) || defined( HAVE_MMX ) #undef HAVE_MMX1 #if defined(HAVE_MMX) && !defined(HAVE_MMX2) && !defined(HAVE_3DNOW) && !defined(HAVE_SSE) @@ -106,6 +94,7 @@ : "memory");\ } +#undef MMREG_SIZE #ifdef HAVE_SSE #define MMREG_SIZE 16 #else @@ -113,6 +102,8 @@ #endif /* Small defines (for readability only) ;) */ +#undef PREFETCH +#undef EMMS #ifdef HAVE_K6_2PLUS #define PREFETCH "prefetch" /* On K6 femms is faster of emms. On K7 femms is directly mapped on emms. */ @@ -122,19 +113,21 @@ #define EMMS "emms" #endif +#undef MOVNTQ #ifdef HAVE_MMX2 #define MOVNTQ "movntq" #else #define MOVNTQ "movq" #endif +#undef MIN_LEN #ifdef HAVE_MMX1 #define MIN_LEN 0x800 /* 2K blocks */ #else #define MIN_LEN 0x40 /* 64-byte blocks */ #endif -void * fast_memcpy(void * to, const void * from, size_t len) +static inline void * RENAME(fast_memcpy)(void * to, const void * from, size_t len) { void *retval; size_t i; @@ -360,7 +353,3 @@ if(len) small_memcpy(to, from, len); return retval; } - - -#endif /* #if defined( HAVE_MMX2 ) || defined( HAVE_3DNOW ) || defined( HAVE_MMX ) */ -#endif /* USE_FASTMEMCPY */