@@ -50,6 +50,24 @@ def compute_obj(X, y, w, alpha, l1_ratio=1):
5050
5151models = ["lasso" , "enet" ]
5252
53+ # Global warmup to ensure all libraries are JIT-compiled before any benchmarking
54+ print ("Performing global warmup to trigger JIT compilation..." )
55+ for model_name in models :
56+ if model_name == "lasso" :
57+ warmup_sklearn = Lasso_sklearn (alpha = alpha , fit_intercept = False , tol = 1e-12 )
58+ warmup_skglm = Lasso (alpha = alpha , fit_intercept = False , tol = 1e-12 )
59+ else : # enet
60+ warmup_sklearn = Enet_sklearn (
61+ alpha = alpha , fit_intercept = False , tol = 1e-12 , l1_ratio = 0.5 )
62+ warmup_skglm = ElasticNet (
63+ alpha = alpha , fit_intercept = False , tol = 1e-12 , l1_ratio = 0.5 )
64+
65+ print (f"Warming up { model_name } models..." )
66+ # Use the full dataset for warmup to ensure all code paths are compiled
67+ _ = warmup_sklearn .fit (X , y )
68+ _ = warmup_skglm .fit (X , y )
69+
70+ # Now start the actual benchmarking
5371fig , axarr = plt .subplots (2 , 1 , constrained_layout = True )
5472
5573for ax , model , l1_ratio in zip (axarr , models , [1 , 0.5 ]):
@@ -61,34 +79,94 @@ def compute_obj(X, y, w, alpha, l1_ratio=1):
6179 time_dict ["sklearn" ] = list ()
6280 time_dict ["us" ] = list ()
6381
64- # Remove compilation time
82+ # Perform warmup runs with a small subset to trigger compilation
83+ _ = dict_sklearn [model ].fit (X [:10 ], y [:10 ])
84+ _ = dict_ours [model ].fit (X [:10 ], y [:10 ])
85+
86+ # Find optimal solution for reference
6587 dict_ours [model ].max_iter = 10_000
6688 w_star = dict_ours [model ].fit (X , y ).coef_
6789 pobj_star = compute_obj (X , y , w_star , alpha , l1_ratio )
90+
91+ # Reset models with fresh instances after using them for reference solution
92+ if model == "lasso" :
93+ dict_sklearn [model ] = Lasso_sklearn (
94+ alpha = alpha , fit_intercept = False , tol = 1e-12 )
95+ dict_ours [model ] = Lasso (
96+ alpha = alpha , fit_intercept = False , tol = 1e-12 )
97+ else : # enet
98+ dict_sklearn [model ] = Enet_sklearn (
99+ alpha = alpha , fit_intercept = False , tol = 1e-12 , l1_ratio = 0.5 )
100+ dict_ours [model ] = ElasticNet (
101+ alpha = alpha , fit_intercept = False , tol = 1e-12 , l1_ratio = 0.5 )
102+
103+ # # --------------------
104+ # # DEBUG: measure compile vs. solver iteration cost
105+ # debug_model = dict_ours[model]
106+
107+ # # 1) compile + first iteration
108+ # debug_model.max_iter = 1
109+ # t0 = time.time()
110+ # _ = debug_model.fit(X, y)
111+ # debug_total = time.time() - t0
112+ # print(f"[DEBUG] {model}: compile+1 iter = {debug_total:.3f}s")
113+
114+ # # 2) only solver iteration (post-compile)
115+ # debug_model.warm_start = True
116+ # debug_model.max_iter = 1
117+ # t0 = time.time()
118+ # _ = debug_model.fit(X, y)
119+ # debug_iter = time.time() - t0
120+ # print(f"[DEBUG] {model}: first iter post-compile = {debug_iter:.3f}s")
121+
122+ # # 3) iteration cost with fixpoint update
123+ # debug_model.warm_start = False
124+ # debug_model.ws_strategy = "fixpoint"
125+ # debug_model.max_iter = 1
126+ # t0 = time.time()
127+ # _ = debug_model.fit(X, y)
128+ # debug_fixpoint = time.time() - t0
129+ # print(f"[DEBUG] {model}: first iter fixpoint = {debug_fixpoint:.3f}s")
130+ # # --------------------
131+
132+ # warm up JIT so that no compile goes into your timing measurements
133+ print ("warming up skglm on a little subset to pay the compile cost up‐front…" )
134+ X_small , y_small = X [:10 ], y [:10 ]
135+ _ = Lasso (alpha = alpha , fit_intercept = False , tol = 1e-12 ).fit (X_small , y_small )
136+ _ = ElasticNet (alpha = alpha , fit_intercept = False , tol = 1e-12 ,
137+ l1_ratio = 0.5 ).fit (X_small , y_small )
138+
139+ print ("Warmup complete!" )
140+
68141 for n_iter_sklearn in np .unique (np .geomspace (1 , 50 , num = 15 ).astype (int )):
69142 dict_sklearn [model ].max_iter = n_iter_sklearn
143+ print (f" sklearn iterations: { n_iter_sklearn } " )
70144
71145 t_start = time .time ()
72146 w_sklearn = dict_sklearn [model ].fit (X , y ).coef_
147+
73148 time_dict ["sklearn" ].append (time .time () - t_start )
74149 pobj_dict ["sklearn" ].append (compute_obj (X , y , w_sklearn , alpha , l1_ratio ))
75150
76- for n_iter_us in range ( 1 , 10 ):
151+ for n_iter_us in np . unique ( np . geomspace ( 1 , 50 , num = 15 ). astype ( int ) ):
77152 dict_ours [model ].max_iter = n_iter_us
153+ print (f" skglm iterations: { n_iter_us } " )
154+
78155 t_start = time .time ()
79156 w = dict_ours [model ].fit (X , y ).coef_
157+
80158 time_dict ["us" ].append (time .time () - t_start )
81159 pobj_dict ["us" ].append (compute_obj (X , y , w , alpha , l1_ratio ))
82160
83161 ax .semilogy (
84- time_dict ["sklearn" ], pobj_dict ["sklearn" ] - pobj_star , label = 'sklearn' )
162+ time_dict ["sklearn" ], pobj_dict ["sklearn" ] - pobj_star , marker = 'o' , label = 'sklearn' )
85163 ax .semilogy (
86- time_dict ["us" ], pobj_dict ["us" ] - pobj_star , label = 'skglm' )
164+ time_dict ["us" ], pobj_dict ["us" ] - pobj_star , marker = 'o' , label = 'skglm' )
87165
88166 ax .set_ylim ((1e-10 , 1 ))
89167 ax .set_title (model )
90168 ax .legend ()
91169 ax .set_ylabel ("Objective suboptimality" )
92170
93171axarr [1 ].set_xlabel ("Time (s)" )
94- plt .show (block = False )
172+ plt .show ()
0 commit comments