Release of G'MIC 3.6

Has JIT code parsing changed recently? In previous versions, I can use macro outside of another macro, and it’ll still work. In addition, I noticed that sometimes variable is uninitialized.

This code works fine in previous version:

Works before maybe 3.5
$ +rep_random_subdivision_of_dimension_boundaries 5,0,20,100%,160000,0,,256,256 256,256 eval.. polygon(#-1,4,i0,i1,i2,i1,i2,i3,i0,i3,1,y);

#@cli rep_random_subdivision_of_dimension_boundaries: split>1,_additional_thickness>=0,_max_iter>=2,_probability[%]>0,_loop_limit>1,0<=_border[%]>=100%,_seed,_number_of_dimensions,spatial_dimension_1,spatial_dimension_2,...
#@cli : Generate random division of rectangle as in random number of division and varying thickness within rectangle.
#@cli : _additional_thickness refers to the excess pixel thickness.
#@cli : _max_iter limits the number of iterations per rectangle.
#@cli : _probability determines the probability that a rectangle will be permitted to be utilized for further iteration.
#@cli : _loop_limit limits the number of time the process of subdivision of all quadrilaterals is done.
#@cli : _seed generates the output based on defined parameter.
#@cli : Default values: '_gen_thick=0','_max_iter=5','_probability[%]=95%','_loop_limit=5000','_border=0%','_seed=n/a'
+rep_random_subdivision_of_dimension_boundaries:
skip ${2=0},${3=5},${4=95%},${5=5000},${6=2},${7=}

old_status=${}

start_img_ind:=$!
split,additional_thickness,maximum_iteration,probability,loop_limit,border_size,seed,use_multithreading=${1-7},1
use_seed={narg($seed)}

check "$split>1 &&
       $additional_thickness>=0 &&
       $maximum_iteration>=2 &&
       inrange($probability,0,1,1,1) &&
       (ispercentage("$border_size")?inrange("$border_size",0,1,1,1):isint("$border_size",0)) &&
       $loop_limit>1"

list_of_dimensions:=abs([${8--1}])
prod_dimensions:=prod($list_of_dimensions)
prod_loops_and_iter:=prod($loop_limit,$maximum_iteration)
dimensions:=$#-7

if $_cpus==1||($prod_dimensions/($additional_thickness+1))<0x10000||prod($maximum_iteration,$loop_limit)<0x10000
	use_multithreading=0
fi

if ispercentage($border_size)
	border_size:=int((additional_thickness)*$border_size)
fi

check "$border_size<(1+$additional_thickness)"

coordinates_and_iterations_array_size={($dimensions<<1)+1}
1,1,1,$coordinates_and_iterations_array_size,"
	const dimensions=$dimensions;
	const additional_thickness=$additional_thickness;
	const border_size=$border_size;
	const double_border_size=border_size<<1;
	const minimum_dimension_requirement=additional_thickness+double_border_size+1;
	list_of_dimensions=["$list_of_dimensions"];
	coordinates=vector(#s,0);
	repeat(dimensions,p,
		current_dimension=list_of_dimensions[p];
		if(!isfinite(current_dimension)||!isint(current_dimension,minimum_dimension_requirement),run('error inv_inp'););
		copy(coordinates[p],[0,current_dimension-1],2,dimensions,1);
	);
	[coordinates,0];"

(1) a[-2,-1] y # Convert last image to dynamic array image
1,1,1,100%
1,1,1,1,$loop_limit

repeat 1+$use_multithreading {
	iter:=$>
	eval[-1] :"
		begin_t(
			if($use_seed,srand($iter+t+$seed));
			const dimensions=$dimensions;
			const decrement_dimensions=dimensions-1;
			const iteration_index=dimensions<<1;
			const step_size=$additional_thickness+1;
			const max_split_count=$split;
			const probability=$probability;
			const coordinates_and_iterations_array_size=$coordinates_and_iterations_array_size;
			const start_array_size=min($prod_dimensions,$prod_loops_and_iter,1<<16);
			const post_mt=$use_multithreading&&!$iter;
			current_selection=vector(#coordinates_and_iterations_array_size);

			if(post_mt
			,condition()=da_size(#modifiable_array_ind)<$_cpus&&loop_limit--;
			,condition()=da_size(#modifiable_array_ind)&&loop_limit--;
			);

			probability==1?(
				calc_probability()=bool_ins_mod;
			):(
				const negate_probability=1-probability;
				calc_probability()=bool_ins_mod&&arg_coordinates[iteration_index]>1?(u>negate_probability):bool_ins_mod;
			);

			calculate_distance(arg_part_coordinates)=arg_part_coordinates[1]-arg_part_coordinates[0]+1;

			array_subdivision(arg_coordinates)=(
				bool_ins_mod=++arg_coordinates[iteration_index]<$maximum_iteration;
				starting_coordinate=v(0,decrement_dimensions);
				end_coordinate=starting_coordinate+dimensions;
				part_coordinates=arg_coordinates[starting_coordinate,2,dimensions];
				max_split_possible=int(calculate_distance(part_coordinates)/step_size);
				max_split_possible>1?(
					rf_split_count=v(2,min(max_split_possible,max_split_count))-1;
					end_point=1+part_coordinates[1]-rf_split_count*step_size;
					repeat(rf_split_count,
						left_coordinate=v(arg_coordinates[starting_coordinate]+step_size,end_point);
						arg_coordinates[end_coordinate]=left_coordinate-1;
						if(calc_probability()
						,da_push(#modifiable_array_ind,arg_coordinates);
						,da_push(#unmodifiable_array_ind,arg_coordinates);
						);
						arg_coordinates[starting_coordinate]=left_coordinate;
						end_point+=step_size;
					);
					arg_coordinates[end_coordinate]=part_coordinates[1];
					if(calc_probability()
					,da_push(#modifiable_array_ind,arg_coordinates);
					,da_push(#unmodifiable_array_ind,arg_coordinates);
					);
				):(
					da_push(#unmodifiable_array_ind,arg_coordinates);
				);
			);
		);
		modifiable_array_ind=$start_img_ind+(x<<1);
		unmodifiable_array_ind=modifiable_array_ind+1;
		loop_limit=i;
		if(!post_mt,
			elm_count_mod=da_size(#modifiable_array_ind);
			elm_count_unm=da_size(#unmodifiable_array_ind);
			resize(#modifiable_array_ind,1,start_array_size,1,coordinates_and_iterations_array_size,0);
			resize(#unmodifiable_array_ind,1,start_array_size,1,coordinates_and_iterations_array_size,0);
			i[#modifiable_array_ind,start_array_size-1]=elm_count_mod;
			i[#unmodifiable_array_ind,start_array_size-1]=elm_count_unm;
		);
		while(condition(),
			current_selection_of_modifiable_array=v(0,da_size(#modifiable_array_ind)-1);
			copy(current_selection[0],i[#modifiable_array_ind,current_selection_of_modifiable_array],coordinates_and_iterations_array_size,1,h(#modifiable_array_ind));
			da_remove(#modifiable_array_ind,current_selection_of_modifiable_array);
			array_subdivision(current_selection);
		);
		final_da_size=da_size(#modifiable_array_ind);
		da_freeze(#modifiable_array_ind);
		da_freeze(#unmodifiable_array_ind);
		end(
			post_mt&&loop_limit&&final_da_size?(
				set('new_loop_limit',loop_limit);
				set('continue_loop_in_multithreaded_mode',1);
			):(
				set('continue_loop_in_multithreaded_mode',0);
			);
		);"

	if $continue_loop_in_multithreaded_mode
		rm.
		{min($new_loop_limit,$_cpus)},1,1,1,$new_loop_limit>>1
		__rep_random_subdivision_of_dimension_boundaries_shuffle[$start_img_ind]
		s[$start_img_ind] y,$_cpus
		foreach[$start_img_ind--3] { ({h}) a[-2,-1] y 1,1,1,100% }
	else
		rm.
		break
	fi
}

if $use_multithreading
	mirror[$start_img_ind--1:2] y
	rep_zip[$start_img_ind--1] y
else
	a[$start_img_ind--1] y
fi

status $old_status

There are two problems with current G’MIC.

Somehow loop_limit turns uninitialized. And unable to use macro outside. These are not the case before.

Coud you exhibit the simplest possible example that reproduces this problem ?
There has not been a lot of changes in the math parser recently, but who knows, a bug is still possible.
Thanks!

Sorry for false alarm, I had to wrap around a variable under JIT compiler with "s. It works just fine now.

EDIT: I’ll have to check da_freeze function. I did da_freeze and seems to do nothing after all that.

EDIT: All checks out. No clue what happened. Apology again.

  • 2025/05/02: Release of G’MIC 3.5.4.
1 Like
  • 2025/05/20: Release of G’MIC 3.5.5.

(contains minor bugfixes, and better Qt6 compatibility).

I’ve started the work for next “major” release 3.6.0 of G’MIC.
Filters update is now disabled for 3.5.x versions.
3.6.0 pre-release binaries are available on the G’MIC website, if you want to stay up-to-date with the version under development.

anim360-ezgif.com-optimize

4 Likes

Multi-threading 3D rendering? Can we have reflections? Probably too much, but I have a few ideas here.

I think your forgot the word ‘saving’ in that line :slight_smile:

1 Like

I’ve spent the last two days working on a fairly major rewrite of the parser in the G’MIC interpreter, mainly to reduce the number of string comparisons (which was quite high before). I’m almost done now, and there are very few string operations left (copies and comparisons).
So I naturally wanted to run a real-world benchmark to enjoy the speed boost of the new interpreter!

And… I got a 2.5% improvement on the computation time (for a process that takes around a minute, I go from 62.928s to 61.256 s). :confused:

Life is hard :cry:

I’ve uploaded the corresponding pre-release packages, do not hesitate to try!

What could be a good testing command?

I’ve used this one:

foo :
  it https://gmic.eu/gmic_stdlib.360
  tic v -2
  repeat 10 { +l[0] {
    parse_gui update
    rm
  } }
  toc q

The parse_gui command requires interpreter parsing a lot, and is thus a good candidate for an interpreter benchmark.

My latest pre-release build gives this:

$ gmic foo
[gmic]./ Start G'MIC interpreter (v.3.6.0).
  > Parsing done!                                                   
  >> Generate output, in 'update' mode.
  >> Output done!                                                    
  > Parsing done!                                                   
  >> Generate output, in 'update' mode.
  >> Output done!                                                    
  > Parsing done!                                                   
  >> Generate output, in 'update' mode.
  >> Output done!                                                    
  > Parsing done!                                                   
  >> Generate output, in 'update' mode.
  >> Output done!                                                    
  > Parsing done!                                                   
  >> Generate output, in 'update' mode.
  >> Output done!                                                    
  > Parsing done!                                                   
  >> Generate output, in 'update' mode.
  >> Output done!                                                    
  > Parsing done!                                                   
  >> Generate output, in 'update' mode.
  >> Output done!                                                    
  > Parsing done!                                                   
  >> Generate output, in 'update' mode.
  >> Output done!                                                    
  > Parsing done!                                                   
  >> Generate output, in 'update' mode.
  >> Output done!                                                    
  > Parsing done!                                                   
  >> Generate output, in 'update' mode.
  >> Output done!                                                    
[gmic]./foo/ Elapsed time: 60.993 s.

(took 62.928s with G’MIC 3.5.5).

I’ve run this experiment several times, alongside with time and I can confirm, the speed improvement is just a bit more than 2%, compared to previous stable version 3.5.5.

But, thinking about this… After 17 years of developpement, I suppose 2% is still something!

Definitely not spectacular, but if I had gained 10% speed, maybe I would have had to question the quality of the code I had produced up to that point :slight_smile:

Just think about it like this: how many piles of 2% have you stacked all this time ? :slight_smile: I just hope there’ll be many more.

My results with Version 3.6.0 (pre-release #25061922), not the latest then:

Power save mode :
[gmic]./bench/ Elapsed time: 100.51 s.

Performance mode:
[gmic]./bench/ Elapsed time: 41.201 s.

Now building latest version…

Results with Version 3.6.0 (pre-release #25072421):

Power save mode:
[gmic]./bench/ Elapsed time: 101.311 s.

Performance mode;
[gmic]./bench/ Elapsed time: 42.763 s.

:upside_down_face:

Edit: Tried with power plug and firefox closed, i get between 40.5 and 40.9 for both versions.

EDIT:
Built version 3.5.5 just to have the same base as you:
On perf mode, power plug etc.
[gmic]./bench/ Elapsed time: 44.926 s. << 1st run
Got as low as 48.776 s.

Power save mode :
[gmic]./bench/ Elapsed time: 107.118 s.

I’m not so surprised, The speed improvement is already hardly noticeable for a process that takes a bit more than a minute, so with a faster CPU, you should probably try more iterations to see a difference (30 ?).
Also, the tic and toc mechanism is not ideal, as it just computes the elapsed time, not the CPU time used.
I’ve used $ time gmic foo and looked at the user timing, and this gives useful information (and a 2% gain everytime).

Yes, but with time you are counting the time spent for downloading the file, right?

So i downloaded the file once and for all and modified the command accordingly.
Here’s what i got :

latest

real	2m4.331s
user	3m30.416s
sys	0m0.466s

prev 360

real	2m6.782s
user	3m27.610s
sys	0m0.486s

355

real	2m21.420s
user	9m42.525s
sys	0m1.087s

Not sure how to interpret these though.
That’s 30 times, btw.

  • 2025/08/21: Release of G’MIC 3.6.0.

:tada: :partying_face:

1 Like

Thanks for keeping versions for 2.10x up to date, David (still haven’t installed 3.0x yet and so far no plans to do so). Just tested a few presets for 3.6.0 and works well.

As a side note, the links from the download site don’t work; had to go to the files link site to download (Index of /files). Not sure why this is. :slight_smile: