View Full Version : Thoughts on CPU optimization
Richard Berg
5th July 2002, 09:43
There are a variety of methods already in use to accomplish this.
(1) ConvertToYUY2 tests against a bool (created upon construction) every frame, then calls different functions
(2) For prefetch support, Merge #defines a macro, then adds in a couple extra ASM instructions as necessary.
(3) For MMX & SSE support, Merge tests in the c-tor then assumes all's well from then on.
A quick pro/con of each...
Method #1:
-allows multiple versions in 1 binary
-inefficient depending on how often you have to branch
-linear code bloat with every new optimization, even if the difference between implementations is minimal (as with prefetch)
Method #2:
-requires multiple binaries to be built with every release, i.e. more work for both package maintainers & casual downloaders
-no speed overhead, all work done by preprocessor
-code bloat minimized to actual diffs, but may reduce readability depending on complexity
Method #3:
-only 1 binary required
-minimal overhead
-no code snafus (as easy to read as ASM gets)
-screws users who have the "wrong" CPU
I don't think we need to "standardize" on one method throughout -- the advantages of each are better suited to different parts of the code. However, these choices have implications for the project as a whole -- method #2's are obvious, and I'll go ahead and say I'm looking for input on what to do about Merge.
More generally, what to do about internal features that may be called by a variety of filters? For instance, dividee's script add-ons made me think about adding optimized versions of some common math functions to the ones inlined in internal.h. In addition to the pro/cons above, different methods would determine how you'd call these functions...
(a) simple functions with separate versions e.g. FastSin_3dnow() -> testing done by caller each time
(b) simple functions e.g. FastSin() where the caller would pass an argument to determine codepath -> testing done by caller, more testing by callee each time
(c) simple functions with #defines inside -> no testing, just compile-time tweaking
(d) create a FastMath object which does its own tests with each call -> testing done by callee each time
(e) create objects that derive from FastMath, e.g. 'FastMath myMath = new FastMath_SSE;' -> testing done by callee only upon creation of object
Now that I've thought the options through, (e) is very appealing, especially to the part of me that was taking OO-design classes not too long ago. It's not something I can just implement by fiat, though -- would it be useful to you in this form? Could this model solve some of the issues with bigger tasks like filters? Any other comments?
Might as well give some links as long as I'm spamming the forum:
(Official PDFs)
IA-32 Intro (ftp://download.intel.com/design/Pentium4/manuals/24547007.pdf)
Athlon Optimization Manual (http://www.amd.com/us-en/assets/content_type/white_papers_and_tech_docs/22007.pdf)
P4 Optimization Manual (ftp://download.intel.com/design/Pentium4/manuals/24896606.pdf)
(Websites)
Win32ASM board (http://board.win32asmcommunity.net/)
Iczelion (http://spiff.tripnet.se/~iczelion/)
Webster (http://webster.cs.ucr.edu/)
trbarry
5th July 2002, 19:16
(e) create objects that derive from FastMath, e.g. 'FastMath myMath = new FastMath_SSE;' -> testing done by callee only upon creation of object
(e) is somewhat appealing if it could be made to work (for more than FastMath, though it's not really what I've been doing so far.
In my own filters I've mostly been checking each frame for CPU type and branching to appropriate copies of the needed functions. In order to do this in, say, UnFilter, I used a bunch of macros and generated different copies of C functions for different assembly expansions.
But I've mostly kept those functions to C instead of C++, both for Avisynth/DScaler code compatibility and because I'm not a very sophisticated C++ programmer. I wasn't sure how to generate multiple C++ functions of the same name in different members and not have the darn names be global, confusing the linker. But I suppose any decent C++ programmer already knows the answer to that one, just not me. ;)
But it would certainly be nice if you could come up with a working version of (e) that could make the choice at object creation time and still allow me to link together multiple copies of the same functions, compiled with different #def's for different CPU's.
BTW, note that generated code size is basically irrelevant for assembler functions these days, so I've not even considered that in recent design. I even have totally separate copies of assembler things just for different combinations of user parms. It would be neat of (e) could allow for that too somehow. To the extent it did'nt overly complicate things there are probably a lot of filters where the runtime speed vs code size tradeoffs would justify selecting one of a large number of copies of a filter at object creation time.
- Tom
Richard Berg
5th July 2002, 19:53
Obviously code size in the sense of 'how big is my DLL' doesn't/shouldn't matter -- I just meant the number of lines of code and its effects on readability, maintainability, and so on. Having a setup such that every time you fix a bug the patch has to be propagated across 6 different-but-very-similar functions is just asking for trouble.
The FastMath idea itself is dying pretty quick -- there aren't special versions of all the transdescental functions like I thought, just Sqrt & Inverse (& Inverse-Sqrt) in 3dnow & SSE. Even in those cases, there's not too much speedup when doing scalar operations, which is about all you can support in a simple function unless someone has an idea of what kind of generalized vector ops could be used often enough to justify their complexity. Moreover, I can't find any examples of needing transdescentals in time-critical code -- they appear to be called at most once per frame.
BitBlt looks to be a much better candidate for optimization. Not only is the actual optimization better (lots of speedup with just MMX, tons more with K6/Athlon/P3 prefetch, ample source-code help in the aforementioned manuals), but even if it's only invoked once per frame that's still a nice speedup since it's so intensive to begin with.
dividee
5th July 2002, 19:56
About the first part of your post:
I dont know why you are concerned by the speed overhead. Filters usually have to handle hundreds of thousand of pixels at each call, compared to that the overhead you mention is unsignifcant.
Anyway, here is another method: Do CPU detection and set up functions pointer in the constructor, and use preprocessor features to produce multiple versions of the same functions, a bit like with no_next_pixel in convert_a.asm (MASM Macros are better suited for this task than the C++ Preprocessor).
- low speed overhead (one level of indirection for the function call)
- allows multiple versions in one binary
- linear code bloat for the compiler, limited to actual diff for the developer, but reduced readability.
About the second part, of course e) is the sexier solution. I'm not too sure it would be really useful at first, but it might be a good thing to have in the long run.
While we're at it, does anyone has a good link about SIMD (MMX,SSE,3DNow!,...) optimisations ? I'm particularly interested into instruction ordering to optimize pipelining but reading Intel docs makes me feels like I must becomes a real hardware guru to understand this.
[edit:] oops those two previous posts weren't here when I started to write this.
poptones
5th July 2002, 20:39
Woohoo! Hardware!
Seriously, anything that makes me feel like I'm writing code for a c64 is, to me, a good thing.
Richard, check this out.
http://www-106.ibm.com/developerworks/library/l-rt3/
The guy goes through all sorts of tests and, in the end, determines "running memcpy for large transfers is probably a good thing."
I love doing assembly tweaks and such. bitblt was one of the first things I looked at, but I really don't think there's much left to do there, except just to avoid calling it as much as one can.
Richard Berg
5th July 2002, 20:54
0x8048ec0 : xor %esi,%esi
0x8048ec2 : cmp %edi,%esi
0x8048ec4 : mov 0xffffffdc(%ebp),%ecx
0x8048ec7 : mov 0xffffffd8(%ebp),%edx
0x8048eca : jae 0x8048f87
0x8048ed0 : mov (%edx),%eax
0x8048ed2 : add $0x4,%esi
0x8048ed5 : mov %eax,(%ecx)
0x8048ed7 : add $0x4,%edx
0x8048eda : add $0x4,%ecx
0x8048edd : cmp %edi,%esi
0x8048edf : jb 0x8048ed0
That's the weakest "optimization" I've ever seen.
Richard Berg
5th July 2002, 22:56
The code:
PVideoFrame __stdcall MemTester::GetFrame(int n, IScriptEnvironment* env)
{
PVideoFrame src = child->GetFrame(n, env);
Pixel32 *srcp = (Pixel32 *) src->GetReadPtr();
PVideoFrame dst = env->NewVideoFrame(vi);
char *dstp = (char *) dst->GetWritePtr();
memcpy (dstp, srcp, vi.height * vi.width * 4);
//fastmemcpy (dstp, srcp, vi.height * vi.width * 4);
return dst;
}
The script:
avisource("l:\fuckedup.avi")
ConvertToRGB32
MemTester.MemTester.MemTester.MemTester.MemTester.MemTester.MemTester.MemTester.MemTester.MemTester
MemTester.MemTester.MemTester.MemTester.MemTester.MemTester.MemTester.MemTester.MemTester.MemTester
The results:
Time to direct stream copy 300 frames
Memcpy from standard C++ libraries - 3:50
Fastmemcpy using MMX & block prefetch - 0:26
Even I didn't expect it to go that fast -- I think I may have been limited by network bandwidth, not throughput!
dividee
5th July 2002, 23:04
Ouch!
Are you sure about your results? Sorry to ask, but it seems to good to be true.
Did you time the script with only AVIsourve and ConvertToRGB32 ?
Richard Berg
5th July 2002, 23:20
The script was exactly the same in both cases. Only difference was I commented out a different line & recompiled.
After I decided it might be fast enough to be pushing network bandwidth, I tested with just AVISource - 0:11. Adding ConvertToRGB32 upon your suggestion - 0:14.
Back-of-the-envelope calculations:
640 x 480 x 4 x 300 x 20 / 230 = 32 MB/s with memcpy
Achieving 10X that should be very possible; my CPU's throughput with 100% cache hits should only be limited by memory bandwidth, which for DDR is 2.1GB/s.
trbarry
6th July 2002, 00:30
I thought the M$ VS6 compiler would already use a fast machine specific memcpy() if you let it. But I don't remember how you would specify this.
- Tom
dividee
6th July 2002, 01:05
I've run some tests and my results differs from yours. I've made a filter which calls BitBlt 10 times (with same source and destination), and I used BlankClip as source.
I get 640 x 480 x 4 x 2000 x 10 / 73 = 320 MB/s with memcpy()
and about 500 MB/s using a simple MOVQ/MOVNTQ pair. (DDR2100)
Richard Berg
6th July 2002, 12:02
I've made a filter which calls BitBlt 10 times (with same source and destination), and I used BlankClip as source.
I tried that at first -- putting memcpy in a loop, actually. I got the same results even when I put the loop counter to 1000, making me think the compiler was optimizing it away since multiple copies are essentially a no-op.
sh0dan
6th July 2002, 15:09
Personally I'm using a method of doing defines around different parts of the code. I'm doing it by haveing the assembler code in a seperate file, that is included several times, but with different defines (and different method names depending on the defines). So far I haven't had any problems using this method.
I'm currently looking at places in Avisynth for important speedups - depending on how often they are used by ordinary people. I seem to remember that I found that either ConverttoYUY2 or ConverttoRGB() is complely unoptimized, and since they are very often used, it is quite obvious. Also an optimized naiive spatial blur (that blurs no matter what, like the built-in Blur()) could be quite useful.
Any thoughs on other important Avisynth hot-spots that could be nice to have optimized?
But since I still have the avisynth smooth hiq and have plans for a deflicker filter for 50fps video for vdub it may take a while. <sigh> ;)
Richard Berg
6th July 2002, 16:11
Personally I'm using a method of doing defines around different parts of the code. I'm doing it by haveing the assembler code in a seperate file, that is included several times, but with different defines (and different method names depending on the defines). So far I haven't had any problems using this method.
Sounds kind of like what dividee was talking about; if I can figure out how to generalize it, sounds like a winner. /runs off to download Merge 1.1 source, since there was no automatic-#define-#include magic in the 1.0 source I integrate...
The Blur() idea is a good one, and should be possible to "rip" directly from VDub.
ConvertTo/FromYUY2 are in MMX as far as I can tell, though there are probably obvious SSE speedups available.
If what I saw with my little fastmemcpy test is any indication, just about every filter that's memory-bound (probably quite a few on fast CPUs) could see huge speedups with prefetching. The "easy" way requires PREFETCHNTA (SSE) or PREFETCHW (3DNow), but for very large blocks (>8KB) you can hack a manual prefetch routine that's actually faster than SSE/3DNow prefetch -- the CPU can't ignore MOV instructions ;)
prefetchloop:
mov ebx, [esi+ecx*8] // Read one address in line,
mov ebx, [esi+ecx*8+64] // and one address in the next.
add ecx, 16 // add 16 QWORDS, = 2 64-byte
// cache lines
dec eax
jnz prefetchloop
sub ecx, CACHEBLOCK
mov eax, CACHEBLOCK / 8
Of course, it's best to use MOVNTQ during the writes to avoid polluting your hacked-up cache (which is what fastmemcpy does), but full MMX compatibility could be achieved using MOVQ without sacrificing all benefits of prefetch.
Also, the manual method requires you to tweak the algorithm for the size of the L1 cache lines & capacity on a given CPU.
Athlon: 64-byte cache line / 64KB size (data cache; 128KB total)
P6: 32b / 16KB
K6: 64b / 32KB
P4: 64b / 8KB
sh0dan
6th July 2002, 16:41
I'm only doing the include trick in smooth hiq, since merge is too simple for that. I decided not to do an athlon specific function - mostly out of laziness - but the prefecth stuff is there - just #define ATHLON.
I was thinking of ConvertToYUY2 - in the version I've got, there doesn't seem to be any optimizations, RGB. It includes a lot of muls, so using properly placed pmaddwd would should be able to speed up this code somewhat - and memory access should also be reduced greatly by using the mmx-registers properly.
The fractions should however be reduced to 15 bits instead of 16 bits, to avoid 16bit signed overflows.
Regarding prefetch, it can be quite tricky - using mov can be a bad thing, since it stalls until the read is completed. Prefetch does not stall, if the data is not yet retrieved. AMD has a prefetch routine in their AMD optimization guide, that (according to their claims) should be the best available.
Also I'm quite reluctant to use movntq, since I almost always have to read again from the same cache line (the next pixel). So if I'm using movntq I get a heavy read penalty, since the data has to be fetched from memory every time. Is this a stupid assumption?
Richard Berg
6th July 2002, 17:04
Not at all - I was talking about the code for a simple memcpy. Won't work in a filter that's actually manipulating stuff.
You're right about ConvertToYUY2, by the way. If you're registered on SourceForge (and if not, fix that! ;)), give me your Unix name and I'll make it so you can patch convert.cpp directly when you get the chance.
Regarding prefetch, it can be quite tricky - using mov can be a bad thing, since it stalls until the read is completed. Prefetch does not stall, if the data is not yet retrieved. AMD has a prefetch routine in their AMD optimization guide, that (according to their claims) should be the best available.
That's why it should only be done for really big blocks, when you're going to be limited by the memory bus no matter what. "My" algorithm is from the same manual, actually.
I'll definitely take a look at sshiq...
sh0dan
6th July 2002, 18:00
I can bring you an in-progress version of the filter so you can see how it's done - it's just an experiment, but it seems to work. Here's how:
Create another file (assembler_templates.cpp for instance), include it in your project, but make VC++ exclude it from the build.
Instead of adding the methods in your filter in your filter source use:
#define ATHLON
#include "assembler_templates.cpp"
#undef ATHLON
#include "assembler_templates.cpp"
Your assembler file then contains:
#ifdef ATHLON
void SmoothHiq::ATH_mmx_blend(param...param...) {
#else
void SmoothHiq::mmx_blend(param...param...) {
#endif
#ifdef ATHLON
prefetch [eax]
#endif
movq mm0,[eax]
.... and so on. Then you just call the correct routine, when you have detected the CPU-capabilities.
Actually IMO a better way, than copy/pasting the same code, and changing defines. This way you get all versions from the "same" code, so you only have to change it once - debugging works perfectly. To my surprise I've found no disadvantages to this method, and you don't have to do strange defines.
Richard Berg
6th July 2002, 19:18
Me likes. Simple, if not elegant -- but this ain't abstract algebra class, it's engineering.
Richard Berg
8th July 2002, 02:05
I'd ask for code review too, but trust me, only masochists need apply. I've hacked together a library of CPU-specific functions -- a small library, right now just consisting of special versions of memcpy & memset, but hopefully one that will set the foundation for things to come.
The software architecture is a combination of the best ideas from this thread. On the level of Fastlib.cpp and 'higher' it's quite nice, with fully granular yet OO support for basically every CPU out there (the included memory routines come in 6 preprocessor-packaged flavors), but it comes at a cost: I would strongly advise not looking at Cpulib.h while operating heavy machinery.
Of course, nobody will care unless there's worthwhile performance to be gained by this approach. That's where you come in. I've written a bunch of code for a big variety of CPUs, but am only able to test on one architecture (Athlon 1.4 / DDR). P4 users especially wanted -- for obvious reasons I don't even know if the SSE2 codepaths work at all.
All varieties of testing are very welcome though; we'll soon find out if I know as much about cache layouts as I think I do. Basically, I need you to verify that the routine earmarked for your CPU is in fact the fastest one (that you can run w/o generating 'illegal instruction'). I.e., on my machine Athlon > P3 > K6 > P2, but on a P3 it should be the reverse.
Instructions: extract EXE file. Run EXE file. Choose option. Repeat a couple times for each option so you know what results are anomalous and can be ignored -- an OS preemption is relatively common, happening most often (to the point of unavoidability) in the big tests but skewing the data the most during small tests...should establish a norm in each category of a few percent at most. Report.
Milkman Dan
8th July 2002, 02:51
Uhm...was there an attachment, or is this on a site somewhere?
I'm really really really glad to see this sort of action on Avisynth. It gives me a warm fuzzy feeling. :D
Richard Berg
8th July 2002, 03:08
Apparently it takes awhile to show up. I know I attached the file, because the first time around I forgot, realized it as soon as I hit "post," deleted the post, and reposted.
trbarry
8th July 2002, 03:51
I also have a few macros (from DScaler) that I use to generate the moderately few integer MMX/SSE/3DNOW assembler instructions that are both useful and different on these platforms.
So when I code, say, a pavgb instruction, I actually code V_PAVGB and it picks up the value of #defines, like discussed above and makes the right instruction(s).
But doing this more closely allows me to just code something once but compile it in flavors.
And I see the mod still hasn't noticed the attachment above.
- Tom
Milkman Dan
8th July 2002, 04:04
I'll see if I can't flag one down...a mod that is.
EDIT: Looks like I just missed Dividee.
dividee
8th July 2002, 05:10
You guys are so impatient ! :D
Milkman Dan
8th July 2002, 06:42
This is fun!
System is a single Athlon MP 1.2GHz (Palomino core) @ 1520MHz (145x10.5) with DDR memory, CAS 2.5, low latency settings on a Soyo Dragon+.
OS is Win2kSP2.
For option 3 (Athlon)
the average is:
2051.5 6400.5 7937.1 2129.9 1055.2 1049.5 fmemcpy
3393.4 9004.4 11593.1 4553.9 2208.7 2167.8 fmemset
For option 1 (stdlib.h)
The average is:
1944.5 3973.6 4353.5 1491.9 405.1 398.9
2107.8 5060.1 5738.0 3265.1 644.5 634.9
For Option 5 (PIII)
The average is:
1826.1 5651.9 7721.5 2204.6 1034.3 1028.1
3113.9 8803.9 11593.1 4452.0 2202.6 2168.5
poptones
8th July 2002, 06:43
I don't wanna be a damp rag, but this is pretty much the kind of stuff I found before. At least with a PII/MMX (the only thing I can test, unless you might find "Pentium classic" results of interest) and at relvant sizes (where am I gonna swap 100 bytes 10,000,000 times?) there's very little repeatable difference. I'll be interested in seeing what differences athlons and P4s make (it might even finally convince me to upgrade, 'tho I don't know that would necessarily be a good thing).
[1] <stdlib.h>
Block size: 100b 1KB 10KB 100KB 1MB 10MB
----------------------------------------------------------------------------------
Throughput (MB/s): 406.9 1190.9 477.9 283.2 157.0 153.7 fMemcpy
385.8 1249.9 2406.8 384.8 198.6 187.1 fMemset
[4] Pentium II
Block size: 100b 1KB 10KB 100KB 1MB 10MB
----------------------------------------------------------------------------------
Throughput (MB/s): 399.6 1112.3 487.2 301.3 142.1 141.2 fMemcpy
612.6 1746.3 2134.4 386.0 199.3 211.1 fMemset
EDIT:
Wow, that's cool. An athlon running about 3.5x my system (a PII/400 running on a 112.5MHz bus) is ten times faster!
Would you mind posting the PII optimized results? Just so we can see how the architecture itself compares using these "antiquated" optimizations?
Milkman Dan
8th July 2002, 06:53
Sure, hold on a sec.
EDIT:
The numbers are:
1918.7 5590.2 7077.0 1957.2 447.7 433.4
3733.2 9004.4 11630.3 4589.4 696.0 688.2
dividee
8th July 2002, 07:13
Athlon XP 1800+ ,DDR@266 CAS2.5, Abit KR7A, Win2K SP2
MEMCPY 100b 1KB 10KB 100KB 1MB 10MB
stdlib 1987 4006 4431 1544 391 384
K6 2217 6624 8144 2131 440 427
Ahtlon 2096 6540 8020 2010 980 980
P II 1962 5700 7234 2006 440 432
P III 1866 5775 7819 2030 960 950
MEMSET
stdlib 2155 5174 5825 3319 648 632
K6 4240 9373 11750 4650 724 710
Ahtlon 3470 9214 11853 4700 2040 2000
P II 3815 9214 11800 4700 725 710
P III 3182 8995 11850 4675 2040 2005
Pentium III 650 @ 866, SDR@133 CAS2, Asus CUSL2-C, Win2K SP2
MEMCPY 100b 1KB 10KB 100KB 1MB 10MB
stdlib 758 2240 2280 1586 151 153
Ahtlon 660 2910 2105 805 318 320
P II 726 2408 2130 1612 161 160
P III 640 2420 2036 859 453 455
MEMSET
stdlib 720 2260 4560 2160 244 236
Ahtlon 1143 2466 4232 2200 720 615
P II 1207 3337 4387 2158 234 230
P III 1059 2380 4172 2200 676 623
Swede
8th July 2002, 07:13
These are my findings on a P4@1.5GHz:
[1] stdlib:
525.2 2862.2 3862.3 4215.6 289.5 292.0 fMemcpy
899.5 3631.9 6282.6 870.7 454.4 402.2 fMemset
[4] PII
512.7 2688.8 3196.4 1051.8 261.0 255.2 fMemcpy
1604.9 3984.6 4853.4 2854.6 361.4 366.5 fMemset
[5] PIII
494.1 2018.6 3559.3 699.5 454.6 447.7 fMemcpy
184.8 2190.5 4343.7 2515.6 896.3 895.5 fMemset
[6] PIV
680.3 3335.2 276.9 423.8 279.8 276.2 fMemcpy
219.7 1718.4 5299.6 844.6 881.5 888.1 fMemset
It's interesting that my P4 is <0.5x Milkman Dan's Athlon... :rolleyes:
poptones
8th July 2002, 07:25
Very interesting. So the PII mmx enhancements scale almost directly proportional to clock across the platforms, and there's little "added" beyond the PIII enhancements?
Hmmm. Maybe it's time to obtain a slot1 PIII/750 or so. Looks like it might be a really worthwhile and cheap upgrade.
Milkman Dan
8th July 2002, 08:37
It's interesting that my P4 is <0.5x Milkman Dan's Athlon... :rolleyes:
Well, you might be running SDR ram, in which case I'm not surprised. However, I believe my results were quite verified by Dividee's results...
Richard already pointed out that I doesn't know if the SSE2 code is working correctly. Perhaps it's just a matter of code issue.
My results are quite real though.
Average on my notebook (Celeron 600, SDRAM @ 100 CAS 2.0, Win NT)
fMemcpy
[1] <stdlib.h> 542 1591 1712 1229 173 170
[3] Athlon 469 2039 1679 242 223 221
[4] Pentium II 517 1718 1571 1160 173 169
[5] Pentium III 452 1684 1589 247 243 238
fMemset
[1] <stdlib.h> 514 1665 3211 2363 221 213
[3] Athlon 783 1788 2996 2195 245 329
[4] Pentium II 826 2297 3085 2173 226 223
[5] Pentium III 726 1742 2937 2203 241 241
$
Swede
8th July 2002, 08:56
Well, you might be running SDR ram, in which case I'm not surprised. That's true, I am.. But I didn't know the difference would be that huge. Well, at least this is my PC at work.
Milkman Dan
8th July 2002, 09:06
In very theoretical situations like this one, that "DDR Is Extra Extra Twice as Good, We Swear!" sort of thing actually comes true. This won't really stand out in practical situations though. There's always going to be some part of the code that's slower and hangs things up.
vlad59
8th July 2002, 11:30
I've tried your fastlib test and it crashed on my computer.
I've got a P3-M (laptop) 550-750 (intel speedstep) with 128Mo SDR and W98 SE.
1 - stdlib.h
Throughput (MB/s): 631.7 1082.7 1120.0 392.2 128.3 135.7 fMemcpy
602.8 890.8 1178.8 1189.1 217.8 162.7 fMemset
4 - PII
I run for 5 or 6 second and it crash (page fault in fastlib.exe).
5 - PIII
same crash
I can do more test at home (on a K6-2 and in a C600 (laptop too)).
EDIT : I tried 3-Athlon on the same computer (P3-M) and it worked perfectly.
manono
8th July 2002, 12:01
I have a P4, but at the risk of seeming an idiot, how do I save the results, as they disappear too quickly?
Milkman Dan
8th July 2002, 12:05
Open a command prompt first, navigate to the directory that has the exe, and then run it. S'all there is to it.
manono
8th July 2002, 13:23
Thanks Dan-it worked like a charm.
System-P4 1.8A Northwood @2500MHz. 512 MB Samsung PC2700 DDR. Asus P4S333 Mobo. WinXP Pro. Each test run 3 times and the results averaged.
stdlib
.......684.7.......4862.1......7710.2......8078.8.....624.6.....619.7
.....1424.9.......5737.7.....10220.1......2910.3....1197.6.....876.2
P3
.......857.3.......3259.9......5092.1......2643.6....1217.6....1197.6
.......235.4.......3281.2......7119.3......8609.8....2645.3....2557.8
P4
.......880.1.......5731.4.......486.2.......721.4.....508.4.......505.6
.......285.0.......2301.2......8602.0.....2643.5....2647.8.....2557.7
sh0dan
8th July 2002, 15:14
Hi Richard & Dividee
I did an experimental MMX optimization of the C code converting RGB32 to YUY2. It's experimental, because I cound't test it on my home computer, since I couldn't compile source.cpp (I'll download the Win2k stuff ASAP).
In THEORY it should do the same as the C-code, but there might very well still be bugs. Implementation notes:
- Algorithm is the same, some contants has been scaled down to fit into 16 bit signed words.
- I assumed that no intermediate values could be negative (bitshifting negative values would be a strange thing to do).
- I'm not 100% sure that the order of psubd registers - they may need a swappin'
- The code is Integer SSE, but converting it to MMX should be trivial (only one pshufw instruction).
- Code may not be as readable as could be, since it is made with maximum instruction pairing in mind.
- Check the constant data alignment - it must be aligned to 8, otherwise it will give a massive penalty (code included to test it in debug mode).
- You need to add the method to convert.h, and implement a CPU-test in the original GetFrame (trivial).
(convert.cpp is attached, so it has to be activated)
trbarry
8th July 2002, 17:41
Fastlib also crashes on my Pentium III 866, PC133, Asus CUSL2, Win/Me.
It will work with the std lib but not if I choose P-II or P-III.
- Tom
edit: crashes on my P4T-E also if I choose P-II or P-III. But choosing P4 works.
sh0dan
8th July 2002, 18:11
Can this "Win2000 platform SDK" be downloaded from anywhere - I can only find an online installer - and my home computer is not online :(
Richard Berg
8th July 2002, 18:38
http://www.microsoft.com/msdownload/platformsdk/sdkupdate/psdk-full.htm
I'll pore over the numbers and make some tweaks...so far it looks obvious that a lot of the tricks I found worked well on the Athlon really suck on other CPUs despite a couple adjustments.
Acaila
8th July 2002, 18:39
While you people are moving code around, do you think there's any chance for multithreading optimization?
Because currently the more filters I put into avisynth to process, the less total CPU power my system uses.
I might as well post a test report:
fmemcpy:
stdlib.h 2162.5 4418.1 4837.1 1680.5 331.3 347.9
K6-2 or K6-3 2412.3 7207.5 9684.3 2339.0 366.7 367.7
Athlon 2280.1 7114.9 9532.8 1546.6 788.5 789.6
P II 2134.2 6213.4 7863.4 2177.5 362.8 368.2
P III 2030.1 6282.2 8446.7 1643.3 737.3 750.8
fmemset:
stdlib.h 2345.1 5626.3 6536.3 3542.6 534.0 531.5
K6-2 or K6-3 4375.7 10208.4 12927.0 5150.9 676.4 671.6
Athlon 3780.3 10026.3 12882.7 4917.4 1618.4 1601.3
P II 4158.4 10025.1 12927.1 5120.1 675.3 672.4
P III 3465.8 9789.5 12887.4 5058.4 1620.6 1603.4
Ps.
Why is Athlon slower than K6-2/K6-3?? (except for the two largest blocks)
Richard Berg
8th July 2002, 18:59
P4 Northwood 1.8A, i845, 168Mhz DDR
fMemcpy
stdlib.h 632.6 4301.8 7087.3 7202.3 448.1 446.1
K6 [crash, uses 3DNow instructions]
Athlon 797.0 3459.9 4682.5 1899.7 926.6 910.6
P2 735.3 3544.5 5284.2 5393.3 444.0 440.1
P3 769.8 2968.7 3791.6 1901.2 880.3 875.3
P4 744.9 5221.1 377.0 648.0 432.0 438.9
fMemset
stdlib.h 1299.5 5224.2 9374.2 3501.6 701.2 499.1
K6 [crash, uses 3DNow instructions]
Athlon 331.8 2158.4 6777.5 7786.0 1901.3 1805.7
P2 2265.2 6169.0 7733.9 7807.4 574.3 573.4
P3 249.1 3063.9 6772.8 7845.4 1899.0 1834.8
P4 330.8 2182.2 8258.1 1642.7 1901.6 1817.1
P4 Willamette 1.7, PC800 RAMBUS
fMemcpy
stdlib.h 600.0 3224.0 4692.4 4795.0 728.2 705.3
K6 [crash, uses 3DNow instructions]
Athlon 604.0 2722.5 4257.0 2078.8 1096.6 1043.1
P2 435.9 3038.2 4110.3 4103.2 736.0 704.1
P3 516.7 2403.5 4182.1 2098.5 1071.8 1029.2
P4 766.8 3664.0 319.5 524.0 418.0 413.0
fMemset
stdlib.h 1001.7 3941.2 7077.0 2104.6 1101.0 947.4
K6 [crash, uses 3DNow instructions]
Athlon 255.9 1812.9 5122.7 5859.6 2107.6 1951.8
P2 1697.8 4616.9 5868.1 5956.7 858.0 794.0
P3 198.4 2283.0 5123.6 5946.1 2109.8 1952.0
P4 259.4 1912.1 6274.5 2090.6 2110.8 2025.5
Dual P3 933, 4x256 PC800 RAMBUS
fMemcpy
stdlib.h 804.5 2534.0 2685.8 993.3 275.3 264.2
K6 [crash, uses 3DNow instructions]
Athlon 730.9 3182.2 2625.5 447.2 400.7 392.3
P2 803.0 2680.6 2606.6 1815.8 249.8 247.3
P3 707.2 2683.2 1547.4 444.5 427.1 422.5
P4 [crash, uses SSE2 instructions]
fMemset
stdlib.h 801.9 2598.3 5009.0 4009.3 483.2 419.2
K6 [crash, uses 3DNow instructions]
Athlon 1223.4 2550.7 4510.6 3210.1 777.4 766.6
P2 1291.1 3675.1 4196.3 3310.5 347.4 317.7
P3 1126.7 2435.7 4413.9 3219.4 778.6 767.5
P4 [crash, uses SSE2 instructions]
Richard Berg
8th July 2002, 19:09
Why is Athlon slower than K6-2/K6-3?? (except for the two largest blocks)
That's the million dollar question I hope to answer by poring over the numbers.
Of course, if I were smart, I wouldn't have overambitiously combined the various algorithms for this test suite. Although it does work really well on my Athlon, it adds a bunch of guesswork to the analysis of others' results. (Thanks for posting, BTW - saves me from doing so since mine are almost exactly the same, not surprising since I have basically the same setup).
'Tho I've never built a speaker, the best analogy is probably to crossover design: right now y'all are giving me the frequency response of a 4-way box I just built, and while it'll lend some hints as to tweaks I can make, it would've been much better to get the specs of the individual drivers first (even though it would've shown them in a really bad light, e.g. the tweeter being down 30dB at 50Hz). The good thing about surrounding myself with people who have too much free time is that I can always post another thread :)
sh0dan
8th July 2002, 19:24
Looked at the code, and it seemed like you mixed up K6 and K7. The prefetch is a pure Athlon instruction, and is faster on Athlon, this is currently in the K6 define. prefetch will not work on K6 - prefetchnta is Integer SSE, and will work on K6-III IIRC - the same with movntq.
Haven't tested performance with sfence.
Try to do an Athlon version with prefetch.
It also seems like it would be a good idea to look at the P4 'optimizations' :)
dividee
8th July 2002, 19:41
According to the doc I have prefetch is supported on K6-2 and K6-3 (standard 3DNow!) and Integer SSE only on K7 (no K6-3).
vlad59
8th July 2002, 19:51
IIRC prefetch en prefetchw are available in the 3dnow set (K6, K6-II, K6-III, athlon, and later)
But with the athlon it's possible to make multiple prefetch or prefetchw.
Prefetchnta and movntq were added with the extended 3dnow set (sse integer). I don't think those instructions were usable on k6-III. I'm sure they aren't on a k6-II (I have one).
I'm sure you already know but the "AMD Athlon Processor x86 code optimization guide" explain very clearly the memcopy stuff and all the optimization possible.
EDIT : Dividee was once again faster
Defiler
9th July 2002, 16:13
Originally posted by sh0dan
Any thoughs on other important Avisynth hot-spots that could be nice to have optimized?
But since I still have the avisynth smooth hiq and have plans for a deflicker filter for 50fps video for vdub it may take a while. <sigh> ;)
You're planning an AVIsynth version of the "Deinterlace - Smooth" Virtualdub filter? If so, you will be my personal god.
That, and (sometimes) Avery Lee's subtitler plugin are the only things that keep me from the glorious world of Fast Recompress.
Slightly off topic, but still within the realm of CPU optimization..
DVD2AVI -> AVIsynth -> VirtualDub (Full Processing).. Only uses one CPU on a dual CPU box. Where does the blame for that lie? In Virtualdub?
There are (awful) video encoding applications out there that exploit SMP very well. Xmpeg is an example of this. It would be nice to see something like this kind of scaling while still using the awesome power of AVIsynth. Can you shed any light on this for me? If it's been answered elsewhere, please scatter my ashes after you are done incinerating me.
trbarry
9th July 2002, 16:29
DVD2AVI -> AVIsynth -> VirtualDub (Full Processing).. Only uses one CPU on a dual CPU box. Where does the blame for that lie? In Virtualdub?
Are you sure? I don't have any direct knowledge of this but I'm sure there have been other posts on the board stating that the decoding and encoding can take place in separate tasks in this situation.
But I don't have a link.
- Tom
vBulletin® v3.8.11, Copyright ©2000-2026, vBulletin Solutions Inc.