View Full Version : Internaly multi-threaded resampling functions
Pages :
1
2
3
4
5
6
7
8
[
9]
jpsdr
3rd October 2025, 09:04
Yes, 32 zmm registers is only in x64, it's 8 in x86. ymm is 16 in x64 and 8 in x86 for AVX2, but if i remember properly, you have 32 xmm and ymm in AVX512.
It's logical because xmm/ymm/zmm are in fact only one register. Bits 0..127 of zmm0 or ymm0 are xmm0, bits 0..255 of zmm0 are ymm0.
The other thing, is that in x64 you have to save "only" xmm6 to xmm15. If you're using xmm16 or more, you don't need to save them.
Meaning the dual row vs single row is probably negative for x86 builds, but positive for x64 builds.
This is why i have, in my failed ASM resample attempt, customized the ASM according x86 or x64.
But, in our days, most of people are in x64.
It's easy in ASM to use the 32 registers, you just "have to", i don't know how intrinsics behave.
For AVX512/AVX2, i'll have to check again speed with large filters, but if i remember properly i've allready tried, even with large filters, AVX512 was slower (at least on my CPU).
If there is realy such a threshold, maybe in the init filter, if opt=-1, it can estimate the filter size and choose between AVX512/AVX2 if AVX512 is detected. But this is for a second time.
DTL
3rd October 2025, 10:40
Intrinsics guide shows we have 128/256/512bit wide instructions in AVX512 group. https://www.laruence.com/sse/#techs=AVX_512&text=cvtph_ps . For AVX512 and non-512 datawords (256 and 128 bits) they are 'masked'. But it is easy to init all-bits (static const) mask (in separate from 32 main 8 additional mask registers in AVX512 architecture). It looks the way to mark these instructions for AVX512 architecture with lower data word size.
https://i.ibb.co/JwhsbSgv/image-2025-10-03-124014626.png (https://ibb.co/rRWwLXNx)
I expect all other required instructions also have 'masked' AVX512 form with 256bit data word size.
Though it is just an idea to test. May be other ways exist to force C compiler to use 32 registers in AVX512 x64 mode for 256bit data word size. If it will over-read 16 coeffs (8 next) from coeff_stride=8 buffer we can skip upper half of register at the final operations. It will not degrade performance. Though buffer memory must be still allocated to allow mod16 stride loads without causing page protection errors at the last buffer row reads. If missed this can cause rare and random memory read errors/crashes.
The key idea to use coeffs_stride=8 for filter_size <=8 and process it with 32 registers AVX512 architecture.
jpsdr
3rd October 2025, 14:40
mmask is totaly different, it allows to apply the instruction only on parts of registers. For example, an and, with the mask you can say something : apply on bits 0..15, but leaves unchanged bit 16..31, apply to bits 32..63, etc... Using mmask is also very slow (according Intel doc) vs not using it.
It's a guess, but likely, for compiler, just enabling AVX512 features will grant access to the 32 registers in x64 mode. I've made an 2 rows x4 AVX2 code put in avx512 cpp, i'll push when back home, but test has to wait, not time right now.
DTL
3rd October 2025, 15:02
"2 rows x4 AVX2 code put in avx512 cpp"
If it will work with coef_stride=8 and use up to 32 registers it may be complete solution to best performance. But with coef_stride=16 (and more) the 512bit version expected to be faster. Though 4 planes formats may be (very) rare in use. But currently if AVX512 enabled system sets coef_stride to 16 at
https://github.com/jpsdr/JincResizeMT/blob/750bf15230147de1b9c1746043dbe6fccbe10b6e/Src/JincResizeMT.cpp#L555
https://github.com/jpsdr/JincResizeMT/blob/750bf15230147de1b9c1746043dbe6fccbe10b6e/Src/JincResizeMT.cpp#L1472
. Need some solution here too. With avx512 not enabled (opt=2) it need to call AVX512 function from _avx512.cpp file ?
jpsdr
3rd October 2025, 18:12
With opt=2 and AVX512 autodetected, it will call the function. I've just pushed things.
DTL
3rd October 2025, 21:57
You miss deleting memory for out_fp16 at class destruction (FreeData) - https://github.com/DTL2020/JincResizeMT/blob/5f42a8511efba36d4e9f0660fcb3c913ae3847a3/Src/JincResizeMT.cpp#L1590
With sources compiled with VS2022 and forced AVX512 for _avx512.cpp file the asm listing shows it really uses >16 SIMD registers:
; 1357 : result1_2 = _mm256_fmadd_ps(src_ps1_2, coeff2, result1_2);
vfmadd231ps ymm18, ymm1, ymm3
mov rax, rcx
sub rax, r12
vpmovzxbd ymm0, XMMWORD PTR [rax]
vcvtdq2ps ymm1, ymm0
vpmovzxbd ymm0, XMMWORD PTR [rcx]
; 1358 : result2 = _mm256_fmadd_ps(src_ps2, coeff, result2);
vfmadd231ps ymm5, ymm1, ymm2
vcvtdq2ps ymm1, ymm0
vpmovzxbd ymm0, XMMWORD PTR [r10+rcx]
; 1359 : result2_2 = _mm256_fmadd_ps(src_ps2_2, coeff2, result2_2);
vfmadd231ps ymm19, ymm1, ymm3
vcvtdq2ps ymm1, ymm0
vpmovzxbd ymm0, XMMWORD PTR [rdi+rcx]
; 1360 : result3 = _mm256_fmadd_ps(src_ps3, coeff, result3);
vfmadd231ps ymm16, ymm1, ymm2
vcvtdq2ps ymm1, ymm0
vpmovzxbd ymm0, XMMWORD PTR [r11+rcx]
; 1361 : result3_2 = _mm256_fmadd_ps(src_ps3_2, coeff2, result3_2);
vfmadd231ps ymm20, ymm1, ymm3
vcvtdq2ps ymm1, ymm0
vpmovzxbd ymm0, XMMWORD PTR [rsi+rcx]
; 1362 : result4 = _mm256_fmadd_ps(src_ps4, coeff, result4);
vfmadd231ps ymm17, ymm1, ymm2
add rcx, 8
vcvtdq2ps ymm1, ymm0
; 1363 : result4_2 = _mm256_fmadd_ps(src_ps4_2, coeff2, result4_2);
vfmadd231ps ymm21, ymm1, ymm3
Performance test shows also some better result - about 25 vs 23 fps with AVX512 enabled and SetMaxCPU("AVX2").
Also UserDefined4ResizeSPMT() missed argument 'opt' and used CPU features can not be controlled. It looks was copy from Jinc256Resize(). Why that presets do not have 'opt' ?
Good news - usage of >16 SIMD registers for 256bit words processing at AVX512 CPU do not cause CPU clock trottling.
Also usage of 512bit words processing cause clock trottling from 3.39 to 2.69 GHz at some old Xeon Gold but it still process a bit faster with tap=7 in comparison with AVX2 mode (opt=2).
Asm output of VS2022 C compiler looks not good for performance - it uses chained fma instructions with dependend sources ymm1, ymm2, ymm3. But fma instructions have big latency (like 4..5 clocktics) and good throughput (up to 2 results per clocktick with 2 FMA units ?) and expected to have better performance if used in a group with independent sources (parallel). I hope clang LLVM compiler may make better result. But with VS2022 clang LLVM can not found include file for AVX2 and other SIMD headers and fail to compile. Need to found why it is. I have same issue with some other AVS plugin projects.
Addition: LLVM build with VS2022 is fixed by adding AVX2 and AVX512 direct setting in the _avx2.cpp and _avx512.cpp files compile settings.
I see addition of
#if defined(CLANG)
__attribute__((__target__("avx2")))
#endif
in program text but it looks not working with VS2022 and LLVM compiler. Only direct setting of Enable Enhanced Instruction Set -> AVX2 (/arch:AVX2) in the project setting for .cpp file is working.
And it looks it make more expected asm with grouped fma instructions at the end of loop as expected:
vpmovzxbd ymm18, qword ptr [r8 + rbx] # ymm18 = mem[0],zero,zero,zero,mem[1],zero,zero,zero,mem[2],zero,zero,zero,mem[3],zero,zero,zero,mem[4],zero,zero,zero,mem[5],zero,zero,zero,mem[6],zero,zero,zero,mem[7],zero,zero,zero
vpmovzxbd ymm19, qword ptr [rcx + rbx] # ymm19 = mem[0],zero,zero,zero,mem[1],zero,zero,zero,mem[2],zero,zero,zero,mem[3],zero,zero,zero,mem[4],zero,zero,zero,mem[5],zero,zero,zero,mem[6],zero,zero,zero,mem[7],zero,zero,zero
vcvtdq2ps ymm18, ymm18
vcvtdq2ps ymm19, ymm19
vpmovzxbd ymm20, qword ptr [r15 + rbx] # ymm20 = mem[0],zero,zero,zero,mem[1],zero,zero,zero,mem[2],zero,zero,zero,mem[3],zero,zero,zero,mem[4],zero,zero,zero,mem[5],zero,zero,zero,mem[6],zero,zero,zero,mem[7],zero,zero,zero
vcvtdq2ps ymm20, ymm20
vpmovzxbd ymm21, qword ptr [r10 + rbx] # ymm21 = mem[0],zero,zero,zero,mem[1],zero,zero,zero,mem[2],zero,zero,zero,mem[3],zero,zero,zero,mem[4],zero,zero,zero,mem[5],zero,zero,zero,mem[6],zero,zero,zero,mem[7],zero,zero,zero
vcvtdq2ps ymm21, ymm21
vpmovzxbd ymm22, qword ptr [r12 + rbx] # ymm22 = mem[0],zero,zero,zero,mem[1],zero,zero,zero,mem[2],zero,zero,zero,mem[3],zero,zero,zero,mem[4],zero,zero,zero,mem[5],zero,zero,zero,mem[6],zero,zero,zero,mem[7],zero,zero,zero
vpmovzxbd ymm23, qword ptr [rax + rbx] # ymm23 = mem[0],zero,zero,zero,mem[1],zero,zero,zero,mem[2],zero,zero,zero,mem[3],zero,zero,zero,mem[4],zero,zero,zero,mem[5],zero,zero,zero,mem[6],zero,zero,zero,mem[7],zero,zero,zero
vcvtdq2ps ymm22, ymm22
vcvtdq2ps ymm23, ymm23
vpmovzxbd ymm24, qword ptr [r9 + rbx] # ymm24 = mem[0],zero,zero,zero,mem[1],zero,zero,zero,mem[2],zero,zero,zero,mem[3],zero,zero,zero,mem[4],zero,zero,zero,mem[5],zero,zero,zero,mem[6],zero,zero,zero,mem[7],zero,zero,zero
vcvtdq2ps ymm24, ymm24
vpmovzxbd ymm25, qword ptr [r11 + rbx] # ymm25 = mem[0],zero,zero,zero,mem[1],zero,zero,zero,mem[2],zero,zero,zero,mem[3],zero,zero,zero,mem[4],zero,zero,zero,mem[5],zero,zero,zero,mem[6],zero,zero,zero,mem[7],zero,zero,zero
vcvtdq2ps ymm25, ymm25
vmovaps ymm26, ymmword ptr [r14 + 4*rbx]
vmovaps ymm27, ymmword ptr [rdx + 4*rbx]
vfmadd231ps ymm0, ymm26, ymm18 # ymm0 = (ymm26 * ymm18) + ymm0
vfmadd231ps ymm17, ymm27, ymm19 # ymm17 = (ymm27 * ymm19) + ymm17
vfmadd231ps ymm3, ymm26, ymm20 # ymm3 = (ymm26 * ymm20) + ymm3
vfmadd231ps ymm16, ymm27, ymm21 # ymm16 = (ymm27 * ymm21) + ymm16
vfmadd231ps ymm2, ymm26, ymm22 # ymm2 = (ymm26 * ymm22) + ymm2
vfmadd231ps ymm5, ymm27, ymm23 # ymm5 = (ymm27 * ymm23) + ymm5
vfmadd231ps ymm1, ymm26, ymm24 # ymm1 = (ymm26 * ymm24) + ymm1
vfmadd231ps ymm4, ymm27, ymm25 # ymm4 = (ymm27 * ymm25) + ymm4
As expected LLVM (clang-cl) builds are faster in all cases.
For JincResizeMT(width*20, height*20) and YUVA 4:4:4:4 8bit source -
tap=3 at AVX512 chip best performance is opt=2 + AVX512 enabled in AVS+ environment = 34 fps. (coeff_stride=8 +32registers +2rows(?))
tap=7 at AVX512 chip is always better with opt=3 even with clock down from 3.38 to 2.7 GHz = 14.6 fps (with FP16=true). (coeff_stride=16)
jpsdr
4th October 2025, 10:56
You miss deleting memory for out_fp16 at class destruction (FreeData) -
:thanks:
jpsdr
4th October 2025, 11:09
I forgot... About LLVM, i've encountered issue building this project.
According what i've found, it's tricky !
LLVM and GCC behave differently. With GCC, putting __attribute__((__target__("avx512f"))) is enough to enable AVX512 even if the global build option doesn't allow AVX512, the intrinsic will work.
But with LLVM, there is a nasty trick :( (according Chat GPT).
When "getting" the intrinsic from #include <immintrin.h>, the global build option will configure what intrinsic are allowed. So with LLVM, putting __attribute__((__target__("avx512f"))) will still allow to build using AVX512 code, BUT... the AVX512 intrinsic will still not be avaible if the global build option is for exemple only SSE2. So, you have with LLVM to add, for this specific file, the global option to build with AVX512 (just the file, not the whole project fortunately).
This is why i had to move the FP16 create coeff to the AVX2 file, otherwise, with LLVM, it will build only if I build with AVX2 the core file, so putting the whole plugin usable only to AVX2 CPU for LLVM build.
DTL
5th October 2025, 10:04
More close to UserDefined2Resize(b=80, c=-20) kernel members for UserDefined2ResizeSPMT() and comparison of result of downsize from UHD 2160p to 1080p of some natural footage with difference frame:
UD4=UserDefined4ResizeSPMT(width/2, height/2, k10=97, k20=-2, k11=35, k21=-1, s=3)
UD2=UserDefined2Resize(width/2, height/2, b=80, c=-20).Subtitle("UD2")
Interleave(UD4, UD2, Subtract(UD4, UD2))
ConvertBits(8, dither=1) # input source is 10bit
https://imgsli.com/NDIwMjEy Vertical and horizontal transients looks close and most difference at diagonals as expected.
Subtract result - https://postimg.cc/FYFqzskG
https://i.postimg.cc/FYFqzskG/2025-10-05-120356.png (https://postimg.cc/FYFqzskG)
Subtraction shows most difference in fine sharp areas and looks also like checker-board pattern or mostly diagonal lines.
You can update defaults to better known now k10=97, k20=-2, k11=35, k21=-1 at https://github.com/jpsdr/JincResizeMT/blob/b7a653ac443d874f95ac0c7db98b6114fcaddb28/Src/JincResizeMT.cpp#L2386 and in the readme.
Test encoding of UHD->FHD rip of nature views with ffmpeg of 46 min length with settings -x264opts "crf=22:level=4.1:ref=4" -preset veryslow
shows slightly lower bitrate with UD4 downsize vs UD2 with settings from above - 5780 and 5809 kbit/s.
FranceBB
5th October 2025, 23:32
Vertical and horizontal transients looks close and most difference at diagonals as expected.
A tiny bit less haloing on the tip of the leaf on UD4 as well in the bottom left of the picture.
https://i.postimg.cc/R0PRHdwK/Screenshot-2025-10-05-232749.png
DTL
6th October 2025, 06:13
More important is flat colouring at the high saturation red OSD/logo patch. Like constant +-1LSB (or more) error/difference (at UV planes ?). It may mean some still non-corrected rounding error with float->integer conversion or something else. Maybe a resampling program needs +0.5f shift somewhere. Or resampling engine before downconversion from float32 to integer.
"A tiny bit less haloing on the tip of the leaf on UD4 "
With jinc-based kernels I think the thinnest possible over/undershoot (and also the speed of the gradient defining visual sharpness) is not as thin as we can get with sinc-based. Also single-pass 2D processing looks less sharp in smallest details (also with diagonal transients). It may be partially because dual-pass 1D resizers leave more diagonal ringing.
To compensate for this possible details loss on finest details we need to increase gain and medium-high frequencies (while having ringing suppressed). All this means (in my current tests) to have comparable sharp output we need to have thicker and higher halo/over/under shoot defined by the filter kernel with UD4 downsize in comparison with UD2 downsize.
Though when watching an image at real (recommended or more) distance from the screen the finest details are typically lost and sharpness defined more by medium-high (valid) frequencies. Not the highest valid. In comparison Spline64Resize keeps much more fine details without amplification of medium-fine but much of that detail is lost at real viewing distance.
Selection of production downsizer (and its settings) for broadcasting may be a complex task with real quality evaluation by non-expert viewers too. Also with different ages and different real viewing conditions. And select some average.
For example small children sitting too close to the screen will see how UD2/UD4 lose finest details and create significant halos over the high contrast standalone transients. And count this as significant distortion.
Though old people sitting at recommended distance (ITU/EBU) or far will see how medium-fine enhanced details create more sharpness, also medium-detail sized textures look more sharp/detailed.
It is a really complex task to select 'best' settings because if we use a linear kernel without significant halos on high contrast transients - we lose significant details sharpness on many more valuable texture patches. Practical selected settings may be some in between too soft look and too high halo on rare standalone high contrast transients. Better may be to use some non-linear sharpening engine (up to NN/AI-based) with some linear downsampling engine.
DTL
8th October 2025, 20:23
I check output of UD2 and UD4 resize of the color bars downsize with AvsPmod for YUV values of samples and they looks equal at the flat patches. In both 8 bit and 16 bit modes. I do not understand why OSD logo of that test footage after Subtract() looks colored. May be issue in the Subtract() ?
So it looks no issues with computing and rounding in current JincResizeMT version.
jpsdr
29th November 2025, 12:05
First page updated, minor changes.
DTL
6th December 2025, 20:57
A tiny bit less haloing on the tip of the leaf on UD4 as well in the bottom left of the picture.
I found your post some at Internet https://www.linkedin.com/posts/francesco-bucciantini-3392b4ab_over-the-years-several-different-resizers-activity-7395870781030809600-jJpG and a question about math around this.
Some days ago I tried to found if it is possible to make even 'sharper' 2D resize using our current 2D resize engine and more spectrum-powered kernel.
The foundings with some Mathcad simulation and also some AI reading from math about IFFT for 2D spaces:
1. About jinc kernel - it is 2D IFFT of the round spectrum in 2D :
F(u,v) = 1 if sqrt(u^2 + v^2) < 0.5 and 0 elsewhere.
Its IFFT2(F(u,v)) is f(x,y)=jinc(x,y). And it is really function of radius - f(x,y)=jinc(sqrt(x^2+y^2))= jinc(radius).
And it is the impulse kernel for filtering/resizing. Its 2D view you see as JincResize(width*10, height*10) of single non_zero sample.
2. The possible disadvantage of jinc kernel - its FFT spectrum not fill all possible frequencies for square sampling grid.
The full filled spectrum in square 2D object is
F(u,v) = 1 if (|u|<0.5 and |v|< 0.5) and 0 elsewhere.
And as Mathcad solver shows and AI tools hints - its
IFFT2 (impulse kernel for resizer) is
f(x,y)=sinc(x)*sinc(y).
But if we either make this 2D kernel for 2D resize or process 2 times with 1D + 1D SincResize - the output is equal. And it is that checker-borad like pattern instead of 'nice circles' like with JincResize.
Current conclusions from this:
1. It looks SincResize (and all weighted single sinc-based resizers like Lanczos and others) is 'separable' to 2 pass 1D and 1D resize and attempt to process with 2D engine will only be slower.
2. The jinc-based resize is not 'separable' and can be only processed with single pass 2D engine.
3. May be most important about sharpness: It looks jinc-based resize will always be less sharp in comparison with sinc-based because its Fourier spectrum is less powerful (as relation of square field with side size r (square is (2r)^2=4(r^2)) for sinc() and square of circle of radius r (square is pi(r^2)) - the jinc resize has less total 2D integrated spectrum power of (pi(r^2))/(4(r^2))=pi/4 ~ about 0.785.
The 2D spectrum power difference between sinc and jinc kernels is about 1-0.785=0.215. The total supported spectrum power of jinc is about 21% less in comparison with max possible in square sampling grid (possible with sinc) (?) .
Possible advantage of the jinc-based resize - it may process all same bandwidth 2D frequencies (with bandwidth of 0.5 in all directions from 0,0 - in H and V and all angled directions) without ringing-like effects that we see with 2D sinc-based resizers. So it may be maximum possible sharpness resizer with ability to display any 2D angle positioned shapes without ringing-like distortions. Sinc-based resizers only can display H and V oriented spatial frequencies without ringing but may expose some ringing-like (or even aliasing-like ?) artifacts at the angled spatial frequencies. It may looks like rectangular sampling grid for 2D frequencies can only support all possible frequencies fitted in the jinc 2D FFT.
Here is some Mathcad project to compute 2D impulse kernels f2D(x,y) as IFFT2 of the spectrum shapes (F2D(u,v))
https://i.ibb.co/fV5V0fM5/2d-fft2.png (https://imgbb.com/)
Unfortunately at my old CPU Mathcad 15 takes too large time to compute even 1D graphs sections like f2D(x,0). May be someone with more powerful PC and better math simulation tools can make nice 2D graph plots (really 3D function of x,y).
Additional notes:
I still not sure if it is valid for Nyquist theorem to use >0.5 radius spectrum in 2D space as we have with 2D sinc kernel. For 1D case it is all simple - definitely not valid.
So JincResize with jinc kernel and 2D spectrum limited by 0.5 radius may be used as definitely aliasing-safe resize method. But it still produce a bit less sharp results in comparison with sinc-based resize.
SincResize (and sinc-based resizers) may be not aliasing-safe for angled (diagonal) spatial frequencies and only safe for H and V directions.
tormento
7th December 2025, 15:24
Some days ago I tried to found if it is possible to make even 'sharper' 2D resize using our current 2D resize engine and more spectrum-powered kernel.
Don't know if this can really help you: I use ewa_lanczos4sharpest from avslibplacebo and find it sharp, pleasant and without any visible artifact.
Perhaps it's the linear + sigmoid resizing but I hope you can find more in the libplacebo source.
DTL
8th December 2025, 08:49
Tried to compare 3 ewa_lanczos resizers with JincResizeMT in filter kernel footprints:
LoadPlugin("avs_libplacebo.dll")
LoadPlugin("JincResizeMT.dll")
BlankClip(200,1,1, pixel_type="YV24", color_yuv=$D07f7f)
AddBorders(7,7,7,7, color_yuv=$307f7f)
elz4shst=libplacebo_Resample(width*20, height*20, filter="ewa_lanczos4sharpest").Subtitle("ewa_lanczos4sharpest")
elzsh=libplacebo_Resample(width*20, height*20, filter="ewa_lanczossharp").Subtitle("ewa_lanczossharp")
elz=libplacebo_Resample(width*20, height*20, filter="ewa_lanczos").Subtitle("ewa_lanczos")
jr_1=JincResizeMT(width*20, height*20,tap=2,blur=1.04).Subtitle("JR_MT_1")
jr_2=JincResizeMT(width*20, height*20,tap=3,blur=1.0).Subtitle("JR_MT_2")
jr_3=JincResizeMT(width*20, height*20,tap=3,blur=1.0).Subtitle("JR_MT_3")
avs_lp=StackVertical(elz4shst, elzsh, elz)
jr=StackVertical(jr_1,jr_2, jr_3)
StackHorizontal(avs_lp, jr, Subtract(avs_lp, jr))
https://i.ibb.co/ksk9w8x4/src3000188.jpg (https://ibb.co/S4WdMnPt)
It looks like the kernel footprint for single sample excitation does not depend on linearization/sigmoidization and it makes comparison more simple.
They look like they have some mess of duplication with filter settings - ewa_lanczossharp and ewa_lanczos are equal and completely equal to JincResizeMT(,tap=3,blur=1.0). If users use linearization/sigmoidization in libplacebo - it must be performed before and after resize with JincResizeMT().
The ewa_lanczos4sharpest looks to use non-jinc weighting with significant suppression of the first negative lobe of the jinc kernel. This can not be emulated in jinc-only weighting +some blur-scaling in current JincResizeMT(). This really expected to make less 'sharp' results in comparison with other ewa_lanczos presets. Some close but not equal settings with JincResizeMT are JincResizeMT(,tap=2,blur=1.04)
What I see from sources filters.c file:
const struct pl_filter_config pl_filter_ewa_lanczos4sharpest = {
.name = "ewa_lanczos4sharpest",
.description = "Sharpened Jinc-AR, 4 taps",
.kernel = &pl_filter_function_jinc,
.window = &pl_filter_function_jinc,
.radius = JINC_ZERO4,
// Similar to above, see:
// https://www.imagemagick.org/discourse-server/viewtopic.php?p=128587#p128587
.blur = 0.88451209326050047745788,
.antiring = 0.8,
.polar = true,
.allowed = PL_FILTER_SCALING,
.recommended = PL_FILTER_UPSCALING,
};
const struct pl_filter_config pl_filter_ewa_lanczos = {
.name = "ewa_lanczos",
.description = "Jinc (EWA Lanczos)",
.kernel = &pl_filter_function_jinc,
.window = &pl_filter_function_jinc,
.radius = JINC_ZERO3,
.polar = true,
.allowed = PL_FILTER_SCALING,
.recommended = PL_FILTER_UPSCALING,
};
const struct pl_filter_config pl_filter_ewa_lanczossharp = {
.name = "ewa_lanczossharp",
.description = "Sharpened Jinc",
.kernel = &pl_filter_function_jinc,
.window = &pl_filter_function_jinc,
.radius = JINC_ZERO3,
// Blur value determined by method originally developed by Nicolas
// Robidoux for Image Magick, see:
// https://www.imagemagick.org/discourse-server/viewtopic.php?p=89068#p89068
.blur = 0.98125058372237073562493,
.polar = true,
.allowed = PL_FILTER_SCALING,
.recommended = PL_FILTER_UPSCALING,
};
Difference between ewa_lanczossharp and ewa_lanczos only in blur of 0.98 (about invisible).
ewa_lanczos4sharpest has larger blur (more wide or more narrow scaling ?) and +1 tap but also 'antiring' of 0.8. Where 'antiring' is additional non-jinc weighting processing and it is about destroying all lobes except the first positive. Though 'antiring' may be applied after convolution resize with not-antiringed kernel (?). If users of AVS like that 'antiring' (post)processing to 2D resize jinc-based it needs to be ported separately (or as a separate filter ?).
In the description to avslibplacebo:
antiring
Antiringing strength.
A value of 0.0 disables antiringing, and a value of 1.0 enables full-strength antiringing.
Only relevant for separated/orthogonal filters.
Default: 0.0.
But it looks outdated and antiringing already implemented and working for 2D/polar resizers too. Also ewa_lanczos4sharpest preset looks like equal to manual tuning of processing 'generic' jincresize as
libplacebo_Resample(filter="ewa_lanczos", radius=4, blur=1.0, antiring=0.8).
tormento
8th December 2025, 12:14
Tried to compare 3 ewa_lanczos resizers with JincResizeMT in filter kernel footprints
Very nice and detailed analysis, thank you!
FranceBB
31st December 2025, 18:44
I found your post
Yeah I've included the latest version of plugins_JPSDR in FFAStrans (https://forum.doom9.org/showthread.php?t=176655) so that users can now choose JincResizeMT() from the drop down menu when inserting a resizing node, that's why I made the post. It will be released as soon as FFAStrans 1.4.2 is deemed stable.
https://images2.imgbox.com/60/fd/Y9fFWlIb_o.png
Anyway, it's always gonna be a tradeoff between spectral power vs directional consistency.
The checkboard-like pattern in Sinc is there and will always be there because of the 1D+1D (horizontal+vertical), in fact the resulting 2D impulse response is: f(x,y) = sinc(x) • sinc(y) In the frequency domain, it creates that square/checkboard-like pattern.
The reason why it's not there in Jinc is that it's a true 2D operation where the kernel is a function of the radius r= sqrt(x^2+y^2) which makes its frequency response a circular disk.
In other words:
SincResize()
Spectral Shape: Square
Diagonal Reach: Preserves frequencies up to 0.707 Nyquist
Ringing Pattern: Rectangular (i.e the checkboard)
Complexity: O(N) so it's linear
JincResize()
Spectral Shape: Circular (isotropic)
Diagonal Reach: Limits all directions to 0.5 Nyquist
Ringing Pattern: Circular (i.e like haloing)
Complexity: O(N^2) so it's exponential
the jinc resize has less total 2D integrated spectrum power of (pi(r^2))/(4(r^2))=pi/4 ~ about 0.785.
Yes. Given that the Sinc kernel fills the corners of the "frequency square", it preserves more diagonal information, which is why it looks sharper but that sharpness is technically anisotropic in the sense that the filter treats the diagonal frequencies differently from the horizontal and vertical ones. The checkboard pattern is actually a diagonal ringing - albeit a weird one given that it's a synthetic benchmark. The square spectrum has "corners" so it allows higher-frequency components on the diagonals than the sampling grid can actually support cleanly, leading to that kind of artifact.
I still not sure if it is valid for Nyquist theorem to use >0.5 radius spectrum in 2D space as we have with 2D sinc kernel. For 1D case it is all simple - definitely not valid.
So JincResize with jinc kernel and 2D spectrum limited by 0.5 radius may be used as definitely aliasing-safe resize method. But it still produce a bit less sharp results in comparison with sinc-based resize.
SincResize (and sinc-based resizers) may be not aliasing-safe for angled (diagonal) spatial frequencies and only safe for H and V directions.
Yep, you're right, in a standard rectangular sampling grid the Nyquist limit is 0.5 in both directions (i.e both u and v). The "extra" space in the corners of a Sinc filter (i.e the area outside the 0.5 radius but inside the 0.5x0.5 square) is where the problem occurs. JincResize() is alising safe because it treats the limit as a radius. In other words, no matter the angle of the edge (diagonal lines, curves), the bandwidth is constrained in the same way. On the other hand, SincResize() creates a sort of "preferential treatment" for diagonals which then creates the problems.
When we use a separable filter, the ringing (oscillation) only occurs along the x and y axes. On a perfectly horizontal or vertical edge, the ringing is a simple 1D ripple following the edge, but on a diagonal edge the horizontal ringing and vertical ringing overlap because the kernel is a square sinc(x) • sinc(y) so the "echoes" of the edge are also square-aligned. Mathematically, it would be anisotropic ringing, but to the eye is the checkboard pattern we saw, but nonetheless not a failure of the Nyquist limit.
JincResize produces isotropic ringing instead because the kernel is circular, so the ripples propagate outwards uniformly in all directions.
https://images2.imgbox.com/41/79/Dh3SF2hg_o.png
J1 is the Bessel function of the first kind. This is to say that obviously JincResize isn't immune to everything, but the thing is that when a Jinc filter "rings", the "echo" of a diagonal line is a ripple that is parallel to that line which is much more natural to the human eye compared to the orthogonal grid of ripples produced by separable Sinc filters, which is why it looks "better". I mean, it preserves the roundness of the Airy Disk example 'cause the ringing itself is round xD
Anyway one could say "well, but if Sinc is supposed to treat diagonal lines differently to avoid staircase artifacts and I only have ripples for functions that are not limited in frequency, wouldn't putting a window like Lanczos or Blackman help?"
to which the answer would be "yes, but not quite".
I mean, using a windowed sinc does help because the window function forces the Sinc function to 0 at a certain radius and reduces the amplitude of the side lobes (i.e the ripples), but LanczosResize() is still separable, so we're still multiplying two windowed 1D functions. The "corners" of the frequency square still exist, meaning that the filter still "sees" further into the diagonal frequencies (0.707 Nyquist) than the horizontal and vertical ones (0.5 Nyquist).
Emulgator
1st January 2026, 00:54
Plus a reminder: Nyquist is the poorest mathematical vehicle which can ONLY be ridden if an infintely (!) stationary signal can be assumed.
(AND be sampled not at its nodes, which can not be taken for granted)
Since real-world signals do vary in time I have banned such narrow bridges from my calculations since 1985.
The steeper antialiasing and reconstruction filters are designed the more harm they cause.
For signal fidelity I can only suggest to keep a safe distance from the nyquist pothole.
FranceBB
2nd January 2026, 16:39
Since real-world signals do vary in time I have banned such narrow bridges from my calculations since 1985.
I wasn't even born back then xD
DTL
6th January 2026, 10:32
users can now choose JincResizeMT() from the drop down menu when inserting a resizing node,
As typical production operation (and many rip creations too) is downsizing it is good to include all downsize filters too.
Now in the drop down list I see mostly Gauss and SinPow (and partially Bicubic) filters for downsizing. No 1D+1D (sinc-based) UserDefined2 and no 2D (jinc-based) UserDefined4 .
As FFASTrans is expected to be an industrial tool for professionals it is expected they are educated enough to set params of kernel for expected best downsizers with more complex kernel control.
FFASTrans is also used in my company for many operations. So it is fun to see how a resizer designed in one room passed via different countries to different software and returned to work in a different room of the same company (in some days or years later). I hope the workers of my company in different rooms using FFASTrans may in some year finally get UserDefinedX downsizers to work via FFASTrans. They do not like to write text-based AVS scripts but can use window-based FFASTrans applications. So it may be some development task for the designer of FFASTrans to set some most commonly used params for UserDefinedX filters if GUI of FFASTrans does not have controls to setup kernel members and support size (filter params).
FranceBB
6th January 2026, 18:24
Let me just say that I'm actually extremely glad that you guys are using FFAStrans. :D
I can of course add UserDefined2 and UserDefined4 to the resize node, I just have to think about a proper way to display the options to the user in the GUI.
Thank you for everything you've been doing for the community, DTL, really. In the end, if those things are possible is thanks to the contributions of each and everyone of us.
In Latin they say "do ut des" which means "give and receive" and I guess that's exactly what the open source community is about. :)
DTL
11th January 2026, 11:01
Tried script for linear resizers kernels footprints from current version of plugins_JPSDR:
LoadPlugin("plugins_JPSDR.dll")
BlankClip(length=200, width=1, height=1, pixel_type="YV24", color_yuv=$D07f7f)
AddBorders(9, 9, 9, 9, color_yuv=$307f7f)
upr=15
SincLin2=SincLin2Resize(width*upr, height*upr,taps=8).Subtitle("SincLin2Resize taps=8 1D+1D", align=8)
Jinc=JincResizeMT(width*upr, height*upr, tap=8).Subtitle("JincResize t=8 2D", align=8)
Point=PointResizeMT(width*upr, height*upr).Subtitle("PointResize t=16 1D+1D", align=8)
Bilinear=BilinearResizeMT(width*upr, height*upr).Subtitle("BilinearResize 1D+1D", align=8)
Bicubic = BicubicResizeMT(width*upr, height*upr).Subtitle("BicubicResize 1D+1D", align=8)
Lanczos=LanczosResizeMT(width*upr, height*upr).Subtitle("LanczosResize 1D+1D", align=8)
Lanczos4=Lanczos4ResizeMT(width*upr, height*upr).Subtitle("Lanczos4Resize 1D+1D", align=8)
Blackman=BlackmanResizeMT(width*upr, height*upr).Subtitle("BlackmanResize 1D+1D", align=8)
Spline16=Spline16ResizeMT(width*upr, height*upr).Subtitle("Spline16Resize 1D+1D", align=8)
Spline36=Spline36ResizeMT(width*upr, height*upr).Subtitle("Spline36Resize 1D+1D", align=8)
Spline64=Spline64ResizeMT(width*upr, height*upr).Subtitle("Spline64Resize 1D+1D", align=8)
Gauss=GaussResizeMT(width*upr, height*upr).Subtitle("GaussResize 1D+1D", align=8)
Sinc=SincResizeMT(width*upr, height*upr).Subtitle("SincResize 1D+1D", align=8)
SinPow=SinPowResizeMT(width*upr, height*upr).Subtitle("SinPowResize 1D+1D", align=8)
UD2=UserDefined2ResizeMT(width*upr, height*upr, b=80, c=-20).Subtitle("UserDefined2 b=80 c=-20 1D+1D", align=8)
UD4=UserDefined4ResizeSPMT(width*upr, height*upr).Subtitle("UserDefined4ResizeSP 2D", align=8)
#Compare
h01=StackHorizontal(SincLin2, Jinc, Point, Bilinear)
h02=StackHorizontal(Bicubic,Lanczos,Lanczos4,Blackman)
h03=StackHorizontal(Spline16,Spline36,Spline64,Gauss)
h04=StackHorizontal(Sinc,SinPow,UD2,UD4)
StackVertical(h01,h02,h03,h04)
https://i.ibb.co/KjkTxHNL/rkfp-pjpsdr01.png (https://ibb.co/Y7MHBGDy)
https://ibb.co/Y7MHBGDy
Sadly found JincResizeMT still need some fixes of kernel at the bottom of frame - if frame size is too small (relative to kernel size ?) the bottom of frame is distorted. Also with tap=16 and upsize ratio of 15 the center point looks like has some computing issues and not looks flat (may be quant is not enough ?). It is to current 'known issues'. With general usage small taps (below 4..6 ?) num and small upscale ratios it expected not very visible.
FranceBB
11th January 2026, 14:04
By the way, I've added the missing resizers "SincLin2Resize", "SinPowerResize", "UserDefined2Resize" as well as the missing "force", "keep_center", "placement" parameters to the old wiki (http://avisynth.nl/index.php/Resize) so that it's now aligned with the new one (https://avisynthplus.readthedocs.io/en/latest/avisynthdoc/corefilters/resize.html).
Well, not quite perfectly aligned, but still... at least they're there now xD
jpsdr
11th March 2026, 20:27
Hi.
I've pushed the update of the new core, but don't have time to test right now. I will, but not for now.
In the meantime, if anyone want to build and test, go ahead :D !
DTL
11th March 2026, 21:28
In resample.cpp -
// VS 2019 v16.2
#if _MSC_VER >= 1922
#define AVX512_BUILD_POSSIBLE
#endif
To make all builds for development and testing I still use VS2017 because it is the last version supported intel SDE for AVX512 emulation with debugger from VS. Better to allow VS2017 builds too. Though it may not support all AVX512 versions of text but as I remember AVS+ sources still compatible with VS2017. The not-supported found things are very few like some ways of loading of bits constants to mask registers. But other ways exist to workaround without performance penalty.
jpsdr
11th March 2026, 23:39
I'll not change it on my side, but you can make your own branch and change it. There is the same in cpp avx512/avx512b on all the file. This allow to have all the files included and activated in the project, but not build if unsuported, instead of included but not activated
jpsdr
12th March 2026, 09:51
Tell me anyway if with your Visual Studio version you can build without any issue. If it's the case, i'll change the minimum requirement.
I took it from check_avx512.h in the avisynth code, so i've assumed it was the minimum requirement.
... ...
Ok... I'll took a look again at check_avx512.h and i was clearly in the wrong, it was just for a few command. I'll change the code to go back to proper check.
jpsdr
12th March 2026, 18:49
@DTL
I've rolled back (and pushed) to a proper AVX512 requierement, i was too quick when looking to check_avx512.h.
Edit:
Tell me if you can build now.
New version, see first post.
It may be interesting to check performance differences at multithreading execution of permutex-based resamplers between AVS+ core inter-frame based MT and ResampleMT intra-frame based MT. Unfortunately those algorithms are fast in single threading (low enough main host RAM read-write streams) but quickly lose performance at many threads execution with poor low-RAM channels host memory. Sort of very non-SDRAM friendly algorithms and can only run fast with small image sizes fits in CPU caches. Though with the next generation CPUs like Xeon MAX with large HBM RAM onboard and AMD with large cache this limitation is expected to be lower.
This is why special L2-cache size granularity was added to make this performance issue lower at bigger image sizes. Though this causes more register file coeffs reloads and adds more performance penalty. Though if each loaded once in the register file subset of coeffs can be used to process at least 10 rows this overhead is already 10 times lower in comparison with each row coeffs reload. With intra-frame MT in ResampleMT other solutions are possible for testing like running each H-stripe of L2-cache size in a separate thread. Though thread synchronization overhead may damage performance too. Or at least each thread may get an interleaved list of H-stripes of an image instead of a single H-stripe to process. This type of frame scan order is only possible in ResampleMT and was not tested in AVS+ core.
Tested release 2.10.0 and it looks follows the performance features of current AVS+ sources. They were optimized for frame-based MT but looks still not completely resolve the memory transfer issues.
Test script:
LoadPlugin("ResampleMT.dll")
BlankClip(100000, width=400, height=400, pixel_type="YUV444PS")
#BicubicResize(width*4,height) # permute, H kernel size 4
BicubicResizeMT(width*4,height, threads=1) # permute, H kernel size 4
Both AVS+ and ResampleMT now runs at about 800 fps with full (6) threads enabled at i5-9600K. And if set no-Prefetch for AVS+ core or threads=1 for ResampleMT - at about 360 fps.
But non-L2 cache optimized AVS+ release from 30 Nov 2025 runs at about 1200 fps no-Prefetch. It was found the stream_ps (uncached store) added to help decrease performance drop with MT-enabled causes significant single threaded performance degradation. The lastest L2-cache optimized version runs at about 2200 fps single threaded with cached store store_ps().
So with current version of <=ks4 resize it may be used either templated or 2 different copies of function with cached or not store for 1threaded or MT processing. Also for MT meander scan and prefetch for write also adds performance (MT performance drop not as big). See commit https://github.com/DTL2020/AviSynthPlus/commit/fbda23c47c9fcf073b3cf3ae138237886f5b2fe4 as eaxmple. Also others permute-based resample functions (AVX2 and AVX512) need to be tested for this issue and possible workarounds (also at different CPUs architectures).
It is still sort of anomality when single threaded (1 core) function runs faster in comparison with any attempt to run in >1 threads. You may test threads=1 execution with _mm256_store_ps() at https://github.com/jpsdr/ResampleMT/blob/360d443fbc5323909736478d45a106755094f22d/ResampleMT/resample_avx2.cpp#L1237 . I hope it is processing function for BicubicResizeMT(width*4,height, threads=1) in ResampleMT too.
jpsdr
12th May 2026, 18:51
I don't have time to make experiments.
If you said that it's better with the "store" instead of "stream", i will change it, i may have a little time to make a new version until the end of the week, but that's all.
In my tests it is better only for single thread execution. Looks like the CPU memory subsystem can only arrange in the best way the memory read-write requests from a single core only and greatly degrade performance if >1 core is used. Maybe some caches synchronization traffic eats all performance additions ? The performance degradation from not best memory access (?) is more in comparison with performance addition from several cores running convolution computing. So my current partial solution - use 'store' in single threaded execution and 'stream' (+ meander scan and prefetch for write) in multithreaded. This needs either 2 separate functions text or templated functions with some additional templating param like bool bMT. Also for the ResampleMT filter with mostly expected multithreaded execution use cases (?) this may cause additional users confusion because at least for some frame sizes and scale ratios single threaded execution is still faster in comparison with any attempt to run 2 and more threads.
jpsdr
16th May 2026, 11:15
By single threaded do you mean using threads=1 but with eventualy prefech>1 (the AVS+ command, not the parameter filter), or pure one core, with both threads=1 and prefech=1 (so no prefetch command in the AVS script) ?
"By single threaded do you mean using threads=1 "
Yes - threads=1 and no Prefetch in script makes best performance. Adding Prefetch(1) already visibly degrades performance (may be -20..30%). But it looks unavoidable if user want to use 1 threaded run of filter in a long script ?
Like
some_filters
Prefetch(N) #to run previous filters with full multithreading
ResizeMT(threads=1)
Preftech(1) # to run ResizeMT in 1 thread mode (?)
? Not yet tested.
jpsdr
17th May 2026, 10:51
If i understand properly, using "store" instead of "stream" is interesting only in the case threads=1 and without prefetch ?
In that case, i will not waste time for checking this too much specific case.
Yes - it may be not very important for MT-specific filter. It is a note about possible better performance mode found at testing. Though it is currently sort of anomaly with best performance at single threaded execution. May be subject of future research if this performance boost may be used in more or less limited multithreaded mode. Currently it is planned for main usage in the future partial plane (tiled) processing inbetween filters. This is what mean note around 'store' operation in current sources.
hello_hello
25th May 2026, 21:20
For a long time my memory has been that the MT resizers are slower with Prefetch in a script than without it, but I can't remember if that includes the use of the Threads argument or not. I haven't bench-marked them in a long time, so after reading DTL's post I thought I'd give it a spin.
The FPS numbers below aren't extremely accurate as for some reason AvsPmod doesn't display the final frame rate at the end of a benchmark when it's running in Wine, only the duration, so the FPS numbers are roughly what they appeared to be while the benchmark was running.
I also remember AvsResize being considerably slower when Avisynth's MT is enabled, but that no longer seems to be the case.
Zen 4 Ryzen 9 7900X, Avisynth+ r4565, running DLT's script from post #432.
BicubicResizeMT
No Threads, Prefetch(12) 121 seconds 800 fps
Threads=12, Prefetch(12) 76 seconds 1300 fps
Threads=1, No Prefetch 24 seconds 4000 fps
Threads=1, Prefetch(12) 13 seconds 8000 fps
z_BicubicResize
No Prefetch 31 seconds 3300 fps
Prefetch(12) 24 seconds 4000 fps
BicubicResize
No Prefetch 26 seconds 3800 fps
Prefetch(12) 11 seconds 8500 fps
jpsdr
26th May 2026, 18:02
What do you mean by "No Threads" ? The parameter threads is not set ? (I'll assume that for now, it's identical to threads=0, meaning for you CPU it's the same as threads=24).
Have yo read the Multi-threading information part of the first post ?
You should try the following if you want to keep at 12 threads (even if your CPU can do 24).
- No Threads (threads=0), No prefetch
- Any combination (threads=p,prefetch=n), Prefetch(n) with n*p=12 & p>1.
hello_hello
27th May 2026, 04:46
I probably read the multi-threading information about 8 years ago. I'd forgotten the MT resizers have a prefetch argument as I haven't used them regularly.
No threads = Threads not set.
The main reason I tested with Prefetch(12) is because it's generally the value that gives me the highest speed when encoding scripts. Depending on the filtering the sweet spot is usually from Prefetch(8) to Prefetch(12), so when I'm too lazy to run a test encode I use Prefetch(12). For this simple script, it turns out Prefetch(8) is about 300fps faster than Prefetch(12) and nothing greater than 12 increases the speed (but at least it doesn't decrease).
Anyway, I tried again, and the only way to achieve roughly 8000 fps as I did in the previous tests, is to use Threads=1 for the resizer (with Prefetch(12) in the script). Any value for Threads other than one, at least halves the speed.
These combinations for the resizer arguments (based on you formula) run at around half the speed of Threads=1.
Threads=2, Prefetch=6
Threads=3, Prefetch=4
Maybe the moral of the story is running the MT resizers in MT mode when using Prefetch is a bad idea, at least when there's more than a few CPU cores. :)
Does anyone not use Prefetch in a script these days? I'm wondering if Threads=1 should be the default for Avisynth+.
jpsdr
27th May 2026, 12:18
As i'm not using Prefetch, i'll keep the default mode as it is.
If you use threads=1, in that case, don't bother to use my MT versions, just keep the internal original resizers.
What speed do you have with for :
No prefetch (no Prefecth command in the script and don't set the prefetch parameter) and :
- threads=0
- threads=12
Out of curiosity for compare with your others tests.
real.finder
27th May 2026, 13:37
As i'm not using Prefetch, i'll keep the default mode as it is.
or maybe do something like this https://github.com/HomeOfAviSynthPlusEvolution/neo_DFTTest/issues/6#issuecomment-3621448531
BicubicResizeMT
No Threads, Prefetch(12) 121 seconds 800 fps
Threads=12, Prefetch(12) 76 seconds 1300 fps
Threads=1, No Prefetch 24 seconds 4000 fps
Threads=1, Prefetch(12) 13 seconds 8000 fps
You can also try full intra-frame MT for 12 threads:
Threads=12 and no Prefetch().
BicubicResize with Prefetch(12) mean 12 threads with inter-frame MT in AVS+ core.
" the only way to achieve roughly 8000 fps as I did in the previous tests,"
As many testing shows the simple resizers like Bicubic with upsampling are not limited by RAW computing math performance but mostly with memory management. So it is better to test total performance with source and sink filters attached in the chain. The RAW computing performance mostly important if resizer is running at 1 thread at 1 core only. But it may be not frequent use case at the days of massive multicore consumer CPUs. Though if user runs many scripts at the separate processes at 1 threaded mode it may be an example.
jpsdr
27th May 2026, 22:22
Didn't notice that with threads=1, Prefetch(12) is only twice faster than No Prefetch... Meaning that in resample compute time is small vs memory transfert. In NNEDI3 where compute time is high vs memory transfert, the speed increase is directly linear with the number of cores with at least around 10 if memory is correct (10 threads is around 10 times faster than 1 thread). Here, 12 threads is only 2 times faster...
jpsdr
31st May 2026, 11:36
Some benchmark, and an unexpected result at the end, test i've made to check in fact something else...
Downscale:
ColorBars(3840,2160,"YV24").KillAudio().trim(0,999)
BicubicResizeMT(width/2, height/2, threads=8)
#Prefetch(8)
ColorBars(3840,2160,"YV24").KillAudio().trim(0,999)
BicubicResize(width/2, height/2)
Prefetch(8)
Upscale:
ColorBars(1920,1080,"YV24").KillAudio().trim(0,999)
BicubicResizeMT(width*2, height*2, threads=8)
#Prefetch(8)
Trim, threads or Prefetch values were adjusted according test.
Results:
Core i7 860 (4 cores / 8 threads)
-----------
Windows 7 x86
AVS 2.60 (2.6.0.6)
Downscale
threads=1: 16.74 fps (1 thread)
threads=2: 33.08 fps (3 threads)
threads=4: 42.00 fps (5 threads)
threads=8: 67.32 fps (9 threads)
Upscale
threads=1: 14.17 fps
threads=2: 28.08 fps
threads=4: 46.31 fps
threads=8: 55.84 fps
-----------
Windows 7 x64
AVS+ 3.7.5 (r4657)
Downscale
threads=1,Prefetch(2): 38,91 fps
threads=1,Prefetch(4): 72,45 fps
threads=1,Prefetch(8): 86,47 fps
threads=1,No Prefetch : 19.33 fps
threads=2,No Prefetch : 38.18 fps
threads=4,No Prefetch : 58.84 fps
threads=8,No Prefetch : 83.38 fps
Upscale
threads=1,Prefetch(2): 32.99 fps
threads=1,Prefetch(4): 61.44 fps
threads=1,Prefetch(8): 70.54 fps
threads=1,No Prefetch : 16.42 fps
threads=2,No Prefetch : 32.52 fps
threads=4,No Prefetch : 54.67 fps
threads=8,No Prefetch : 68.17 fps
=========================================
Core i7 6950X (10 cores / 20 threads)
-----------
Windows 7 x86
AVS 2.60 (2.6.0.6)
Downscale
threads=1: 32.27 fps
threads=4: 127.1 fps
threads=10: 176.5 fps
threads=20: 317.0 fps
Upscale
threads=1: 24.18 fps
threads=4: 95.90 fps
threads=10: 133.1 fps
threads=20: 226.0 fps
-----------
Windows 7 x64
AVS+ 3.7.5 (r4657)
Downscale
threads=1,Prefetch(4): 221.1 fps
threads=1,Prefetch(10): 518.3 fps
threads=1,Prefetch(20): 578.3 fps
threads=1,No Prefetch : 58.69 fps
threads=4,No Prefetch : 229.3 fps
threads=10,No Prefetch : 307.1 fps
threads=20,No Prefetch : 542.7 fps
Upscale
threads=1,Prefetch(4): 179.6 fps
threads=1,Prefetch(10): 382.8 fps
threads=1,Prefetch(20): 429.8 fps
threads=1,No Prefetch : 46.02 fps
threads=4,No Prefetch : 179.4 fps
threads=10,No Prefetch : 235.2 fps
threads=20,No Prefetch : 332.2 fps
=========================================
Rizen Threadripper Pro 7975WX (32 cores / 64 threads)
-----------
Windows 10 x64
AVS+ 3.7.5 (r4657)
Downscale
threads=1,Prefetch(4): 935.7 fps (72 threads)
threads=1,Prefetch(32): 4892 fps (100 threads)
threads=1,Prefetch(64): 4804 fps (132 threads)
threads=1,No Prefetch : 236.9 fps (68 threads)
threads=4,No Prefetch : 856.3 fps (72 threads)
threads=32,No Prefetch : 1946 fps (100 threads)
threads=64,No Prefetch : 1531 fps (132 threads)
Upscale
threads=1,Prefetch(4): 858.0 fps (72 threads)
threads=1,Prefetch(32): 2992 fps (100 threads)
threads=1,Prefetch(64): 2718 fps (132 threads)
threads=1,No Prefetch : 270.3 fps (68 threads)
threads=4,No Prefetch : 868.5 fps (72 threads)
threads=32,No Prefetch : 1922 fps (100 threads)
threads=64,No Prefetch : 1491 fps (132 threads)
Downscale, internal Bicubic
No Prefetch: 246.5 fps (68 threads)
Prefetch(4): 708.0 fps (72 threads)
Prefetch(32): 3374 fps (100 threads)
Prefetch(64): 3345 fps (132 threads)
Unexpected result:
Downscale, internal Bicubic (LLVM Zen 4 build of AVS+)
No Prefetch: 246.5 fps (68 threads)
Downscale, internal Bicubic (MSCV AVX512 build of AVS+)
No Prefetch: 163.4 fps (68 threads)
hello_hello
13th June 2026, 00:09
What speed do you have with for :
No prefetch (no Prefecth command in the script and don't set the prefetch parameter) and :
- threads=0
- threads=12
Out of curiosity for compare with your others tests.
Sorry about the very slow reply, but I forgot for some reason. To answer your question.... and once again the fps is an estimate (because when it's running in Wine, AvsPmod doesn't display the resulting fps following the benchmark) but the duration is accurate.
BlankClip(100000, width=400, height=400, pixel_type="YUV444PS")
BicubicResizeMT(width*4,height, threads=?)
Threads=0 122 seconds ~850 fps (it jumps around a bit)
Threads=12 76 seconds ~1300 fps
vBulletin® v3.8.11, Copyright ©2000-2026, vBulletin Solutions Inc.