Prepare NEW_SIMD_MODE for -a 1 kernels
[hashcat.git] / src / oclHashcat.c
index 6c2069f..996baeb 100644 (file)
@@ -1,6 +1,7 @@
 /**
  * Authors.....: Jens Steube <jens.steube@gmail.com>
  *               Gabriele Gristina <matrix@hashcat.net>
+ *               magnum <john.magnum@hushmail.com>
  *
  * License.....: MIT
  */
@@ -18,6 +19,8 @@ const char *PROGNAME            = "oclHashcat";
 const uint  VERSION_BIN         = 210;
 const uint  RESTORE_MIN         = 210;
 
+double TARGET_MS_PROFILE[3]     = { 8, 16, 96 };
+
 #define INCR_RULES              10000
 #define INCR_SALTS              100000
 #define INCR_MASKS              1000
@@ -30,7 +33,7 @@ const uint  RESTORE_MIN         = 210;
 #define MARKOV_DISABLE          0
 #define MARKOV_CLASSIC          0
 #define BENCHMARK               0
-#define BENCHMARK_MODE          1
+#define BENCHMARK_REPEATS       2
 #define RESTORE                 0
 #define RESTORE_TIMER           60
 #define RESTORE_DISABLE         0
@@ -134,7 +137,7 @@ const uint  RESTORE_MIN         = 210;
 
 #define MAX_DICTSTAT            10000
 
-#define NUM_DEFAULT_BENCHMARK_ALGORITHMS 130
+#define NUM_DEFAULT_BENCHMARK_ALGORITHMS 131
 
 #define global_free(attr)       \
 {                               \
@@ -180,6 +183,7 @@ static uint default_benchmark_algorithms[NUM_DEFAULT_BENCHMARK_ALGORITHMS] =
   5600,
   7300,
   7500,
+  13100,
   8300,
   11100,
   11200,
@@ -216,12 +220,12 @@ static uint default_benchmark_algorithms[NUM_DEFAULT_BENCHMARK_ALGORITHMS] =
   101,
   111,
   1711,
-  3000, // broken in osx
+  3000,
   1000,
   1100,
   2100,
   12800,
-  1500, // broken in osx
+  1500,
   12400,
   500,
   3200,
@@ -274,7 +278,7 @@ static uint default_benchmark_algorithms[NUM_DEFAULT_BENCHMARK_ALGORITHMS] =
   10410,
   10500,
   10600,
-  10700, // broken in osx
+  10700,
   9000,
   5200,
   6800,
@@ -334,11 +338,6 @@ const char *USAGE_BIG[] =
   "  -h,  --help                        Print help",
   "       --quiet                       Suppress output",
   "",
-  "* Benchmark:",
-  "",
-  "  -b,  --benchmark                   Run benchmark",
-  "       --benchmark-mode=NUM          Benchmark-mode, see references below",
-  "",
   "* Misc:",
   "",
   "       --hex-charset                 Assume charset is given in hex",
@@ -387,21 +386,19 @@ const char *USAGE_BIG[] =
   "",
   "* Resources:",
   "",
+  "  -b,  --benchmark                   Run benchmark",
+  "       --benchmark-repeats=NUM       Repeat the kernel on the device NUM times to increase benchmark accuracy",
   "  -c,  --segment-size=NUM            Size in MB to cache from the wordfile",
   "       --bitmap-min=NUM              Minimum number of bits allowed for bitmaps",
   "       --bitmap-max=NUM              Maximum number of bits allowed for bitmaps",
-  #ifndef OSX
   "       --cpu-affinity=STR            Locks to CPU devices, separate with comma",
-  #else
-  "       --cpu-affinity=STR            Locks to CPU devices, separate with comma (disabled on OSX)",
-  #endif
   "       --opencl-platforms=STR        OpenCL platforms to use, separate with comma",
   "  -d,  --opencl-devices=STR          OpenCL devices to use, separate with comma",
   "       --opencl-device-types=STR     OpenCL device-types to use, separate with comma, see references below",
-  "       --opencl-vector-width=NUM     OpenCL vector-width (either 1, 2, 4 or 8), overrides value from device query",
+  "       --opencl-vector-width=NUM     OpenCL vector-width (either 1, 2, 4, 8 or 16), overrides value from device query",
   "  -w,  --workload-profile=NUM        Enable a specific workload profile, see references below",
-  "  -n,  --kernel-accel=NUM            Workload tuning: 1, 8, 40, 80, 160",
-  "  -u,  --kernel-loops=NUM            Workload fine-tuning: 8 - 1024",
+  "  -n,  --kernel-accel=NUM            Workload tuning, increase the outer-loop step size",
+  "  -u,  --kernel-loops=NUM            Workload tuning, increase the inner-loop step size",
   "       --gpu-temp-disable            Disable temperature and fanspeed readings and triggers",
   #ifdef HAVE_HWMON
   "       --gpu-temp-abort=NUM          Abort session if GPU temperature reaches NUM degrees celsius",
@@ -447,14 +444,9 @@ const char *USAGE_BIG[] =
   "",
   "* Workload Profile:",
   "",
-  "    1 = Reduced performance profile (low latency desktop)",
-  "    2 = Default performance profile",
-  "    3 = Tuned   performance profile (high latency desktop)",
-  "",
-  "* Benchmark Settings:",
-  "",
-  "    0 = Manual Tuning",
-  "    1 = Performance Tuning, default",
+  "    1 = Interactive performance profile, kernel execution runtime to  8ms, lower latency desktop, lower speed",
+  "    2 = Default     performance profile, kernel execution runtime to 16ms, economic setting",
+  "    3 = Headless    performance profile, kernel execution runtime to 96ms, higher latency desktop, higher speed",
   "",
   "* OpenCL device-types:",
   "",
@@ -492,7 +484,7 @@ const char *USAGE_BIG[] =
   "   ?l = abcdefghijklmnopqrstuvwxyz",
   "   ?u = ABCDEFGHIJKLMNOPQRSTUVWXYZ",
   "   ?d = 0123456789",
-  "   ?s =  !\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~",
+  "   ?s =  !\"#$%%&'()*+,-./:;<=>?@[\\]^_`{|}~",
   "   ?a = ?l?u?d?s",
   "   ?b = 0x00 - 0xff",
   "",
@@ -587,6 +579,7 @@ const char *USAGE_BIG[] =
   "  11100 = PostgreSQL Challenge-Response Authentication (MD5)",
   "  11200 = MySQL Challenge-Response Authentication (SHA1)",
   "  11400 = SIP digest authentication (MD5)",
+  "  13100 = Kerberos 5 TGS-REP etype 23",
   "",
   "[[ Forums, CMS, E-Commerce, Frameworks, Middleware, Wiki, Management ]]",
   "",
@@ -742,6 +735,33 @@ const char *USAGE_BIG[] =
  * oclHashcat specific functions
  */
 
+static double get_avg_exec_time (hc_device_param_t *device_param, const int last_num_entries)
+{
+  int exec_pos = (int) device_param->exec_pos - last_num_entries;
+
+  if (exec_pos < 0) exec_pos += EXEC_CACHE;
+
+  double exec_ms_sum = 0;
+
+  int exec_ms_cnt = 0;
+
+  for (int i = 0; i < last_num_entries; i++)
+  {
+    double exec_ms = device_param->exec_ms[(exec_pos + i) % EXEC_CACHE];
+
+    if (exec_ms)
+    {
+      exec_ms_sum += exec_ms;
+
+      exec_ms_cnt++;
+    }
+  }
+
+  if (exec_ms_cnt == 0) return 0;
+
+  return exec_ms_sum / exec_ms_cnt;
+}
+
 void status_display_automat ()
 {
   FILE *out = stdout;
@@ -781,6 +801,23 @@ void status_display_automat ()
     fprintf (out, "%llu\t%f\t", (unsigned long long int) speed_cnt, speed_ms);
   }
 
+  /**
+   * exec time
+   */
+
+  fprintf (out, "EXEC_RUNTIME\t");
+
+  for (uint device_id = 0; device_id < data.devices_cnt; device_id++)
+  {
+    hc_device_param_t *device_param = &data.devices_param[device_id];
+
+    if (device_param->skipped) continue;
+
+    double exec_ms_avg = get_avg_exec_time (device_param, EXEC_CACHE);
+
+    fprintf (out, "%f\t", exec_ms_avg);
+  }
+
   /**
    * words_cur
    */
@@ -878,6 +915,10 @@ void status_display_automat ()
   }
   #endif // HAVE_HWMON
 
+  /**
+   * flush
+   */
+
   #ifdef _WIN
   fputc ('\r', out);
   fputc ('\n', out);
@@ -1161,6 +1202,23 @@ void status_display ()
     }
   }
 
+  /**
+   * exec time
+   */
+
+  double exec_all_ms[DEVICES_MAX] = { 0 };
+
+  for (uint device_id = 0; device_id < data.devices_cnt; device_id++)
+  {
+    hc_device_param_t *device_param = &data.devices_param[device_id];
+
+    if (device_param->skipped) continue;
+
+    double exec_ms_avg = get_avg_exec_time (device_param, EXEC_CACHE);
+
+    exec_all_ms[device_id] = exec_ms_avg;
+  }
+
   /**
    * timers
    */
@@ -1379,7 +1437,7 @@ void status_display ()
 
     format_speed_display (hashes_dev_ms[device_id] * 1000, display_dev_cur, sizeof (display_dev_cur));
 
-    log_info ("Speed.Dev.#%d...: %9sH/s", device_id + 1, display_dev_cur);
+    log_info ("Speed.Dev.#%d...: %9sH/s (%0.2fms)", device_id + 1, display_dev_cur, exec_all_ms[device_id]);
   }
 
   char display_all_cur[16] = { 0 };
@@ -1554,11 +1612,7 @@ void status_display ()
         }
         else if (device_param->vendor_id == VENDOR_ID_NV)
         {
-          #ifdef LINUX
           hm_device_val_to_str ((char *) fanspeed, HM_STR_BUF_SIZE, "%", hm_get_fanspeed_with_device_id (device_id));
-          #else
-          hm_device_val_to_str ((char *) fanspeed, HM_STR_BUF_SIZE, "rpm", hm_get_fanspeed_with_device_id (device_id));
-          #endif
         }
 
         log_info ("HWMon.GPU.#%d...: %s Util, %s Temp, %s Fan", device_id + 1, utilization, temperature, fanspeed);
@@ -1629,6 +1683,23 @@ static void status_benchmark ()
     }
   }
 
+  /**
+   * exec time
+   */
+
+  double exec_all_ms[DEVICES_MAX] = { 0 };
+
+  for (uint device_id = 0; device_id < data.devices_cnt; device_id++)
+  {
+    hc_device_param_t *device_param = &data.devices_param[device_id];
+
+    if (device_param->skipped) continue;
+
+    double exec_ms_avg = get_avg_exec_time (device_param, EXEC_CACHE);
+
+    exec_all_ms[device_id] = exec_ms_avg;
+  }
+
   for (uint device_id = 0; device_id < data.devices_cnt; device_id++)
   {
     hc_device_param_t *device_param = &data.devices_param[device_id];
@@ -1641,7 +1712,7 @@ static void status_benchmark ()
 
     format_speed_display (hashes_dev_ms[device_id] * 1000, display_dev_cur, sizeof (display_dev_cur));
 
-    log_info ("Speed.Dev.#%d.: %9sH/s", device_id + 1, display_dev_cur);
+    log_info ("Speed.Dev.#%d.: %9sH/s (%0.2fms)", device_id + 1, display_dev_cur, exec_all_ms[device_id]);
   }
 
   char display_all_cur[16] = { 0 };
@@ -1672,7 +1743,7 @@ static void generate_source_kernel_filename (const uint attack_exec, const uint
     snprintf (source_file, 255, "%s/OpenCL/m%05d.cl", shared_dir, (int) kern_type);
 }
 
-static void generate_cached_kernel_filename (const uint attack_exec, const uint attack_kern, const uint kern_type, char *profile_dir, char *device_name_chksum, char *cached_file)
+static void generate_cached_kernel_filename (const uint attack_exec, const uint attack_kern, const uint kern_type, char *profile_dir, const char *device_name_chksum, char *cached_file)
 {
   if (attack_exec == ATTACK_EXEC_INSIDE_KERNEL)
   {
@@ -1701,7 +1772,7 @@ static void generate_source_kernel_mp_filename (const uint opti_type, const uint
   }
 }
 
-static void generate_cached_kernel_mp_filename (const uint opti_type, const uint opts_type, char *profile_dir, char *device_name_chksum, char *cached_file)
+static void generate_cached_kernel_mp_filename (const uint opti_type, const uint opts_type, char *profile_dir, const char *device_name_chksum, char *cached_file)
 {
   if ((opti_type & OPTI_TYPE_BRUTE_FORCE) && (opts_type & OPTS_TYPE_PT_GENERATE_BE))
   {
@@ -1718,7 +1789,7 @@ static void generate_source_kernel_amp_filename (const uint attack_kern, char *s
   snprintf (source_file, 255, "%s/OpenCL/amp_a%d.cl", shared_dir, attack_kern);
 }
 
-static void generate_cached_kernel_amp_filename (const uint attack_kern, char *profile_dir, char *device_name_chksum, char *cached_file)
+static void generate_cached_kernel_amp_filename (const uint attack_kern, char *profile_dir, const char *device_name_chksum, char *cached_file)
 {
   snprintf (cached_file, 255, "%s/kernels/amp_a%d.%s.kernel", profile_dir, attack_kern, device_name_chksum);
 }
@@ -1766,54 +1837,6 @@ static uint convert_from_hex (char *line_buf, const uint line_len)
   return (line_len);
 }
 
-static uint count_lines (FILE *fd)
-{
-  uint cnt = 0;
-
-  char *buf = (char *) mymalloc (BUFSIZ + 1);
-
-  size_t nread_tmp = 0;
-
-  char *ptr = buf;
-
-  while (!feof (fd))
-  {
-    size_t nread = fread (buf, sizeof (char), BUFSIZ, fd);
-    nread_tmp    = nread;
-
-    if (nread < 1) continue;
-
-    ptr = buf;
-
-    do
-    {
-      if (*ptr++ == '\n') cnt++;
-
-    } while (nread--);
-  }
-
-  // special case (if last line did not contain a newline char ... at the very end of the file)
-
-  if (nread_tmp > 3)
-  {
-    ptr -= 2;
-
-    if (*ptr != '\n')
-    {
-      ptr--;
-
-      if (*ptr != '\n') // needed ? different on windows systems?
-      {
-        cnt++;
-      }
-    }
-  }
-
-  myfree (buf);
-
-  return cnt;
-}
-
 static void clear_prompt ()
 {
   fputc ('\r', stdout);
@@ -2350,21 +2373,21 @@ static void save_hash ()
   unlink (old_hashfile);
 }
 
-static float find_kernel_blocks_div (const u64 total_left, const uint kernel_blocks_all)
+static float find_kernel_power_div (const u64 total_left, const uint kernel_power_all)
 {
-  // function called only in case kernel_blocks_all > words_left)
+  // function called only in case kernel_power_all > words_left
 
-  float kernel_blocks_div = (float) (total_left) / kernel_blocks_all;
+  float kernel_power_div = (float) (total_left) / kernel_power_all;
 
-  kernel_blocks_div += kernel_blocks_div / 100;
+  kernel_power_div += kernel_power_div / 100;
 
-  u32 kernel_blocks_new = (u32) (kernel_blocks_all * kernel_blocks_div);
+  u32 kernel_power_new = (u32) (kernel_power_all * kernel_power_div);
 
-  while (kernel_blocks_new < total_left)
+  while (kernel_power_new < total_left)
   {
-    kernel_blocks_div += kernel_blocks_div / 100;
+    kernel_power_div += kernel_power_div / 100;
 
-    kernel_blocks_new = (u32) (kernel_blocks_all * kernel_blocks_div);
+    kernel_power_new = (u32) (kernel_power_all * kernel_power_div);
   }
 
   if (data.quiet == 0)
@@ -2382,12 +2405,12 @@ static float find_kernel_blocks_div (const u64 total_left, const uint kernel_blo
     fflush (stdout);
   }
 
-  if ((kernel_blocks_all * kernel_blocks_div) < 8) return 1;
+  if ((kernel_power_all * kernel_power_div) < 8) return 1;
 
-  return kernel_blocks_div;
+  return kernel_power_div;
 }
 
-static void run_kernel (const uint kern_run, hc_device_param_t *device_param, const uint num)
+static void run_kernel (const uint kern_run, hc_device_param_t *device_param, const uint num, const uint event_update)
 {
   uint num_elements = num;
 
@@ -2421,31 +2444,54 @@ static void run_kernel (const uint kern_run, hc_device_param_t *device_param, co
   hc_clSetKernelArg (data.ocl, kernel, 30, sizeof (cl_uint), device_param->kernel_params[30]);
   hc_clSetKernelArg (data.ocl, kernel, 31, sizeof (cl_uint), device_param->kernel_params[31]);
 
+  hc_timer_t timer;
+
+  hc_timer_set (&timer);
+
   if ((data.opts_type & OPTS_TYPE_PT_BITSLICE) && (data.attack_mode == ATTACK_MODE_BF))
   {
     const size_t global_work_size[3] = { num_elements,        32, 1 };
     const size_t local_work_size[3]  = { kernel_threads / 32, 32, 1 };
 
-    hc_clEnqueueNDRangeKernel (data.ocl, device_param->command_queue, kernel, 2, NULL, global_work_size, local_work_size, 0, NULL, NULL, true);
+    hc_clEnqueueNDRangeKernel (data.ocl, device_param->command_queue, kernel, 2, NULL, global_work_size, local_work_size, 0, NULL, NULL);
   }
   else
   {
-    const size_t global_work_size[3] = { num_elements,   1, 1 };
-    const size_t local_work_size[3]  = { kernel_threads, 1, 1 };
+    size_t workgroup_size = 0;
 
-    const cl_int rc = hc_clEnqueueNDRangeKernel (data.ocl, device_param->command_queue, kernel, 1, NULL, global_work_size, local_work_size, 0, NULL, NULL, false);
+    hc_clGetKernelWorkGroupInfo (data.ocl, kernel, device_param->device, CL_KERNEL_WORK_GROUP_SIZE, sizeof (size_t), &workgroup_size, NULL);
 
-    if (rc != CL_SUCCESS)
-    {
-      const size_t local_work_size_fallback[3]  = { 1, 1, 1 };
+    if (kernel_threads > workgroup_size) kernel_threads = workgroup_size;
 
-      hc_clEnqueueNDRangeKernel (data.ocl, device_param->command_queue, kernel, 1, NULL, global_work_size, local_work_size_fallback, 0, NULL, NULL, true);
-    }
+    const size_t global_work_size[3] = { num_elements,   1, 1 };
+    const size_t local_work_size[3]  = { kernel_threads, 1, 1 };
+
+    hc_clEnqueueNDRangeKernel (data.ocl, device_param->command_queue, kernel, 1, NULL, global_work_size, local_work_size, 0, NULL, NULL);
   }
 
   hc_clFlush (data.ocl, device_param->command_queue);
 
   hc_clFinish (data.ocl, device_param->command_queue);
+
+  if (event_update)
+  {
+    float exec_time;
+
+    hc_timer_get (timer, exec_time);
+
+    uint exec_pos = device_param->exec_pos;
+
+    device_param->exec_ms[exec_pos] = exec_time;
+
+    exec_pos++;
+
+    if (exec_pos == EXEC_CACHE)
+    {
+      exec_pos = 0;
+    }
+
+    device_param->exec_pos = exec_pos;
+  }
 }
 
 static void run_kernel_mp (const uint kern_run, hc_device_param_t *device_param, const uint num)
@@ -2462,7 +2508,7 @@ static void run_kernel_mp (const uint kern_run, hc_device_param_t *device_param,
   // causes problems with special threads like in bcrypt
   // const uint kernel_threads = device_param->kernel_threads;
 
-  const uint kernel_threads = KERNEL_THREADS;
+  uint kernel_threads = KERNEL_THREADS;
 
   while (num_elements % kernel_threads) num_elements++;
 
@@ -2501,17 +2547,14 @@ static void run_kernel_mp (const uint kern_run, hc_device_param_t *device_param,
                         break;
   }
 
+  size_t workgroup_size = 0;
+  hc_clGetKernelWorkGroupInfo (data.ocl, kernel, device_param->device, CL_KERNEL_WORK_GROUP_SIZE, sizeof(size_t), &workgroup_size, NULL);
+  if (kernel_threads > workgroup_size) kernel_threads = workgroup_size;
+
   const size_t global_work_size[3] = { num_elements, 1, 1 };
   const size_t local_work_size[3]  = { kernel_threads, 1, 1 };
 
-  const cl_int rc = hc_clEnqueueNDRangeKernel (data.ocl, device_param->command_queue, kernel, 1, NULL, global_work_size, local_work_size, 0, NULL, NULL, false);
-
-  if (rc != CL_SUCCESS)
-  {
-    const size_t local_work_size_fallback[3]  = { 1, 1, 1 };
-
-    hc_clEnqueueNDRangeKernel (data.ocl, device_param->command_queue, kernel, 1, NULL, global_work_size, local_work_size_fallback, 0, NULL, NULL, true);
-  }
+  hc_clEnqueueNDRangeKernel (data.ocl, device_param->command_queue, kernel, 1, NULL, global_work_size, local_work_size, 0, NULL, NULL);
 
   hc_clFlush (data.ocl, device_param->command_queue);
 
@@ -2528,10 +2571,14 @@ static void run_kernel_tb (hc_device_param_t *device_param, const uint num)
 
   cl_kernel kernel = device_param->kernel_tb;
 
+  size_t workgroup_size = 0;
+  hc_clGetKernelWorkGroupInfo (data.ocl, kernel, device_param->device, CL_KERNEL_WORK_GROUP_SIZE, sizeof (size_t), &workgroup_size, NULL);
+  if (kernel_threads > workgroup_size) kernel_threads = workgroup_size;
+
   const size_t global_work_size[3] = { num_elements, 1, 1 };
   const size_t local_work_size[3]  = { kernel_threads,  1, 1 };
 
-  hc_clEnqueueNDRangeKernel (data.ocl, device_param->command_queue, kernel, 1, NULL, global_work_size, local_work_size, 0, NULL, NULL, true);
+  hc_clEnqueueNDRangeKernel (data.ocl, device_param->command_queue, kernel, 1, NULL, global_work_size, local_work_size, 0, NULL, NULL);
 
   hc_clFlush (data.ocl, device_param->command_queue);
 
@@ -2542,14 +2589,18 @@ static void run_kernel_tm (hc_device_param_t *device_param)
 {
   const uint num_elements = 1024; // fixed
 
-  const uint kernel_threads = 32;
+  uint kernel_threads = 32;
 
   cl_kernel kernel = device_param->kernel_tm;
 
+  size_t workgroup_size = 0;
+  hc_clGetKernelWorkGroupInfo (data.ocl, kernel, device_param->device, CL_KERNEL_WORK_GROUP_SIZE, sizeof (size_t), &workgroup_size, NULL);
+  if (kernel_threads > workgroup_size) kernel_threads = workgroup_size;
+
   const size_t global_work_size[3] = { num_elements, 1, 1 };
   const size_t local_work_size[3]  = { kernel_threads,  1, 1 };
 
-  hc_clEnqueueNDRangeKernel (data.ocl, device_param->command_queue, kernel, 1, NULL, global_work_size, local_work_size, 0, NULL, NULL, true);
+  hc_clEnqueueNDRangeKernel (data.ocl, device_param->command_queue, kernel, 1, NULL, global_work_size, local_work_size, 0, NULL, NULL);
 
   hc_clFlush (data.ocl, device_param->command_queue);
 
@@ -2566,7 +2617,7 @@ static void run_kernel_amp (hc_device_param_t *device_param, const uint num)
   // causes problems with special threads like in bcrypt
   // const uint kernel_threads = device_param->kernel_threads;
 
-  const uint kernel_threads = KERNEL_THREADS;
+  uint kernel_threads = KERNEL_THREADS;
 
   while (num_elements % kernel_threads) num_elements++;
 
@@ -2575,10 +2626,14 @@ static void run_kernel_amp (hc_device_param_t *device_param, const uint num)
   hc_clSetKernelArg (data.ocl, kernel, 5, sizeof (cl_uint), device_param->kernel_params_amp[5]);
   hc_clSetKernelArg (data.ocl, kernel, 6, sizeof (cl_uint), device_param->kernel_params_amp[6]);
 
+  size_t workgroup_size = 0;
+  hc_clGetKernelWorkGroupInfo (data.ocl, kernel, device_param->device, CL_KERNEL_WORK_GROUP_SIZE, sizeof (size_t), &workgroup_size, NULL);
+  if (kernel_threads > workgroup_size) kernel_threads = workgroup_size;
+
   const size_t global_work_size[3] = { num_elements, 1, 1 };
   const size_t local_work_size[3]  = { kernel_threads,  1, 1 };
 
-  hc_clEnqueueNDRangeKernel (data.ocl, device_param->command_queue, kernel, 1, NULL, global_work_size, local_work_size, 0, NULL, NULL, true);
+  hc_clEnqueueNDRangeKernel (data.ocl, device_param->command_queue, kernel, 1, NULL, global_work_size, local_work_size, 0, NULL, NULL);
 
   hc_clFlush (data.ocl, device_param->command_queue);
 
@@ -2587,15 +2642,18 @@ static void run_kernel_amp (hc_device_param_t *device_param, const uint num)
 
 static void run_kernel_bzero (hc_device_param_t *device_param, cl_mem buf, const uint size)
 {
-  if (device_param->vendor_id == VENDOR_ID_AMD)
+  int rc = -1;
+
+  if (device_param->opencl_v12 && device_param->vendor_id == VENDOR_ID_AMD)
   {
     // So far tested, amd is the only supporting this OpenCL 1.2 function without segfaulting
 
     const cl_uchar zero = 0;
 
-    hc_clEnqueueFillBuffer (data.ocl, device_param->command_queue, buf, &zero, sizeof (cl_uchar), 0, size, 0, NULL, NULL);
+    rc = hc_clEnqueueFillBuffer (data.ocl, device_param->command_queue, buf, &zero, sizeof (cl_uchar), 0, size, 0, NULL, NULL);
   }
-  else
+
+  if (rc != 0)
   {
     // NOTE: clEnqueueFillBuffer () always fails with -59
     //       IOW, it's not supported by Nvidia ForceWare <= 352.21, also pocl segfaults, also on apple
@@ -2619,6 +2677,83 @@ static void run_kernel_bzero (hc_device_param_t *device_param, cl_mem buf, const
   }
 }
 
+static void choose_kernel (hc_device_param_t *device_param, const uint attack_exec, const uint attack_mode, const uint opts_type, const salt_t *salt_buf, const uint highest_pw_len, const uint pws_cnt)
+{
+  if (attack_exec == ATTACK_EXEC_INSIDE_KERNEL)
+  {
+    if (attack_mode == ATTACK_MODE_BF)
+    {
+      if (opts_type & OPTS_TYPE_PT_BITSLICE)
+      {
+        const uint size_tm = 32 * sizeof (bs_word_t);
+
+        run_kernel_bzero (device_param, device_param->d_tm_c, size_tm);
+
+        run_kernel_tm (device_param);
+
+        hc_clEnqueueCopyBuffer (data.ocl, device_param->command_queue, device_param->d_tm_c, device_param->d_bfs_c, 0, 0, size_tm, 0, NULL, NULL);
+      }
+    }
+
+    if (highest_pw_len < 16)
+    {
+      run_kernel (KERN_RUN_1, device_param, pws_cnt, true);
+    }
+    else if (highest_pw_len < 32)
+    {
+      run_kernel (KERN_RUN_2, device_param, pws_cnt, true);
+    }
+    else
+    {
+      run_kernel (KERN_RUN_3, device_param, pws_cnt, true);
+    }
+  }
+  else
+  {
+    run_kernel_amp (device_param, pws_cnt);
+
+    run_kernel (KERN_RUN_1, device_param, pws_cnt, false);
+
+    if (opts_type & OPTS_TYPE_HOOK12)
+    {
+      run_kernel (KERN_RUN_12, device_param, pws_cnt, false);
+    }
+
+    uint iter = salt_buf->salt_iter;
+
+    uint loop_step = device_param->kernel_loops;
+
+    for (uint loop_pos = 0; loop_pos < iter; loop_pos += loop_step)
+    {
+      uint loop_left = iter - loop_pos;
+
+      loop_left = MIN (loop_left, loop_step);
+
+      device_param->kernel_params_buf32[25] = loop_pos;
+      device_param->kernel_params_buf32[26] = loop_left;
+
+      run_kernel (KERN_RUN_2, device_param, pws_cnt, true);
+
+      if (data.devices_status == STATUS_CRACKED) break;
+      if (data.devices_status == STATUS_ABORTED) break;
+      if (data.devices_status == STATUS_QUIT)    break;
+    }
+
+    if (opts_type & OPTS_TYPE_HOOK23)
+    {
+      run_kernel (KERN_RUN_23, device_param, pws_cnt, false);
+
+      hc_clEnqueueReadBuffer (data.ocl, device_param->command_queue, device_param->d_hooks, CL_TRUE, 0, device_param->size_hooks, device_param->hooks_buf, 0, NULL, NULL);
+
+      // do something with data
+
+      hc_clEnqueueWriteBuffer (data.ocl, device_param->command_queue, device_param->d_hooks, CL_TRUE, 0, device_param->size_hooks, device_param->hooks_buf, 0, NULL, NULL);
+    }
+
+    run_kernel (KERN_RUN_3, device_param, pws_cnt, false);
+  }
+}
+
 static int run_rule_engine (const int rule_len, const char *rule_buf)
 {
   if (rule_len == 0)
@@ -2653,106 +2788,369 @@ static void run_copy (hc_device_param_t *device_param, const uint pws_cnt)
   }
 }
 
-static void run_cracker (hc_device_param_t *device_param, const uint pw_cnt, const uint pws_cnt)
+static double try_run (hc_device_param_t *device_param, const u32 kernel_accel, const u32 kernel_loops, const int repeat)
 {
-  const uint kernel_loops = data.kernel_loops;
+  const u32 kernel_power = device_param->device_processors * device_param->kernel_threads * kernel_accel;
 
-  // init speed timer
+  device_param->kernel_params_buf32[26] = kernel_loops;
+  device_param->kernel_params_buf32[27] = kernel_loops;
 
-  uint speed_pos = device_param->speed_pos;
+  // init some fake words
 
-  #ifdef _POSIX
-  if (device_param->timer_speed.tv_sec == 0)
+  if (data.attack_kern == ATTACK_KERN_BF)
   {
-    hc_timer_set (&device_param->timer_speed);
+    run_kernel_mp (KERN_RUN_MP_L, device_param, kernel_power);
+    run_kernel_mp (KERN_RUN_MP_R, device_param, kernel_loops);
   }
-  #endif
-
-  #ifdef _WIN
-  if (device_param->timer_speed.QuadPart == 0)
+  else
   {
-    hc_timer_set (&device_param->timer_speed);
-  }
-  #endif
-
-  // find higest password length, this is for optimization stuff
+    for (u32 i = 0; i < kernel_power; i++)
+    {
+      device_param->pws_buf[i].pw_len = i & 7;
+    }
 
-  uint highest_pw_len = 0;
+    hc_clEnqueueWriteBuffer (data.ocl, device_param->command_queue, device_param->d_pws_buf, CL_TRUE, 0, kernel_power * sizeof (pw_t), device_param->pws_buf, 0, NULL, NULL);
 
-  if (data.attack_kern == ATTACK_KERN_STRAIGHT)
-  {
+    if (data.attack_exec == ATTACK_EXEC_OUTSIDE_KERNEL)
+    {
+      run_kernel_amp (device_param, kernel_power);
+    }
   }
-  else if (data.attack_kern == ATTACK_KERN_COMBI)
+
+  // caching run
+
+  if (data.attack_exec == ATTACK_EXEC_INSIDE_KERNEL)
   {
+    run_kernel (KERN_RUN_1, device_param, kernel_power, false);
   }
-  else if (data.attack_kern == ATTACK_KERN_BF)
+  else
   {
-    highest_pw_len = device_param->kernel_params_mp_l_buf32[4]
-                   + device_param->kernel_params_mp_l_buf32[5];
+    run_kernel (KERN_RUN_2, device_param, kernel_power, false);
   }
 
-  // bitslice optimization stuff
+  // now user repeats
 
-  if (data.attack_mode == ATTACK_MODE_BF)
+  for (int i = 0; i < repeat; i++)
   {
-    if (data.opts_type & OPTS_TYPE_PT_BITSLICE)
+    if (data.attack_exec == ATTACK_EXEC_INSIDE_KERNEL)
     {
-      run_kernel_tb (device_param, pws_cnt);
+      run_kernel (KERN_RUN_1, device_param, kernel_power, true);
+    }
+    else
+    {
+      run_kernel (KERN_RUN_2, device_param, kernel_power, true);
     }
   }
 
-  // iteration type
+  const double exec_ms_prev = get_avg_exec_time (device_param, repeat);
 
-  uint innerloop_step = 0;
-  uint innerloop_cnt  = 0;
+  // reset fake words
 
-  if      (data.attack_exec == ATTACK_EXEC_INSIDE_KERNEL)   innerloop_step = kernel_loops;
-  else                                                      innerloop_step = 1;
+  memset (device_param->pws_buf, 0, kernel_power * sizeof (pw_t));
 
-  if      (data.attack_kern == ATTACK_KERN_STRAIGHT) innerloop_cnt  = data.kernel_rules_cnt;
-  else if (data.attack_kern == ATTACK_KERN_COMBI)    innerloop_cnt  = data.combs_cnt;
-  else if (data.attack_kern == ATTACK_KERN_BF)       innerloop_cnt  = data.bfs_cnt;
+  hc_clEnqueueWriteBuffer (data.ocl, device_param->command_queue, device_param->d_pws_buf,     CL_TRUE, 0, kernel_power * sizeof (pw_t), device_param->pws_buf, 0, NULL, NULL);
+  hc_clEnqueueWriteBuffer (data.ocl, device_param->command_queue, device_param->d_pws_amp_buf, CL_TRUE, 0, kernel_power * sizeof (pw_t), device_param->pws_buf, 0, NULL, NULL);
 
-  // loop start: most outer loop = salt iteration, then innerloops (if multi)
+  return exec_ms_prev;
+}
 
-  for (uint salt_pos = 0; salt_pos < data.salts_cnt; salt_pos++)
-  {
-    while (data.devices_status == STATUS_PAUSED) hc_sleep (1);
+static void autotune (hc_device_param_t *device_param)
+{
+  const double target_ms = TARGET_MS_PROFILE[data.workload_profile - 1];
 
-    if (data.devices_status == STATUS_STOP_AT_CHECKPOINT) check_checkpoint ();
+  const u32 kernel_accel_min = device_param->kernel_accel_min;
+  const u32 kernel_accel_max = device_param->kernel_accel_max;
 
-    if (data.devices_status == STATUS_CRACKED) break;
-    if (data.devices_status == STATUS_ABORTED) break;
-    if (data.devices_status == STATUS_QUIT)    break;
-    if (data.devices_status == STATUS_BYPASS)  break;
+  const u32 kernel_loops_min = device_param->kernel_loops_min;
+  const u32 kernel_loops_max = device_param->kernel_loops_max;
 
-    if (data.salts_shown[salt_pos] == 1) continue;
+  u32 kernel_accel = kernel_accel_min;
+  u32 kernel_loops = kernel_loops_min;
 
-    salt_t *salt_buf = &data.salts_buf[salt_pos];
+  // steps
 
-    device_param->kernel_params_buf32[24] = salt_pos;
-    device_param->kernel_params_buf32[28] = salt_buf->digests_cnt;
-    device_param->kernel_params_buf32[29] = salt_buf->digests_offset;
+  #define STEPS_CNT 10
 
-    FILE *combs_fp = device_param->combs_fp;
+  #define STEPS_ACCEL_CNT (STEPS_CNT + 2)
+  #define STEPS_LOOPS_CNT (STEPS_CNT + 2)
 
-    if (data.attack_mode == ATTACK_MODE_COMBI)
-    {
-      rewind (combs_fp);
-    }
+  u32 steps_accel[STEPS_ACCEL_CNT];
+  u32 steps_loops[STEPS_LOOPS_CNT];
 
-    // innerloops
+  for (int i = 0; i < STEPS_ACCEL_CNT; i++)
+  {
+    steps_accel[i] = 1 << i;
+  }
 
-    for (uint innerloop_pos = 0; innerloop_pos < innerloop_cnt; innerloop_pos += innerloop_step)
-    {
-      while (data.devices_status == STATUS_PAUSED) hc_sleep (1);
+  for (int i = 0; i < STEPS_LOOPS_CNT; i++)
+  {
+    steps_loops[i] = 1 << i;
+  }
 
-      if (data.devices_status == STATUS_STOP_AT_CHECKPOINT) check_checkpoint ();
+  steps_accel[STEPS_CNT + 0] = kernel_accel_min;
+  steps_accel[STEPS_CNT + 1] = kernel_accel_max;
 
-      if (data.devices_status == STATUS_CRACKED) break;
-      if (data.devices_status == STATUS_ABORTED) break;
-      if (data.devices_status == STATUS_QUIT)    break;
-      if (data.devices_status == STATUS_BYPASS)  break;
+  steps_loops[STEPS_CNT + 0] = kernel_loops_min;
+  steps_loops[STEPS_CNT + 1] = kernel_loops_max;
+
+  qsort (steps_accel, STEPS_ACCEL_CNT, sizeof (u32), sort_by_u32);
+  qsort (steps_loops, STEPS_LOOPS_CNT, sizeof (u32), sort_by_u32);
+
+  // find out highest kernel-loops that stays below target_ms, we can use it later for multiplication as this is a linear function
+
+  u32 kernel_loops_tmp;
+
+  for (kernel_loops_tmp = kernel_loops_max; kernel_loops_tmp > kernel_loops_min; kernel_loops_tmp >>= 1)
+  {
+    const double exec_ms = try_run (device_param, kernel_accel_min, kernel_loops_tmp, 1);
+
+    if (exec_ms < target_ms) break;
+  }
+
+  // kernel-accel
+
+  if (kernel_accel_min < kernel_accel_max)
+  {
+    double e_best = 0;
+
+    for (int i = 0; i < STEPS_ACCEL_CNT; i++)
+    {
+      const u32 kernel_accel_try = steps_accel[i];
+
+      if (kernel_accel_try < kernel_accel_min) continue;
+      if (kernel_accel_try > kernel_accel_max) break;
+
+      const double exec_ms = try_run (device_param, kernel_accel_try, kernel_loops_tmp, 1);
+
+      if (exec_ms > target_ms) break;
+
+      const double e = kernel_accel_try / exec_ms;
+
+      if (e > e_best)
+      {
+        kernel_accel = kernel_accel_try;
+
+        e_best = e;
+      }
+    }
+  }
+
+  // kernel-loops final
+
+  if (kernel_loops_min < kernel_loops_max)
+  {
+    double e_best = 0;
+
+    for (int i = 0; i < STEPS_LOOPS_CNT; i++)
+    {
+      const u32 kernel_loops_try = steps_loops[i];
+
+      if (kernel_loops_try < kernel_loops_min) continue;
+      if (kernel_loops_try > kernel_loops_max) break;
+
+      const double exec_ms = try_run (device_param, kernel_accel, kernel_loops_try, 1);
+
+      if (exec_ms > target_ms) break;
+
+      const double e = kernel_loops_try / exec_ms;
+
+      if (e > e_best)
+      {
+        kernel_loops = kernel_loops_try;
+
+        e_best = e;
+      }
+    }
+  }
+
+  // final balance
+
+  const double exec_ms = try_run (device_param, kernel_accel, kernel_loops, 1);
+
+  u32 kernel_accel_best = kernel_accel;
+  u32 kernel_loops_best = kernel_loops;
+
+  u32 exec_best = exec_ms;
+
+  // reset
+
+  if (kernel_accel_min < kernel_accel_max)
+  {
+    u32 kernel_accel_try = kernel_accel;
+    u32 kernel_loops_try = kernel_loops;
+
+    for (int i = 0; i < 2; i++)
+    {
+      kernel_accel_try >>= 1;
+      kernel_loops_try <<= 1;
+
+      if (kernel_accel_try < kernel_accel_min) break;
+      if (kernel_loops_try > kernel_loops_max) break;
+
+      const double exec_ms = try_run (device_param, kernel_accel_try, kernel_loops_try, 1);
+
+      if (exec_ms < exec_best)
+      {
+        kernel_accel_best = kernel_accel_try;
+        kernel_loops_best = kernel_loops_try;
+
+        exec_best = exec_ms;
+      }
+    }
+
+    kernel_accel = kernel_accel_best;
+    kernel_loops = kernel_loops_best;
+  }
+
+  // reset
+
+
+  if (kernel_loops_min < kernel_loops_max)
+  {
+    u32 kernel_accel_try = kernel_accel;
+    u32 kernel_loops_try = kernel_loops;
+
+    for (int i = 0; i < 2; i++)
+    {
+      kernel_accel_try <<= 1;
+      kernel_loops_try >>= 1;
+
+      if (kernel_accel_try > kernel_accel_max) break;
+      if (kernel_loops_try < kernel_loops_min) break;
+
+      const double exec_ms = try_run (device_param, kernel_accel_try, kernel_loops_try, 1);
+
+      if (exec_ms < exec_best)
+      {
+        kernel_accel_best = kernel_accel_try;
+        kernel_loops_best = kernel_loops_try;
+
+        exec_best = exec_ms;
+      }
+    }
+
+    kernel_accel = kernel_accel_best;
+    kernel_loops = kernel_loops_best;
+  }
+
+  // reset timer
+
+  device_param->exec_pos = 0;
+
+  memset (device_param->exec_ms, 0, EXEC_CACHE * sizeof (double));
+
+  // store
+
+  device_param->kernel_loops = kernel_loops;
+  device_param->kernel_accel = kernel_accel;
+
+  const u32 kernel_power = device_param->device_processors * device_param->kernel_threads * device_param->kernel_accel;
+
+  device_param->kernel_power = kernel_power;
+
+  log_info ("Device #%u: autotuned kernel-accel to %u", device_param->device_id + 1, kernel_accel);
+  log_info ("Device #%u: autotuned kernel-loops to %u", device_param->device_id + 1, kernel_loops);
+  log_info ("");
+}
+
+static void run_cracker (hc_device_param_t *device_param, const uint pw_cnt, const uint pws_cnt)
+{
+  // init speed timer
+
+  uint speed_pos = device_param->speed_pos;
+
+  #ifdef _POSIX
+  if (device_param->timer_speed.tv_sec == 0)
+  {
+    hc_timer_set (&device_param->timer_speed);
+  }
+  #endif
+
+  #ifdef _WIN
+  if (device_param->timer_speed.QuadPart == 0)
+  {
+    hc_timer_set (&device_param->timer_speed);
+  }
+  #endif
+
+  // find higest password length, this is for optimization stuff
+
+  uint highest_pw_len = 0;
+
+  if (data.attack_kern == ATTACK_KERN_STRAIGHT)
+  {
+  }
+  else if (data.attack_kern == ATTACK_KERN_COMBI)
+  {
+  }
+  else if (data.attack_kern == ATTACK_KERN_BF)
+  {
+    highest_pw_len = device_param->kernel_params_mp_l_buf32[4]
+                   + device_param->kernel_params_mp_l_buf32[5];
+  }
+
+  // bitslice optimization stuff
+
+  if (data.attack_mode == ATTACK_MODE_BF)
+  {
+    if (data.opts_type & OPTS_TYPE_PT_BITSLICE)
+    {
+      run_kernel_tb (device_param, pws_cnt);
+    }
+  }
+
+  // iteration type
+
+  uint innerloop_step = 0;
+  uint innerloop_cnt  = 0;
+
+  if      (data.attack_exec == ATTACK_EXEC_INSIDE_KERNEL)   innerloop_step = device_param->kernel_loops;
+  else                                                      innerloop_step = 1;
+
+  if      (data.attack_kern == ATTACK_KERN_STRAIGHT) innerloop_cnt  = data.kernel_rules_cnt;
+  else if (data.attack_kern == ATTACK_KERN_COMBI)    innerloop_cnt  = data.combs_cnt;
+  else if (data.attack_kern == ATTACK_KERN_BF)       innerloop_cnt  = data.bfs_cnt;
+
+  // loop start: most outer loop = salt iteration, then innerloops (if multi)
+
+  for (uint salt_pos = 0; salt_pos < data.salts_cnt; salt_pos++)
+  {
+    while (data.devices_status == STATUS_PAUSED) hc_sleep (1);
+
+    if (data.devices_status == STATUS_STOP_AT_CHECKPOINT) check_checkpoint ();
+
+    if (data.devices_status == STATUS_CRACKED) break;
+    if (data.devices_status == STATUS_ABORTED) break;
+    if (data.devices_status == STATUS_QUIT)    break;
+    if (data.devices_status == STATUS_BYPASS)  break;
+
+    if (data.salts_shown[salt_pos] == 1) continue;
+
+    salt_t *salt_buf = &data.salts_buf[salt_pos];
+
+    device_param->kernel_params_buf32[24] = salt_pos;
+    device_param->kernel_params_buf32[28] = salt_buf->digests_cnt;
+    device_param->kernel_params_buf32[29] = salt_buf->digests_offset;
+
+    FILE *combs_fp = device_param->combs_fp;
+
+    if (data.attack_mode == ATTACK_MODE_COMBI)
+    {
+      rewind (combs_fp);
+    }
+
+    // innerloops
+
+    for (uint innerloop_pos = 0; innerloop_pos < innerloop_cnt; innerloop_pos += innerloop_step)
+    {
+      while (data.devices_status == STATUS_PAUSED) hc_sleep (1);
+
+      if (data.devices_status == STATUS_STOP_AT_CHECKPOINT) check_checkpoint ();
+
+      if (data.devices_status == STATUS_CRACKED) break;
+      if (data.devices_status == STATUS_ABORTED) break;
+      if (data.devices_status == STATUS_QUIT)    break;
+      if (data.devices_status == STATUS_BYPASS)  break;
 
       uint innerloop_left = innerloop_cnt - innerloop_pos;
 
@@ -2763,7 +3161,13 @@ static void run_cracker (hc_device_param_t *device_param, const uint pw_cnt, con
 
       device_param->kernel_params_buf32[27] = innerloop_left;
 
-      if (innerloop_left == 0) continue;
+      // i think we can get rid of this
+      if (innerloop_left == 0)
+      {
+        puts ("bug, how should this happen????\n");
+
+        continue;
+      }
 
       // initialize amplifiers
 
@@ -2898,78 +3302,14 @@ static void run_cracker (hc_device_param_t *device_param, const uint pw_cnt, con
         hc_clEnqueueCopyBuffer (data.ocl, device_param->command_queue, device_param->d_combs, device_param->d_combs_c, 0, 0, innerloop_left * sizeof (comb_t), 0, NULL, NULL);
       }
 
-      if (data.attack_exec == ATTACK_EXEC_INSIDE_KERNEL)
-      {
-        if (data.attack_mode == ATTACK_MODE_BF)
-        {
-          if (data.opts_type & OPTS_TYPE_PT_BITSLICE)
-          {
-            const uint size_tm = 32 * sizeof (bs_word_t);
-
-            run_kernel_bzero (device_param, device_param->d_tm_c, size_tm);
-
-            run_kernel_tm (device_param);
-
-            hc_clEnqueueCopyBuffer (data.ocl, device_param->command_queue, device_param->d_tm_c, device_param->d_bfs_c, 0, 0, size_tm, 0, NULL, NULL);
-          }
-        }
+      choose_kernel (device_param, data.attack_exec, data.attack_mode, data.opts_type, salt_buf, highest_pw_len, pws_cnt);
 
-        if (highest_pw_len < 16)
-        {
-          run_kernel (KERN_RUN_1, device_param, pws_cnt);
-        }
-        else if (highest_pw_len < 32)
-        {
-          run_kernel (KERN_RUN_2, device_param, pws_cnt);
-        }
-        else
-        {
-          run_kernel (KERN_RUN_3, device_param, pws_cnt);
-        }
-      }
-      else
+      if (data.benchmark == 1)
       {
-        run_kernel_amp (device_param, pws_cnt);
-
-        run_kernel (KERN_RUN_1, device_param, pws_cnt);
-
-        if (data.opts_type & OPTS_TYPE_HOOK12)
-        {
-          run_kernel (KERN_RUN_12, device_param, pws_cnt);
-        }
-
-        uint iter = salt_buf->salt_iter;
-
-        for (uint loop_pos = 0; loop_pos < iter; loop_pos += kernel_loops)
-        {
-          uint loop_left = iter - loop_pos;
-
-          loop_left = MIN (loop_left, kernel_loops);
-
-          device_param->kernel_params_buf32[25] = loop_pos;
-          device_param->kernel_params_buf32[26] = loop_left;
-
-          run_kernel (KERN_RUN_2, device_param, pws_cnt);
-
-          if (data.devices_status == STATUS_STOP_AT_CHECKPOINT) check_checkpoint ();
-
-          if (data.devices_status == STATUS_CRACKED) break;
-          if (data.devices_status == STATUS_ABORTED) break;
-          if (data.devices_status == STATUS_QUIT)    break;
-        }
-
-        if (data.opts_type & OPTS_TYPE_HOOK23)
+        for (u32 i = 0; i < data.benchmark_repeats; i++)
         {
-          run_kernel (KERN_RUN_23, device_param, pws_cnt);
-
-          hc_clEnqueueReadBuffer (data.ocl, device_param->command_queue, device_param->d_hooks, CL_TRUE, 0, device_param->size_hooks, device_param->hooks_buf, 0, NULL, NULL);
-
-          // do something with data
-
-          hc_clEnqueueWriteBuffer (data.ocl, device_param->command_queue, device_param->d_hooks, CL_TRUE, 0, device_param->size_hooks, device_param->hooks_buf, 0, NULL, NULL);
+          choose_kernel (device_param, data.attack_exec, data.attack_mode, data.opts_type, salt_buf, highest_pw_len, pws_cnt);
         }
-
-        run_kernel (KERN_RUN_3, device_param, pws_cnt);
       }
 
       if (data.devices_status == STATUS_STOP_AT_CHECKPOINT) check_checkpoint ();
@@ -2994,6 +3334,11 @@ static void run_cracker (hc_device_param_t *device_param, const uint pw_cnt, con
 
       u64 perf_sum_all = (u64) pw_cnt * (u64) innerloop_left;
 
+      if (data.benchmark == 1)
+      {
+        perf_sum_all = (perf_sum_all * data.benchmark_repeats) + perf_sum_all;
+      }
+
       hc_thread_mutex_lock (mux_counter);
 
       data.words_progress_done[salt_pos] += perf_sum_all;
@@ -3026,6 +3371,12 @@ static void run_cracker (hc_device_param_t *device_param, const uint pw_cnt, con
       {
         speed_pos = 0;
       }
+
+      /**
+       * benchmark
+       */
+
+      if (data.benchmark == 1) break;
     }
   }
 
@@ -3976,32 +4327,30 @@ static uint get_work (hc_device_param_t *device_param, const u64 max)
 
   const u64 words_left = words_base - words_cur;
 
-  if (data.kernel_blocks_all > words_left)
+  if (data.kernel_power_all > words_left)
   {
-    if (data.kernel_blocks_div == 0)
+    if (data.kernel_power_div == 0)
     {
-      data.kernel_blocks_div = find_kernel_blocks_div (words_left, data.kernel_blocks_all);
+      data.kernel_power_div = find_kernel_power_div (words_left, data.kernel_power_all);
     }
   }
 
-  if (data.kernel_blocks_div)
+  if (data.kernel_power_div)
   {
-    if (device_param->kernel_blocks == device_param->kernel_blocks_user)
+    if (device_param->kernel_power == device_param->kernel_power_user)
     {
-      const u32 kernel_blocks_new = (float) device_param->kernel_blocks * data.kernel_blocks_div;
-      const u32 kernel_power_new  = kernel_blocks_new;
+      const u32 kernel_power_new = (float) device_param->kernel_power * data.kernel_power_div;
 
-      if (kernel_blocks_new < device_param->kernel_blocks)
+      if (kernel_power_new < device_param->kernel_power)
       {
-        device_param->kernel_blocks  = kernel_blocks_new;
-        device_param->kernel_power   = kernel_power_new;
+        device_param->kernel_power = kernel_power_new;
       }
     }
   }
 
-  const uint kernel_blocks = device_param->kernel_blocks;
+  const uint kernel_power = device_param->kernel_power;
 
-  uint work = MIN (words_left, kernel_blocks);
+  uint work = MIN (words_left, kernel_power);
 
   work = MIN (work, max);
 
@@ -4018,9 +4367,11 @@ static void *thread_calc_stdin (void *p)
 
   if (device_param->skipped) return NULL;
 
+  autotune (device_param);
+
   const uint attack_kern = data.attack_kern;
 
-  const uint kernel_blocks = device_param->kernel_blocks;
+  const uint kernel_power = device_param->kernel_power;
 
   while ((data.devices_status != STATUS_EXHAUSTED) && (data.devices_status != STATUS_CRACKED) && (data.devices_status != STATUS_ABORTED) && (data.devices_status != STATUS_QUIT))
   {
@@ -4035,7 +4386,7 @@ static void *thread_calc_stdin (void *p)
 
     uint words_cur = 0;
 
-    while (words_cur < kernel_blocks)
+    while (words_cur < kernel_power)
     {
       char buf[BUFSIZ] = { 0 };
 
@@ -4223,6 +4574,9 @@ static void *thread_calc_stdin (void *p)
     }
   }
 
+  device_param->kernel_accel = 0;
+  device_param->kernel_loops = 0;
+
   return NULL;
 }
 
@@ -4232,6 +4586,8 @@ static void *thread_calc (void *p)
 
   if (device_param->skipped) return NULL;
 
+  autotune (device_param);
+
   const uint attack_mode = data.attack_mode;
   const uint attack_kern = data.attack_kern;
 
@@ -4269,6 +4625,8 @@ static void *thread_calc (void *p)
       if (data.devices_status == STATUS_QUIT)    break;
       if (data.devices_status == STATUS_BYPASS)  break;
 
+      if (data.benchmark == 1) break;
+
       device_param->words_done = words_fin;
     }
   }
@@ -4590,11 +4948,21 @@ static void *thread_calc (void *p)
     fclose (fd);
   }
 
+  device_param->kernel_accel = 0;
+  device_param->kernel_loops = 0;
+
   return NULL;
 }
 
-static void weak_hash_check (hc_device_param_t *device_param, const uint salt_pos, const uint kernel_loops)
+static void weak_hash_check (hc_device_param_t *device_param, const uint salt_pos)
 {
+  if (!device_param)
+  {
+    log_error ("ERROR: %s : Invalid argument", __func__);
+
+    exit (-1);
+  }
+
   salt_t *salt_buf = &data.salts_buf[salt_pos];
 
   device_param->kernel_params_buf32[24] = salt_pos;
@@ -4620,27 +4988,29 @@ static void weak_hash_check (hc_device_param_t *device_param, const uint salt_po
 
   if (data.attack_exec == ATTACK_EXEC_INSIDE_KERNEL)
   {
-    run_kernel (KERN_RUN_1, device_param, 1);
+    run_kernel (KERN_RUN_1, device_param, 1, false);
   }
   else
   {
-    run_kernel (KERN_RUN_1, device_param, 1);
+    run_kernel (KERN_RUN_1, device_param, 1, false);
+
+    uint loop_step = 16;
 
     const uint iter = salt_buf->salt_iter;
 
-    for (uint loop_pos = 0; loop_pos < iter; loop_pos += kernel_loops)
+    for (uint loop_pos = 0; loop_pos < iter; loop_pos += loop_step)
     {
       uint loop_left = iter - loop_pos;
 
-      loop_left = MIN (loop_left, kernel_loops);
+      loop_left = MIN (loop_left, loop_step);
 
       device_param->kernel_params_buf32[25] = loop_pos;
       device_param->kernel_params_buf32[26] = loop_left;
 
-      run_kernel (KERN_RUN_2, device_param, 1);
+      run_kernel (KERN_RUN_2, device_param, 1, false);
     }
 
-    run_kernel (KERN_RUN_3, device_param, 1);
+    run_kernel (KERN_RUN_3, device_param, 1, false);
   }
 
   /**
@@ -5069,7 +5439,7 @@ int main (int argc, char **argv)
 
   if (compute)
   {
-    char display[100] = { 0 };
+    static char display[100];
 
     snprintf (display, sizeof (display) - 1, "DISPLAY=%s", compute);
 
@@ -5118,7 +5488,7 @@ int main (int argc, char **argv)
   uint  version           = VERSION;
   uint  quiet             = QUIET;
   uint  benchmark         = BENCHMARK;
-  uint  benchmark_mode    = BENCHMARK_MODE;
+  uint  benchmark_repeats = BENCHMARK_REPEATS;
   uint  show              = SHOW;
   uint  left              = LEFT;
   uint  username          = USERNAME;
@@ -5165,9 +5535,7 @@ int main (int argc, char **argv)
   uint  increment         = INCREMENT;
   uint  increment_min     = INCREMENT_MIN;
   uint  increment_max     = INCREMENT_MAX;
-  #ifndef OSX
   char *cpu_affinity      = NULL;
-  #endif
   OCL_PTR *ocl            = NULL;
   char *opencl_devices    = NULL;
   char *opencl_platforms  = NULL;
@@ -5216,7 +5584,7 @@ int main (int argc, char **argv)
   #define IDX_FORCE             0xff08
   #define IDX_RUNTIME           0xff09
   #define IDX_BENCHMARK         'b'
-  #define IDX_BENCHMARK_MODE    0xff32
+  #define IDX_BENCHMARK_REPEATS 0xff78
   #define IDX_HASH_MODE         'm'
   #define IDX_ATTACK_MODE       'a'
   #define IDX_RP_FILE           'r'
@@ -5294,7 +5662,7 @@ int main (int argc, char **argv)
     {"outfile-check-dir", required_argument, 0, IDX_OUTFILE_CHECK_DIR},
     {"force",             no_argument,       0, IDX_FORCE},
     {"benchmark",         no_argument,       0, IDX_BENCHMARK},
-    {"benchmark-mode",    required_argument, 0, IDX_BENCHMARK_MODE},
+    {"benchmark-repeats", required_argument, 0, IDX_BENCHMARK_REPEATS},
     {"restore",           no_argument,       0, IDX_RESTORE},
     {"restore-disable",   no_argument,       0, IDX_RESTORE_DISABLE},
     {"status",            no_argument,       0, IDX_STATUS},
@@ -5330,9 +5698,7 @@ int main (int argc, char **argv)
     {"markov-classic",    no_argument,       0, IDX_MARKOV_CLASSIC},
     {"markov-threshold",  required_argument, 0, IDX_MARKOV_THRESHOLD},
     {"markov-hcstat",     required_argument, 0, IDX_MARKOV_HCSTAT},
-    #ifndef OSX
     {"cpu-affinity",      required_argument, 0, IDX_CPU_AFFINITY},
-    #endif
     {"opencl-devices",    required_argument, 0, IDX_OPENCL_DEVICES},
     {"opencl-platforms",  required_argument, 0, IDX_OPENCL_PLATFORMS},
     {"opencl-device-types", required_argument, 0, IDX_OPENCL_DEVICE_TYPES},
@@ -5557,19 +5923,22 @@ int main (int argc, char **argv)
     #endif
   }
 
-  uint hash_mode_chgd       = 0;
-  uint runtime_chgd         = 0;
-  uint kernel_loops_chgd    = 0;
-  uint kernel_accel_chgd    = 0;
-  uint attack_mode_chgd     = 0;
-  uint outfile_format_chgd  = 0;
-  uint rp_gen_seed_chgd     = 0;
-  uint remove_timer_chgd    = 0;
-  uint increment_min_chgd   = 0;
-  uint increment_max_chgd   = 0;
+  uint hash_mode_chgd           = 0;
+  uint runtime_chgd             = 0;
+  uint kernel_loops_chgd        = 0;
+  uint kernel_accel_chgd        = 0;
+  uint attack_mode_chgd         = 0;
+  uint outfile_format_chgd      = 0;
+  uint rp_gen_seed_chgd         = 0;
+  uint remove_timer_chgd        = 0;
+  uint increment_min_chgd       = 0;
+  uint increment_max_chgd       = 0;
+  uint workload_profile_chgd    = 0;
+  uint opencl_vector_width_chgd = 0;
+
   #if defined(HAVE_HWMON) && defined(HAVE_ADL)
-  uint gpu_temp_retain_chgd = 0;
-  uint gpu_temp_abort_chgd  = 0;
+  uint gpu_temp_retain_chgd   = 0;
+  uint gpu_temp_abort_chgd    = 0;
   #endif
 
   optind = 1;
@@ -5602,7 +5971,7 @@ int main (int argc, char **argv)
       case IDX_LIMIT:             limit             = atoll (optarg);  break;
       case IDX_KEYSPACE:          keyspace          = 1;               break;
       case IDX_BENCHMARK:         benchmark         = 1;               break;
-      case IDX_BENCHMARK_MODE:    benchmark_mode    = atoi (optarg);   break;
+      case IDX_BENCHMARK_REPEATS: benchmark_repeats = atoi (optarg);   break;
       case IDX_RESTORE:                                                break;
       case IDX_RESTORE_DISABLE:   restore_disable   = 1;               break;
       case IDX_STATUS:            status            = 1;               break;
@@ -5641,20 +6010,20 @@ int main (int argc, char **argv)
       case IDX_HEX_CHARSET:       hex_charset       = 1;               break;
       case IDX_HEX_SALT:          hex_salt          = 1;               break;
       case IDX_HEX_WORDLIST:      hex_wordlist      = 1;               break;
-      #ifndef OSX
       case IDX_CPU_AFFINITY:      cpu_affinity      = optarg;          break;
-      #endif
       case IDX_OPENCL_DEVICES:    opencl_devices    = optarg;          break;
       case IDX_OPENCL_PLATFORMS:  opencl_platforms  = optarg;          break;
       case IDX_OPENCL_DEVICE_TYPES:
                                   opencl_device_types = optarg;        break;
       case IDX_OPENCL_VECTOR_WIDTH:
-                                  opencl_vector_width = atoi (optarg); break;
-      case IDX_WORKLOAD_PROFILE:  workload_profile  = atoi (optarg);   break;
-      case IDX_KERNEL_ACCEL:      kernel_accel      = atoi (optarg);
-                                  kernel_accel_chgd = 1;               break;
-      case IDX_KERNEL_LOOPS:      kernel_loops      = atoi (optarg);
-                                  kernel_loops_chgd = 1;               break;
+                                  opencl_vector_width      = atoi (optarg);
+                                  opencl_vector_width_chgd = 1;        break;
+      case IDX_WORKLOAD_PROFILE:  workload_profile         = atoi (optarg);
+                                  workload_profile_chgd    = 1;        break;
+      case IDX_KERNEL_ACCEL:      kernel_accel             = atoi (optarg);
+                                  kernel_accel_chgd        = 1;        break;
+      case IDX_KERNEL_LOOPS:      kernel_loops             = atoi (optarg);
+                                  kernel_loops_chgd        = 1;        break;
       case IDX_GPU_TEMP_DISABLE:  gpu_temp_disable  = 1;               break;
       #ifdef HAVE_HWMON
       case IDX_GPU_TEMP_ABORT:    gpu_temp_abort    = atoi (optarg);
@@ -5747,7 +6116,7 @@ int main (int argc, char **argv)
     return (-1);
   }
 
-  if (hash_mode_chgd && hash_mode > 13000) // just added to remove compiler warnings for hash_mode_chgd
+  if (hash_mode_chgd && hash_mode > 13100) // just added to remove compiler warnings for hash_mode_chgd
   {
     log_error ("ERROR: Invalid hash-type specified");
 
@@ -5889,13 +6258,6 @@ int main (int argc, char **argv)
 
   if (kernel_accel_chgd == 1)
   {
-    if (workload_profile != WORKLOAD_PROFILE)
-    {
-      log_error ("ERROR: kernel-accel parameter can only be set when workload-profile %i is used", WORKLOAD_PROFILE);
-
-      return (-1);
-    }
-
     if (kernel_accel < 1)
     {
       log_error ("ERROR: Invalid kernel-accel specified");
@@ -5903,7 +6265,7 @@ int main (int argc, char **argv)
       return (-1);
     }
 
-    if (kernel_accel > 800)
+    if (kernel_accel > 1024)
     {
       log_error ("ERROR: Invalid kernel-accel specified");
 
@@ -5913,13 +6275,6 @@ int main (int argc, char **argv)
 
   if (kernel_loops_chgd == 1)
   {
-    if (workload_profile != WORKLOAD_PROFILE)
-    {
-      log_error ("ERROR: kernel-loops parameter can only be set when workload-profile %i is used", WORKLOAD_PROFILE);
-
-      return (-1);
-    }
-
     if (kernel_loops < 1)
     {
       log_error ("ERROR: Invalid kernel-loops specified");
@@ -5935,24 +6290,14 @@ int main (int argc, char **argv)
     }
   }
 
-  if (benchmark == 1)
-  {
-    if (workload_profile != WORKLOAD_PROFILE)
-    {
-      log_error ("ERROR: Using the workload-profile in benchmark mode is not allowed");
-
-      return (-1);
-    }
-  }
-
-  if ((workload_profile < 1) || (workload_profile > 3))
+  if ((workload_profile < 1) || (workload_profile > 3))
   {
     log_error ("ERROR: workload-profile %i not available", workload_profile);
 
     return (-1);
   }
 
-  if ((opencl_vector_width != 0) && (opencl_vector_width != 1) && (opencl_vector_width != 2) && (opencl_vector_width != 4) && (opencl_vector_width != 8))
+  if (opencl_vector_width_chgd && (!is_power_of_2(opencl_vector_width) || opencl_vector_width > 16))
   {
     log_error ("ERROR: opencl-vector-width %i not allowed", opencl_vector_width);
 
@@ -6066,26 +6411,6 @@ int main (int argc, char **argv)
         return (-1);
       }
     }
-
-    if (benchmark_mode == 0)
-    {
-      // nothing to do
-    }
-    else if (benchmark_mode == 1)
-    {
-      if (kernel_accel_chgd == 1 || kernel_loops_chgd == 1)
-      {
-        log_error ("ERROR: Benchmark-mode 1 does not allow kernel-accel or kernel-loops changed");
-
-        return (-1);
-      }
-    }
-    else
-    {
-      log_error ("ERROR: Benchmark-mode must be 0 or 1");
-
-      return (-1);
-    }
   }
 
   if (skip != 0 && limit != 0)
@@ -6280,6 +6605,16 @@ int main (int argc, char **argv)
 
   char *loopback_file = (char *) mymalloc (loopback_size);
 
+  /**
+   * tuning db
+   */
+
+  char tuning_db_file[256] = { 0 };
+
+  snprintf (tuning_db_file, sizeof (tuning_db_file) - 1, "%s/%s", shared_dir, TUNING_DB_FILE);
+
+  tuning_db_t *tuning_db = tuning_db_init (tuning_db_file);
+
   /**
    * outfile-check directory
    */
@@ -6379,6 +6714,7 @@ int main (int argc, char **argv)
   data.rp_gen_seed       = rp_gen_seed;
   data.force             = force;
   data.benchmark         = benchmark;
+  data.benchmark_repeats = benchmark_repeats;
   data.skip              = skip;
   data.limit             = limit;
   #if defined(HAVE_HWMON) && defined(HAVE_ADL)
@@ -6387,17 +6723,16 @@ int main (int argc, char **argv)
   data.logfile_disable   = logfile_disable;
   data.truecrypt_keyfiles = truecrypt_keyfiles;
   data.scrypt_tmto       = scrypt_tmto;
+  data.workload_profile  = workload_profile;
 
   /**
    * cpu affinity
    */
 
-  #ifndef OSX
   if (cpu_affinity)
   {
     set_cpu_affinity (cpu_affinity);
   }
-  #endif
 
   if (rp_gen_seed_chgd == 0)
   {
@@ -6454,7 +6789,7 @@ int main (int argc, char **argv)
   logfile_top_uint   (attack_mode);
   logfile_top_uint   (attack_kern);
   logfile_top_uint   (benchmark);
-  logfile_top_uint   (benchmark_mode);
+  logfile_top_uint   (benchmark_repeats);
   logfile_top_uint   (bitmap_min);
   logfile_top_uint   (bitmap_max);
   logfile_top_uint   (debug_mode);
@@ -6512,9 +6847,7 @@ int main (int argc, char **argv)
   logfile_top_uint64 (limit);
   logfile_top_uint64 (skip);
   logfile_top_char   (separator);
-  #ifndef OSX
   logfile_top_string (cpu_affinity);
-  #endif
   logfile_top_string (custom_charset_1);
   logfile_top_string (custom_charset_2);
   logfile_top_string (custom_charset_3);
@@ -6541,7 +6874,7 @@ int main (int argc, char **argv)
   {
     ocl = (OCL_PTR *) mymalloc (sizeof (OCL_PTR));
 
-    ocl_init(ocl);
+    ocl_init (ocl);
 
     data.ocl = ocl;
   }
@@ -6574,20 +6907,16 @@ int main (int argc, char **argv)
      * disable useless stuff for benchmark
      */
 
-    restore_timer    = 0;
-    status_timer     = 0;
-    restore_disable  = 1;
-    potfile_disable  = 1;
-    weak_hash_threshold = 0;
-
-    data.restore_timer   = restore_timer;
-    data.status_timer    = status_timer;
-    data.restore_disable = restore_disable;
+    status_timer          = 0;
+    restore_timer         = 0;
+    restore_disable       = 1;
+    potfile_disable       = 1;
+    weak_hash_threshold   = 0;
+    gpu_temp_disable      = 1;
 
-    if (benchmark_mode == 1)
-    {
-      markov_disable   = 1;
-    }
+    data.status_timer     = status_timer;
+    data.restore_timer    = restore_timer;
+    data.restore_disable  = restore_disable;
 
     /**
      * force attack mode to be bruteforce
@@ -6596,13 +6925,11 @@ int main (int argc, char **argv)
     attack_mode = ATTACK_MODE_BF;
     attack_kern = ATTACK_KERN_BF;
 
-    if (runtime_chgd == 0)
+    if (workload_profile_chgd == 0)
     {
-      runtime =  8;
+      workload_profile = 3;
 
-      if (benchmark_mode == 1) runtime = 17;
-
-      data.runtime = runtime;
+      data.workload_profile = workload_profile;
     }
   }
 
@@ -10023,6 +10350,22 @@ int main (int argc, char **argv)
                    dgst_pos3   = 3;
                    break;
 
+      case 13100:  hash_type   = HASH_TYPE_KRB5TGS;
+                   salt_type   = SALT_TYPE_EMBEDDED;
+                   attack_exec = ATTACK_EXEC_INSIDE_KERNEL;
+                   opts_type   = OPTS_TYPE_PT_GENERATE_LE;
+                   kern_type   = KERN_TYPE_KRB5TGS;
+                   dgst_size   = DGST_SIZE_4_4;
+                   parse_func  = krb5tgs_parse_hash;
+                   sort_by_digest = sort_by_digest_4_4;
+                   opti_type   = OPTI_TYPE_ZERO_BYTE
+                               | OPTI_TYPE_NOT_ITERATED;
+                   dgst_pos0   = 0;
+                   dgst_pos1   = 1;
+                   dgst_pos2   = 2;
+                   dgst_pos3   = 3;
+                   break;
+
       default:     usage_mini_print (PROGNAME); return (-1);
     }
 
@@ -10081,17 +10424,17 @@ int main (int argc, char **argv)
       case  5400:  esalt_size = sizeof (ikepsk_t);        break;
       case  5500:  esalt_size = sizeof (netntlm_t);       break;
       case  5600:  esalt_size = sizeof (netntlm_t);       break;
-      case  6211:
-      case  6212:
-      case  6213:
-      case  6221:
-      case  6222:
-      case  6223:
-      case  6231:
-      case  6232:
-      case  6233:
-      case  6241:
-      case  6242:
+      case  6211:  esalt_size = sizeof (tc_t);            break;
+      case  6212:  esalt_size = sizeof (tc_t);            break;
+      case  6213:  esalt_size = sizeof (tc_t);            break;
+      case  6221:  esalt_size = sizeof (tc_t);            break;
+      case  6222:  esalt_size = sizeof (tc_t);            break;
+      case  6223:  esalt_size = sizeof (tc_t);            break;
+      case  6231:  esalt_size = sizeof (tc_t);            break;
+      case  6232:  esalt_size = sizeof (tc_t);            break;
+      case  6233:  esalt_size = sizeof (tc_t);            break;
+      case  6241:  esalt_size = sizeof (tc_t);            break;
+      case  6242:  esalt_size = sizeof (tc_t);            break;
       case  6243:  esalt_size = sizeof (tc_t);            break;
       case  6600:  esalt_size = sizeof (agilekey_t);      break;
       case  7100:  esalt_size = sizeof (pbkdf2_sha512_t); break;
@@ -10126,6 +10469,7 @@ int main (int argc, char **argv)
       case 12000:  esalt_size = sizeof (pbkdf2_sha1_t);   break;
       case 12100:  esalt_size = sizeof (pbkdf2_sha512_t); break;
       case 13000:  esalt_size = sizeof (rar5_t);          break;
+      case 13100:  esalt_size = sizeof (krb5tgs_t);       break;
     }
 
     data.esalt_size = esalt_size;
@@ -10415,45 +10759,6 @@ int main (int argc, char **argv)
       qsort (pot, pot_cnt, sizeof (pot_t), sort_by_pot);
     }
 
-    /**
-     * kernel accel and loops auto adjustment
-     */
-
-    if (kernel_accel_chgd == 0) kernel_accel = set_kernel_accel (hash_mode);
-    if (kernel_loops_chgd == 0) kernel_loops = set_kernel_loops (hash_mode);
-
-    if (workload_profile == 1)
-    {
-      kernel_loops /= 8;
-      kernel_accel /= 4;
-
-      if (kernel_loops == 0) kernel_loops = 8;
-      if (kernel_accel == 0) kernel_accel = 2;
-    }
-    else if (workload_profile == 3)
-    {
-      kernel_loops *= 8;
-      kernel_accel *= 4;
-
-      if (kernel_loops > 1024) kernel_loops = 1024;
-      if (kernel_accel >  256) kernel_accel =  256; // causes memory problems otherwise
-    }
-
-    // those hashes *must* run at a specific kernel_loops count because of some optimization inside the kernel
-
-    if ((opts_type & OPTS_TYPE_PT_BITSLICE) && (attack_mode == ATTACK_MODE_BF))
-    {
-      kernel_loops = 1024;
-    }
-
-    if (hash_mode == 12500)
-    {
-      kernel_loops = ROUNDS_RAR3 / 16;
-    }
-
-    data.kernel_accel = kernel_accel;
-    data.kernel_loops = kernel_loops;
-
     /**
      * word len
      */
@@ -11239,17 +11544,28 @@ int main (int argc, char **argv)
                     break;
         case 5400:  data.hashfile = mystrdup ("hashcat.ikesha1");
                     break;
-        case 6211:
-        case 6212:
-        case 6213:
-        case 6221:
-        case 6222:
-        case 6223:
-        case 6231:
-        case 6232:
-        case 6233:
-        case 6241:
-        case 6242:
+        case 6211:  data.hashfile = mystrdup ("hashcat.tc");
+                    break;
+        case 6212:  data.hashfile = mystrdup ("hashcat.tc");
+                    break;
+        case 6213:  data.hashfile = mystrdup ("hashcat.tc");
+                    break;
+        case 6221:  data.hashfile = mystrdup ("hashcat.tc");
+                    break;
+        case 6222:  data.hashfile = mystrdup ("hashcat.tc");
+                    break;
+        case 6223:  data.hashfile = mystrdup ("hashcat.tc");
+                    break;
+        case 6231:  data.hashfile = mystrdup ("hashcat.tc");
+                    break;
+        case 6232:  data.hashfile = mystrdup ("hashcat.tc");
+                    break;
+        case 6233:  data.hashfile = mystrdup ("hashcat.tc");
+                    break;
+        case 6241:  data.hashfile = mystrdup ("hashcat.tc");
+                    break;
+        case 6242:  data.hashfile = mystrdup ("hashcat.tc");
+                    break;
         case 6243:  data.hashfile = mystrdup ("hashcat.tc");
                     break;
         case 6600:  data.hashfile = mystrdup ("hashcat.agilekey");
@@ -11284,20 +11600,28 @@ int main (int argc, char **argv)
                      break;
         case  5800:  hashes_buf[0].salt->salt_iter = ROUNDS_ANDROIDPIN - 1;
                      break;
-        case  6211:
-        case  6212:
+        case  6211:  hashes_buf[0].salt->salt_iter = ROUNDS_TRUECRYPT_2K;
+                     break;
+        case  6212:  hashes_buf[0].salt->salt_iter = ROUNDS_TRUECRYPT_2K;
+                     break;
         case  6213:  hashes_buf[0].salt->salt_iter = ROUNDS_TRUECRYPT_2K;
                      break;
-        case  6221:
-        case  6222:
+        case  6221:  hashes_buf[0].salt->salt_iter = ROUNDS_TRUECRYPT_1K;
+                     break;
+        case  6222:  hashes_buf[0].salt->salt_iter = ROUNDS_TRUECRYPT_1K;
+                     break;
         case  6223:  hashes_buf[0].salt->salt_iter = ROUNDS_TRUECRYPT_1K;
                      break;
-        case  6231:
-        case  6232:
+        case  6231:  hashes_buf[0].salt->salt_iter = ROUNDS_TRUECRYPT_1K;
+                     break;
+        case  6232:  hashes_buf[0].salt->salt_iter = ROUNDS_TRUECRYPT_1K;
+                     break;
         case  6233:  hashes_buf[0].salt->salt_iter = ROUNDS_TRUECRYPT_1K;
                      break;
-        case  6241:
-        case  6242:
+        case  6241:  hashes_buf[0].salt->salt_iter = ROUNDS_TRUECRYPT_1K;
+                     break;
+        case  6242:  hashes_buf[0].salt->salt_iter = ROUNDS_TRUECRYPT_1K;
+                     break;
         case  6243:  hashes_buf[0].salt->salt_iter = ROUNDS_TRUECRYPT_1K;
                      break;
         case  6300:  hashes_buf[0].salt->salt_iter = ROUNDS_MD5CRYPT;
@@ -11380,225 +11704,6 @@ int main (int argc, char **argv)
                      break;
       }
 
-      // set special tuning for benchmark-mode 1
-
-      if (benchmark_mode == 1)
-      {
-        kernel_loops *= 8;
-        kernel_accel *= 4;
-
-        switch (hash_mode)
-        {
-          case   400:  kernel_loops = ROUNDS_PHPASS;
-                       kernel_accel = 32;
-                       break;
-          case   500:  kernel_loops = ROUNDS_MD5CRYPT;
-                       kernel_accel = 32;
-                       break;
-          case   501:  kernel_loops = ROUNDS_MD5CRYPT;
-                       kernel_accel = 32;
-                       break;
-          case  1600:  kernel_loops = ROUNDS_MD5CRYPT;
-                       kernel_accel = 32;
-                       break;
-          case  1800:  kernel_loops = ROUNDS_SHA512CRYPT;
-                       kernel_accel = 16;
-                       break;
-          case  2100:  kernel_loops = ROUNDS_DCC2;
-                       kernel_accel = 16;
-                       break;
-          case  2500:  kernel_loops = ROUNDS_WPA2;
-                       kernel_accel = 32;
-                       break;
-          case  3200:  kernel_loops = ROUNDS_BCRYPT;
-                       kernel_accel = 8;
-                       break;
-          case  5200:  kernel_loops = ROUNDS_PSAFE3;
-                       kernel_accel = 16;
-                       break;
-          case  5800:  kernel_loops = ROUNDS_ANDROIDPIN;
-                       kernel_accel = 16;
-                       break;
-          case  6211:  kernel_loops = ROUNDS_TRUECRYPT_2K;
-                       #ifndef OSX
-                       kernel_accel = 64;
-                       #endif
-                       break;
-          case  6212:  kernel_loops = ROUNDS_TRUECRYPT_2K;
-                       kernel_accel = 32;
-                       break;
-          case  6213:  kernel_loops = ROUNDS_TRUECRYPT_2K;
-                       kernel_accel = 32;
-                       break;
-          case  6221:  kernel_loops = ROUNDS_TRUECRYPT_1K;
-                       kernel_accel = 8;
-                       break;
-          case  6222:  kernel_loops = ROUNDS_TRUECRYPT_1K;
-                       kernel_accel = 8;
-                       break;
-          case  6223:  kernel_loops = ROUNDS_TRUECRYPT_1K;
-                       kernel_accel = 8;
-                       break;
-          case  6231:  kernel_loops = ROUNDS_TRUECRYPT_1K;
-                       kernel_accel = 8;
-                       break;
-          case  6232:  kernel_loops = ROUNDS_TRUECRYPT_1K;
-                       kernel_accel = 8;
-                       break;
-          case  6233:  kernel_loops = ROUNDS_TRUECRYPT_1K;
-                       kernel_accel = 8;
-                       break;
-          case  6241:  kernel_loops = ROUNDS_TRUECRYPT_1K;
-                       #ifndef OSX
-                       kernel_accel = 128;
-                       #endif
-                       break;
-          case  6242:  kernel_loops = ROUNDS_TRUECRYPT_1K;
-                       kernel_accel = 64;
-                       break;
-          case  6243:  kernel_loops = ROUNDS_TRUECRYPT_1K;
-                       kernel_accel = 64;
-                       break;
-          case  6300:  kernel_loops = ROUNDS_MD5CRYPT;
-                       kernel_accel = 32;
-                       break;
-          case  6700:  kernel_loops = ROUNDS_SHA1AIX;
-                       kernel_accel = 128;
-                       break;
-          case  6400:  kernel_loops = ROUNDS_SHA256AIX;
-                       kernel_accel = 128;
-                       break;
-          case  6500:  kernel_loops = ROUNDS_SHA512AIX;
-                       kernel_accel = 32;
-                       break;
-          case  6600:  kernel_loops = ROUNDS_AGILEKEY;
-                       kernel_accel = 64;
-                       break;
-          case  6800:  kernel_loops = ROUNDS_LASTPASS;
-                       kernel_accel = 64;
-                       break;
-          case  7100:  kernel_loops = ROUNDS_SHA512OSX;
-                       kernel_accel = 8;
-                       break;
-          case  7200:  kernel_loops = ROUNDS_GRUB;
-                       #ifndef OSX
-                       kernel_accel = 16;
-                       #endif
-                       break;
-          case  7400:  kernel_loops = ROUNDS_SHA256CRYPT;
-                       kernel_accel = 8;
-                       break;
-          case  7900:  kernel_loops = ROUNDS_DRUPAL7;
-                       kernel_accel = 8;
-                       break;
-          case  8200:  kernel_loops = ROUNDS_CLOUDKEY;
-                       kernel_accel = 8;
-                       break;
-          case  8800:  kernel_loops = ROUNDS_ANDROIDFDE;
-                       kernel_accel = 32;
-                       break;
-          case  8900:  kernel_loops = 1;
-                       kernel_accel = 64;
-                       break;
-          case  9000:  kernel_loops = ROUNDS_PSAFE2;
-                       kernel_accel = 16;
-                       break;
-          case  9100:  kernel_loops = ROUNDS_LOTUS8;
-                       kernel_accel = 64;
-                       break;
-          case  9200:  kernel_loops = ROUNDS_CISCO8;
-                       kernel_accel = 8;
-                       break;
-          case  9300:  kernel_loops = 1;
-                       kernel_accel = 4;
-                       break;
-          case  9400:  kernel_loops = ROUNDS_OFFICE2007;
-                       kernel_accel = 32;
-                       break;
-          case  9500:  kernel_loops = ROUNDS_OFFICE2010;
-                       kernel_accel = 32;
-                       break;
-          case  9600:  kernel_loops = ROUNDS_OFFICE2013;
-                       kernel_accel = 8;
-                       break;
-          case 10000:  kernel_loops = ROUNDS_DJANGOPBKDF2;
-                       kernel_accel = 8;
-                       break;
-          case 10300:  kernel_loops = ROUNDS_SAPH_SHA1;
-                       kernel_accel = 16;
-                       break;
-          case 10500:  kernel_loops = ROUNDS_PDF14;
-                       kernel_accel = 256;
-                       break;
-          case 10700:  kernel_loops = ROUNDS_PDF17L8;
-                       kernel_accel = 8;
-                       break;
-          case 10900:  kernel_loops = ROUNDS_PBKDF2_SHA256;
-                       kernel_accel = 8;
-                       break;
-          case 11300:  kernel_loops = ROUNDS_BITCOIN_WALLET;
-                       kernel_accel = 8;
-                       break;
-          case 11600:  kernel_loops = ROUNDS_SEVEN_ZIP;
-                       kernel_accel = 8;
-                       break;
-          case 11900:  kernel_loops = ROUNDS_PBKDF2_MD5;
-                       kernel_accel = 8;
-                       break;
-          case 12000:  kernel_loops = ROUNDS_PBKDF2_SHA1;
-                       kernel_accel = 8;
-                       break;
-          case 12100:  kernel_loops = ROUNDS_PBKDF2_SHA512;
-                       kernel_accel = 8;
-                       break;
-          case 12200:  kernel_loops = ROUNDS_ECRYPTFS;
-                       kernel_accel = 8;
-                       break;
-          case 12300:  kernel_loops = ROUNDS_ORACLET;
-                       kernel_accel = 8;
-                       break;
-          case 12500:  kernel_loops = ROUNDS_RAR3;
-                       kernel_accel = 32;
-                       break;
-          case 12700:  kernel_loops = ROUNDS_MYWALLET;
-                       kernel_accel = 512;
-                       break;
-          case 12800:  kernel_loops = ROUNDS_MS_DRSR;
-                       kernel_accel = 512;
-                       break;
-          case 12900:  kernel_loops = ROUNDS_ANDROIDFDE_SAMSUNG;
-                       kernel_accel = 8;
-                       break;
-          case 13000:  kernel_loops = ROUNDS_RAR5;
-                       kernel_accel = 8;
-                       break;
-        }
-
-        // some algorithm collide too fast, make that impossible
-
-        switch (hash_mode)
-        {
-          case 11500:  ((uint *) digests_buf)[1] = 1;
-                       break;
-        }
-
-        if (kernel_loops > 1024) kernel_loops = 1024;
-        if (kernel_accel >  256) kernel_accel =  256; // causes memory problems otherwise
-      }
-
-      if ((opts_type & OPTS_TYPE_PT_BITSLICE) && (attack_mode == ATTACK_MODE_BF))
-      {
-        kernel_loops = 1024;
-      }
-
-      if (hash_mode == 12500)
-      {
-        kernel_loops = ROUNDS_RAR3 / 16;
-      }
-
-      data.kernel_accel = kernel_accel;
-      data.kernel_loops = kernel_loops;
-
       hashes_cnt = 1;
     }
 
@@ -11833,7 +11938,7 @@ int main (int argc, char **argv)
 
               if (hash_mode == 6800)
               {
-                if (i < 48) // 48 = 12 * uint in salt_buf[]
+                if (i < 64) // 64 = 16 * uint in salt_buf[]
                 {
                   // manipulate salt_buf
                   memcpy (hash_buf.salt->salt_buf, line_buf, i);
@@ -11845,7 +11950,7 @@ int main (int argc, char **argv)
               }
               else if (hash_mode == 2500)
               {
-                if (i < 48) // 48 = 12 * uint in salt_buf[]
+                if (i < 64) // 64 = 16 * uint in salt_buf[]
                 {
                   // here we have in line_buf: ESSID:MAC1:MAC2   (without the plain)
                   // manipulate salt_buf
@@ -12521,12 +12626,10 @@ int main (int argc, char **argv)
      */
 
     cl_platform_id platforms[CL_PLATFORMS_MAX] = { 0 };
-
-    cl_uint platforms_cnt = 0;
-
     cl_device_id platform_devices[DEVICES_MAX] = { 0 };
 
-    cl_uint platform_devices_cnt;
+    cl_uint platforms_cnt = 0;
+    cl_uint platform_devices_cnt = 0;
 
     if (keyspace == 0)
     {
@@ -12588,6 +12691,8 @@ int main (int argc, char **argv)
 
       for (uint platform_devices_id = 0; platform_devices_id < platform_devices_cnt; platform_devices_id++)
       {
+        size_t param_value_size = 0;
+
         const uint device_id = devices_cnt;
 
         hc_device_param_t *device_param = &data.devices_param[device_id];
@@ -12618,20 +12723,40 @@ int main (int argc, char **argv)
 
         // device_name
 
-        char *device_name = (char *) mymalloc (INFOSZ);
+        hc_clGetDeviceInfo (data.ocl, device_param->device, CL_DEVICE_NAME, 0, NULL, &param_value_size);
+
+        char *device_name = (char *) mymalloc (param_value_size);
 
-        hc_clGetDeviceInfo (data.ocl, device_param->device, CL_DEVICE_NAME, INFOSZ, device_name, NULL);
+        hc_clGetDeviceInfo (data.ocl, device_param->device, CL_DEVICE_NAME, param_value_size, device_name, NULL);
 
         device_param->device_name = device_name;
 
+        // tuning db
+
+        tuning_db_entry_t *tuningdb_entry = tuning_db_search (tuning_db, device_param, attack_mode, hash_mode);
+
         // device_version
 
-        char *device_version = (char *) mymalloc (INFOSZ);
+        hc_clGetDeviceInfo (data.ocl, device_param->device, CL_DEVICE_VERSION, 0, NULL, &param_value_size);
+
+        char *device_version = (char *) mymalloc (param_value_size);
 
-        hc_clGetDeviceInfo (data.ocl, device_param->device, CL_DEVICE_VERSION, INFOSZ, device_version, NULL);
+        hc_clGetDeviceInfo (data.ocl, device_param->device, CL_DEVICE_VERSION, param_value_size, device_version, NULL);
 
         device_param->device_version = device_version;
 
+        // device_opencl_version
+
+        hc_clGetDeviceInfo (data.ocl, device_param->device, CL_DEVICE_OPENCL_C_VERSION, 0, NULL, &param_value_size);
+
+        char *device_opencl_version = (char *) mymalloc (param_value_size);
+
+        hc_clGetDeviceInfo (data.ocl, device_param->device, CL_DEVICE_OPENCL_C_VERSION, param_value_size, device_opencl_version, NULL);
+
+        device_param->opencl_v12 = device_opencl_version[9] > '1' || device_opencl_version[11] >= '2';
+
+        myfree (device_opencl_version);
+
         if (strstr (device_version, "pocl"))
         {
           // pocl returns the real vendor_id in CL_DEVICE_VENDOR_ID which causes many problems because of hms and missing amd_bfe () etc
@@ -12642,24 +12767,26 @@ int main (int argc, char **argv)
           device_param->vendor_id = vendor_id;
         }
 
-        // max_compute_units
+        // vector_width
 
         cl_uint vector_width;
 
-        if (opencl_vector_width == OPENCL_VECTOR_WIDTH)
+        if (opencl_vector_width_chgd == 0)
         {
-          hc_clGetDeviceInfo (data.ocl, device_param->device, CL_DEVICE_NATIVE_VECTOR_WIDTH_INT, sizeof (vector_width), &vector_width, NULL);
-
-          if ((vendor_id == VENDOR_ID_NV) && (strstr (device_name, " Ti") || strstr (device_name, " TI")))
+          if (tuningdb_entry == NULL || tuningdb_entry->vector_width == -1)
           {
-            // Yeah that's a super bad hack, but there's no other attribute we could use
-
-            if (vector_width < 2) vector_width *= 2;
+            if (opti_type & OPTI_TYPE_USES_BITS_64)
+            {
+              hc_clGetDeviceInfo (data.ocl, device_param->device, CL_DEVICE_NATIVE_VECTOR_WIDTH_LONG, sizeof (vector_width), &vector_width, NULL);
+            }
+            else
+            {
+              hc_clGetDeviceInfo (data.ocl, device_param->device, CL_DEVICE_NATIVE_VECTOR_WIDTH_INT,  sizeof (vector_width), &vector_width, NULL);
+            }
           }
-
-          if (opti_type & OPTI_TYPE_USES_BITS_64)
+          else
           {
-            if (vector_width > 1) vector_width /= 2;
+            vector_width = (cl_uint) tuningdb_entry->vector_width;
           }
         }
         else
@@ -12667,7 +12794,7 @@ int main (int argc, char **argv)
           vector_width = opencl_vector_width;
         }
 
-        if (vector_width > 8) vector_width = 8;
+        if (vector_width > 16) vector_width = 16;
 
         device_param->vector_width = vector_width;
 
@@ -12711,10 +12838,11 @@ int main (int argc, char **argv)
         device_param->skipped = (skipped1 || skipped2);
 
         // driver_version
+        hc_clGetDeviceInfo (data.ocl, device_param->device, CL_DRIVER_VERSION, 0, NULL, &param_value_size);
 
-        char *driver_version = (char *) mymalloc (INFOSZ);
+        char *driver_version = (char *) mymalloc (param_value_size);
 
-        hc_clGetDeviceInfo (data.ocl, device_param->device, CL_DRIVER_VERSION, INFOSZ, driver_version, NULL);
+        hc_clGetDeviceInfo (data.ocl, device_param->device, CL_DRIVER_VERSION, param_value_size, driver_version, NULL);
 
         device_param->driver_version = driver_version;
 
@@ -12895,138 +13023,88 @@ int main (int argc, char **argv)
             }
           }
 
-          devices_active++;
-        }
+          /**
+           * kernel accel and loops tuning db adjustment
+           */
 
-        // next please
+          device_param->kernel_accel_min = 1;
+          device_param->kernel_accel_max = 1024;
 
-        devices_cnt++;
-      }
-    }
+          device_param->kernel_loops_min = 1;
+          device_param->kernel_loops_max = 1024;
 
-    if (keyspace == 0 && devices_active == 0)
-    {
-      log_error ("ERROR: No devices found/left");
-
-      return (-1);
-    }
+          tuning_db_entry_t *tuningdb_entry = tuning_db_search (tuning_db, device_param, attack_mode, hash_mode);
 
-    data.devices_cnt = devices_cnt;
+          if (tuningdb_entry)
+          {
+            u32 _kernel_accel = tuningdb_entry->kernel_accel;
+            u32 _kernel_loops = tuningdb_entry->kernel_loops;
 
-    data.devices_active = devices_active;
+            if (_kernel_accel)
+            {
+              device_param->kernel_accel_min = _kernel_accel;
+              device_param->kernel_accel_max = _kernel_accel;
+            }
 
-    if ((benchmark == 1 || quiet == 0) && (algorithm_pos == 0))
-    {
-      log_info ("");
-    }
+            if (_kernel_loops)
+            {
+              if (workload_profile == 1)
+              {
+                _kernel_loops = (_kernel_loops > 8) ? _kernel_loops / 8 : 1;
+              }
+              else if (workload_profile == 2)
+              {
+                _kernel_loops = (_kernel_loops > 4) ? _kernel_loops / 4 : 1;
+              }
 
-    /**
-     * OpenCL devices: allocate buffer for device specific information
-     */
+              device_param->kernel_loops_min = _kernel_loops;
+              device_param->kernel_loops_max = _kernel_loops;
+            }
+          }
 
-    #ifdef HAVE_HWMON
-    int *temp_retain_fanspeed_value = (int *) mycalloc (devices_cnt, sizeof (int));
+          // commandline parameters overwrite tuningdb entries
 
-    #ifdef HAVE_ADL
-    ADLOD6MemClockState *od_clock_mem_status = (ADLOD6MemClockState *) mycalloc (devices_cnt, sizeof (ADLOD6MemClockState));
+          if (kernel_accel)
+          {
+            device_param->kernel_accel_min = kernel_accel;
+            device_param->kernel_accel_max = kernel_accel;
+          }
 
-    int *od_power_control_status = (int *) mycalloc (devices_cnt, sizeof (int));
-    #endif // ADL
-    #endif
+          if (kernel_loops)
+          {
+            device_param->kernel_loops_min = kernel_loops;
+            device_param->kernel_loops_max = kernel_loops;
+          }
 
-    /**
-     * enable custom signal handler(s)
-     */
+          /**
+           * activate device
+           */
 
-    if (benchmark == 0)
-    {
-      hc_signal (sigHandler_default);
-    }
-    else
-    {
-      hc_signal (sigHandler_benchmark);
-    }
+          devices_active++;
+        }
 
-    /**
-     * User-defined GPU temp handling
-     */
+        // next please
 
-    #ifdef HAVE_HWMON
-    if (gpu_temp_disable == 1)
-    {
-      gpu_temp_abort  = 0;
-      gpu_temp_retain = 0;
+        devices_cnt++;
+      }
     }
 
-    if ((gpu_temp_abort != 0) && (gpu_temp_retain != 0))
+    if (keyspace == 0 && devices_active == 0)
     {
-      if (gpu_temp_abort < gpu_temp_retain)
-      {
-        log_error ("ERROR: invalid values for gpu-temp-abort. Parameter gpu-temp-abort is less than gpu-temp-retain.");
+      log_error ("ERROR: No devices found/left");
 
-        return (-1);
-      }
+      return (-1);
     }
 
-    data.gpu_temp_disable = gpu_temp_disable;
-    data.gpu_temp_abort   = gpu_temp_abort;
-    data.gpu_temp_retain  = gpu_temp_retain;
-    #endif
+    data.devices_cnt = devices_cnt;
 
-    /**
-     * inform the user
-     */
+    data.devices_active = devices_active;
 
-    if (data.quiet == 0)
+    if ((benchmark == 1 || quiet == 0) && (algorithm_pos == 0))
     {
-      log_info ("Hashes: %u hashes; %u unique digests, %u unique salts", hashes_cnt_orig, digests_cnt, salts_cnt);
-
-      log_info ("Bitmaps: %u bits, %u entries, 0x%08x mask, %u bytes, %u/%u rotates", bitmap_bits, bitmap_nums, bitmap_mask, bitmap_size, bitmap_shift1, bitmap_shift2);
-
-      if (attack_mode == ATTACK_MODE_STRAIGHT)
-      {
-        log_info ("Rules: %u", kernel_rules_cnt);
-      }
-
-      if (opti_type)
-      {
-        log_info ("Applicable Optimizers:");
-
-        for (uint i = 0; i < 32; i++)
-        {
-          const uint opti_bit = 1u << i;
-
-          if (opti_type & opti_bit) log_info ("* %s", stroptitype (opti_bit));
-        }
-      }
-
-      /**
-       * Watchdog and Temperature balance
-       */
-
-      #ifdef HAVE_HWMON
-      if (gpu_temp_abort == 0)
-      {
-        log_info ("Watchdog: Temperature abort trigger disabled");
-      }
-      else
-      {
-        log_info ("Watchdog: Temperature abort trigger set to %uc", gpu_temp_abort);
-      }
-
-      if (gpu_temp_retain == 0)
-      {
-        log_info ("Watchdog: Temperature retain trigger disabled");
-      }
-      else
-      {
-        log_info ("Watchdog: Temperature retain trigger set to %uc", gpu_temp_retain);
-      }
-      #endif
+      log_info ("");
     }
 
-    if (data.quiet == 0) log_info ("");
-
     /**
      * HM devices: init
      */
@@ -13043,36 +13121,47 @@ int main (int argc, char **argv)
     if (gpu_temp_disable == 0)
     {
       #if defined(WIN) && defined(HAVE_NVAPI)
-      if (NvAPI_Initialize () == NVAPI_OK)
+      NVAPI_PTR *nvapi = (NVAPI_PTR *) mymalloc (sizeof (NVAPI_PTR));
+
+      if (nvapi_init (nvapi) == 0)
+        data.hm_nv = nvapi;
+
+      if (data.hm_nv)
       {
-        HM_ADAPTER_NV nvGPUHandle[DEVICES_MAX] = { 0 };
+        if (hm_NvAPI_Initialize (data.hm_nv) == NVAPI_OK)
+        {
+          HM_ADAPTER_NV nvGPUHandle[DEVICES_MAX] = { 0 };
 
-        int tmp_in = hm_get_adapter_index_nv (nvGPUHandle);
+          int tmp_in = hm_get_adapter_index_nv (nvGPUHandle);
 
-        int tmp_out = 0;
+          int tmp_out = 0;
 
-        for (int i = 0; i < tmp_in; i++)
-        {
-          hm_adapters_nv[tmp_out++].adapter_index.nv = nvGPUHandle[i];
-        }
+          for (int i = 0; i < tmp_in; i++)
+          {
+            hm_adapters_nv[tmp_out++].adapter_index.nv = nvGPUHandle[i];
+          }
 
-        for (int i = 0; i < tmp_out; i++)
-        {
-          NvU32 speed;
+          for (int i = 0; i < tmp_out; i++)
+          {
+            NV_GPU_COOLER_SETTINGS pCoolerSettings;
 
-          if (NvAPI_GPU_GetTachReading (hm_adapters_nv[i].adapter_index.nv, &speed) != NVAPI_NOT_SUPPORTED) hm_adapters_nv[i].fan_supported = 1;
+            pCoolerSettings.Version = GPU_COOLER_SETTINGS_VER | sizeof (NV_GPU_COOLER_SETTINGS);
+
+            if (hm_NvAPI_GPU_GetCoolerSettings (data.hm_nv, hm_adapters_nv[i].adapter_index.nv, 0, &pCoolerSettings) != NVAPI_NOT_SUPPORTED) hm_adapters_nv[i].fan_supported = 1;
+          }
         }
       }
       #endif // WIN && HAVE_NVAPI
 
       #if defined(LINUX) && defined(HAVE_NVML)
-      HM_LIB hm_dll_nv = hm_init (VENDOR_ID_NV);
+      NVML_PTR *nvml = (NVML_PTR *) mymalloc (sizeof (NVML_PTR));
 
-      data.hm_dll_nv = hm_dll_nv;
+      if (nvml_init (nvml) == 0)
+        data.hm_nv = nvml;
 
-      if (hm_dll_nv)
+      if (data.hm_nv)
       {
-        if (hc_NVML_nvmlInit (hm_dll_nv) == NVML_SUCCESS)
+        if (hm_NVML_nvmlInit (data.hm_nv) == NVML_SUCCESS)
         {
           HM_ADAPTER_NV nvGPUHandle[DEVICES_MAX] = { 0 };
 
@@ -13089,67 +13178,187 @@ int main (int argc, char **argv)
           {
             unsigned int speed;
 
-            if (hc_NVML_nvmlDeviceGetFanSpeed (hm_dll_nv, 1, hm_adapters_nv[i].adapter_index.nv, &speed) != NVML_ERROR_NOT_SUPPORTED) hm_adapters_nv[i].fan_supported = 1;
+            if (hm_NVML_nvmlDeviceGetFanSpeed (data.hm_nv, 1, hm_adapters_nv[i].adapter_index.nv, &speed) != NVML_ERROR_NOT_SUPPORTED) hm_adapters_nv[i].fan_supported = 1;
           }
         }
       }
       #endif // LINUX && HAVE_NVML
 
+      data.hm_amd = NULL;
+
       #ifdef HAVE_ADL
-      HM_LIB hm_dll_amd = hm_init (VENDOR_ID_AMD);
+      ADL_PTR *adl = (ADL_PTR *) mymalloc (sizeof (ADL_PTR));
 
-      data.hm_dll_amd = hm_dll_amd;
+      if (adl_init (adl) == 0)
+        data.hm_amd = adl;
 
-      if (hm_dll_amd)
+      if (data.hm_amd)
       {
-        if (hc_ADL_Main_Control_Create (hm_dll_amd, ADL_Main_Memory_Alloc, 0) == ADL_OK)
+        if (hm_ADL_Main_Control_Create (data.hm_amd, ADL_Main_Memory_Alloc, 0) == ADL_OK)
         {
           // total number of adapters
 
-          int hm_adapters_num;
+          int hm_adapters_num;
+
+          if (get_adapters_num_amd (data.hm_amd, &hm_adapters_num) != 0) return (-1);
+
+          // adapter info
+
+          LPAdapterInfo lpAdapterInfo = hm_get_adapter_info_amd (data.hm_amd, hm_adapters_num);
+
+          if (lpAdapterInfo == NULL) return (-1);
+
+          // get a list (of ids of) valid/usable adapters
+
+          int num_adl_adapters = 0;
+
+          u32 *valid_adl_device_list = hm_get_list_valid_adl_adapters (hm_adapters_num, &num_adl_adapters, lpAdapterInfo);
+
+          if (num_adl_adapters > 0)
+          {
+            hc_thread_mutex_lock (mux_adl);
+
+            // hm_get_opencl_busid_devid (hm_adapters_amd, devices_all_cnt, devices_all);
+
+            hm_get_adapter_index_amd (hm_adapters_amd, valid_adl_device_list, num_adl_adapters, lpAdapterInfo);
+
+            hm_get_overdrive_version  (data.hm_amd, hm_adapters_amd, valid_adl_device_list, num_adl_adapters, lpAdapterInfo);
+            hm_check_fanspeed_control (data.hm_amd, hm_adapters_amd, valid_adl_device_list, num_adl_adapters, lpAdapterInfo);
+
+            hc_thread_mutex_unlock (mux_adl);
+          }
+
+          myfree (valid_adl_device_list);
+          myfree (lpAdapterInfo);
+        }
+      }
+      #endif // HAVE_ADL
+
+      if (data.hm_amd == NULL && data.hm_nv == NULL)
+      {
+        gpu_temp_disable = 1;
+      }
+    }
+
+    /**
+     * OpenCL devices: allocate buffer for device specific information
+     */
+
+    #ifdef HAVE_HWMON
+    int *temp_retain_fanspeed_value = (int *) mycalloc (data.devices_cnt, sizeof (int));
+
+    #ifdef HAVE_ADL
+    ADLOD6MemClockState *od_clock_mem_status = (ADLOD6MemClockState *) mycalloc (data.devices_cnt, sizeof (ADLOD6MemClockState));
+
+    int *od_power_control_status = (int *) mycalloc (data.devices_cnt, sizeof (int));
+    #endif // ADL
+    #endif
+
+    /**
+     * enable custom signal handler(s)
+     */
+
+    if (benchmark == 0)
+    {
+      hc_signal (sigHandler_default);
+    }
+    else
+    {
+      hc_signal (sigHandler_benchmark);
+    }
+
+    /**
+     * User-defined GPU temp handling
+     */
+
+    #ifdef HAVE_HWMON
+    if (gpu_temp_disable == 1)
+    {
+      gpu_temp_abort  = 0;
+      gpu_temp_retain = 0;
+    }
+
+    if ((gpu_temp_abort != 0) && (gpu_temp_retain != 0))
+    {
+      if (gpu_temp_abort < gpu_temp_retain)
+      {
+        log_error ("ERROR: invalid values for gpu-temp-abort. Parameter gpu-temp-abort is less than gpu-temp-retain.");
 
-          if (get_adapters_num_amd (hm_dll_amd, &hm_adapters_num) != 0) return (-1);
+        return (-1);
+      }
+    }
 
-          // adapter info
+    data.gpu_temp_disable = gpu_temp_disable;
+    data.gpu_temp_abort   = gpu_temp_abort;
+    data.gpu_temp_retain  = gpu_temp_retain;
+    #endif
 
-          LPAdapterInfo lpAdapterInfo = hm_get_adapter_info_amd (hm_dll_amd, hm_adapters_num);
+    /**
+     * inform the user
+     */
 
-          if (lpAdapterInfo == NULL) return (-1);
+    if (data.quiet == 0)
+    {
+      log_info ("Hashes: %u hashes; %u unique digests, %u unique salts", hashes_cnt_orig, digests_cnt, salts_cnt);
 
-          // get a list (of ids of) valid/usable adapters
+      log_info ("Bitmaps: %u bits, %u entries, 0x%08x mask, %u bytes, %u/%u rotates", bitmap_bits, bitmap_nums, bitmap_mask, bitmap_size, bitmap_shift1, bitmap_shift2);
 
-          int num_adl_adapters = 0;
+      if (attack_mode == ATTACK_MODE_STRAIGHT)
+      {
+        log_info ("Rules: %u", kernel_rules_cnt);
+      }
 
-          u32 *valid_adl_device_list = hm_get_list_valid_adl_adapters (hm_adapters_num, &num_adl_adapters, lpAdapterInfo);
+      if (opti_type)
+      {
+        log_info ("Applicable Optimizers:");
 
-          if (num_adl_adapters > 0)
-          {
-            hc_thread_mutex_lock (mux_adl);
+        for (uint i = 0; i < 32; i++)
+        {
+          const uint opti_bit = 1u << i;
 
-            // hm_get_opencl_busid_devid (hm_adapters_amd, devices_all_cnt, devices_all);
+          if (opti_type & opti_bit) log_info ("* %s", stroptitype (opti_bit));
+        }
+      }
 
-            hm_get_adapter_index_amd (hm_adapters_amd, valid_adl_device_list, num_adl_adapters, lpAdapterInfo);
+      /**
+       * Watchdog and Temperature balance
+       */
 
-            hm_get_overdrive_version  (hm_dll_amd, hm_adapters_amd, valid_adl_device_list, num_adl_adapters, lpAdapterInfo);
-            hm_check_fanspeed_control (hm_dll_amd, hm_adapters_amd, valid_adl_device_list, num_adl_adapters, lpAdapterInfo);
+      #ifdef HAVE_HWMON
+      if (gpu_temp_disable == 0 && data.hm_amd == NULL && data.hm_nv == NULL)
+      {
+        log_info ("Watchdog: Hardware Monitoring Interface not found on your system");
+      }
 
-            hc_thread_mutex_unlock (mux_adl);
-          }
+      if (gpu_temp_abort == 0)
+      {
+        log_info ("Watchdog: Temperature abort trigger disabled");
+      }
+      else
+      {
+        log_info ("Watchdog: Temperature abort trigger set to %uc", gpu_temp_abort);
+      }
 
-          myfree (valid_adl_device_list);
-          myfree (lpAdapterInfo);
-        }
+      if (gpu_temp_retain == 0)
+      {
+        log_info ("Watchdog: Temperature retain trigger disabled");
       }
-      #endif // HAVE_ADL
+      else
+      {
+        log_info ("Watchdog: Temperature retain trigger set to %uc", gpu_temp_retain);
+      }
+      #endif
     }
 
+    if (data.quiet == 0) log_info ("");
+
     /**
      * HM devices: copy
      */
 
     if (gpu_temp_disable == 0)
     {
-      for (uint device_id = 0; device_id < devices_cnt; device_id++)
+      for (uint device_id = 0; device_id < data.devices_cnt; device_id++)
       {
         hc_device_param_t *device_param = &data.devices_param[device_id];
 
@@ -13179,7 +13388,7 @@ int main (int argc, char **argv)
     * Temporary fix:
     * with AMD r9 295x cards it seems that we need to set the powertune value just AFTER the ocl init stuff
     * otherwise after hc_clCreateContext () etc, powertune value was set back to "normal" and cards unfortunately
-    * were not working @ full speed (setting hc_ADL_Overdrive_PowerControl_Set () here seems to fix the problem)
+    * were not working @ full speed (setting hm_ADL_Overdrive_PowerControl_Set () here seems to fix the problem)
     * Driver / ADL bug?
     */
 
@@ -13188,7 +13397,7 @@ int main (int argc, char **argv)
     {
       hc_thread_mutex_lock (mux_adl);
 
-      for (uint device_id = 0; device_id < devices_cnt; device_id++)
+      for (uint device_id = 0; device_id < data.devices_cnt; device_id++)
       {
         hc_device_param_t *device_param = &data.devices_param[device_id];
 
@@ -13202,7 +13411,7 @@ int main (int argc, char **argv)
 
           int ADL_rc = 0;
 
-          if ((ADL_rc = hc_ADL_Overdrive6_PowerControl_Caps (data.hm_dll_amd, data.hm_device[device_id].adapter_index.amd, &powertune_supported)) != ADL_OK)
+          if ((ADL_rc = hm_ADL_Overdrive6_PowerControl_Caps (data.hm_amd, data.hm_device[device_id].adapter_index.amd, &powertune_supported)) != ADL_OK)
           {
             log_error ("ERROR: Failed to get ADL PowerControl Capabilities");
 
@@ -13214,14 +13423,14 @@ int main (int argc, char **argv)
             // powertune set
             ADLOD6PowerControlInfo powertune = {0, 0, 0, 0, 0};
 
-            if ((ADL_rc = hc_ADL_Overdrive_PowerControlInfo_Get (data.hm_dll_amd, data.hm_device[device_id].adapter_index.amd, &powertune)) != ADL_OK)
+            if ((ADL_rc = hm_ADL_Overdrive_PowerControlInfo_Get (data.hm_amd, data.hm_device[device_id].adapter_index.amd, &powertune)) != ADL_OK)
             {
               log_error ("ERROR: Failed to get current ADL PowerControl settings");
 
               return (-1);
             }
 
-            if ((ADL_rc = hc_ADL_Overdrive_PowerControl_Set (data.hm_dll_amd, data.hm_device[device_id].adapter_index.amd, powertune.iMaxValue)) != ADL_OK)
+            if ((ADL_rc = hm_ADL_Overdrive_PowerControl_Set (data.hm_amd, data.hm_device[device_id].adapter_index.amd, powertune.iMaxValue)) != ADL_OK)
             {
               log_error ("ERROR: Failed to set new ADL PowerControl values");
 
@@ -13236,28 +13445,13 @@ int main (int argc, char **argv)
     #endif // HAVE_ADK
     #endif // HAVE_HWMON
 
-    #ifdef OSX
-    if (hash_mode == 3000 || hash_mode == 1500 || hash_mode == 10700)
-    {
-      if (force == 0)
-      {
-        log_info ("");
-        log_info ("Warning: Hash mode %d is not stable with OSX.", hash_mode);
-        log_info ("You can use --force to override this but do not post error reports if you do so");
-        log_info ("");
-
-        continue;
-      }
-    }
-    #endif
-
     #ifdef DEBUG
     if (benchmark == 1) log_info ("Hashmode: %d", data.hash_mode);
     #endif
 
-    uint kernel_blocks_all = 0;
+    uint kernel_power_all = 0;
 
-    for (uint device_id = 0; device_id < devices_cnt; device_id++)
+    for (uint device_id = 0; device_id < data.devices_cnt; device_id++)
     {
       /**
        * host buffer
@@ -13271,13 +13465,9 @@ int main (int argc, char **argv)
        * device properties
        */
 
-      char *device_name_chksum = device_param->device_name_chksum;
-
-      uint device_processors = device_param->device_processors;
-
-      uint device_processor_cores = device_param->device_processor_cores;
-
-      cl_device_type device_type = device_param->device_type;
+      const char *device_name_chksum      = device_param->device_name_chksum;
+      const u32   device_processors       = device_param->device_processors;
+      const u32   device_processor_cores  = device_param->device_processor_cores;
 
       /**
        * create context for each device
@@ -13292,127 +13482,12 @@ int main (int argc, char **argv)
       // not supported with NV
       // device_param->command_queue = hc_clCreateCommandQueueWithProperties (device_param->context, device_param->device, NULL);
 
-      device_param->command_queue = hc_clCreateCommandQueue (data.ocl, device_param->context, device_param->device, 0);
+      device_param->command_queue = hc_clCreateCommandQueue (data.ocl, device_param->context, device_param->device, CL_QUEUE_PROFILING_ENABLE);
 
       /**
-       * create input buffers on device
+       * create input buffers on device : calculate size of fixed memory buffers
        */
 
-      uint kernel_threads = KERNEL_THREADS;
-
-      // bcrypt
-      if (hash_mode == 3200) kernel_threads = 8;
-      if (hash_mode == 9000) kernel_threads = 8;
-
-      if (device_type & CL_DEVICE_TYPE_CPU)
-      {
-        if (benchmark_mode == 0)
-        {
-          if (kernel_accel > 16)
-          {
-            kernel_accel = 16;
-          }
-        }
-        else
-        {
-          if (kernel_accel > 64)
-          {
-            kernel_accel = 64;
-          }
-        }
-      }
-
-      uint kernel_power  = device_processors * kernel_threads * kernel_accel;
-      uint kernel_blocks = kernel_power;
-
-      device_param->kernel_threads      = kernel_threads;
-      device_param->kernel_power_user   = kernel_power;
-      device_param->kernel_blocks_user  = kernel_blocks;
-
-      kernel_blocks_all += kernel_blocks;
-
-      uint size_pws = kernel_power * sizeof (pw_t);
-
-      uint size_tmps = 4;
-
-      switch (hash_mode)
-      {
-        case   400: size_tmps = kernel_blocks * sizeof (phpass_tmp_t);        break;
-        case   500: size_tmps = kernel_blocks * sizeof (md5crypt_tmp_t);      break;
-        case   501: size_tmps = kernel_blocks * sizeof (md5crypt_tmp_t);      break;
-        case  1600: size_tmps = kernel_blocks * sizeof (md5crypt_tmp_t);      break;
-        case  1800: size_tmps = kernel_blocks * sizeof (sha512crypt_tmp_t);   break;
-        case  2100: size_tmps = kernel_blocks * sizeof (dcc2_tmp_t);          break;
-        case  2500: size_tmps = kernel_blocks * sizeof (wpa_tmp_t);           break;
-        case  3200: size_tmps = kernel_blocks * sizeof (bcrypt_tmp_t);        break;
-        case  5200: size_tmps = kernel_blocks * sizeof (pwsafe3_tmp_t);       break;
-        case  5800: size_tmps = kernel_blocks * sizeof (androidpin_tmp_t);    break;
-        case  6211:
-        case  6212:
-        case  6213: size_tmps = kernel_blocks * sizeof (tc_tmp_t);            break;
-        case  6221:
-        case  6222:
-        case  6223: size_tmps = kernel_blocks * sizeof (tc64_tmp_t);          break;
-        case  6231:
-        case  6232:
-        case  6233: size_tmps = kernel_blocks * sizeof (tc_tmp_t);            break;
-        case  6241:
-        case  6242:
-        case  6243: size_tmps = kernel_blocks * sizeof (tc_tmp_t);            break;
-        case  6300: size_tmps = kernel_blocks * sizeof (md5crypt_tmp_t);      break;
-        case  6400: size_tmps = kernel_blocks * sizeof (sha256aix_tmp_t);     break;
-        case  6500: size_tmps = kernel_blocks * sizeof (sha512aix_tmp_t);     break;
-        case  6600: size_tmps = kernel_blocks * sizeof (agilekey_tmp_t);      break;
-        case  6700: size_tmps = kernel_blocks * sizeof (sha1aix_tmp_t);       break;
-        case  6800: size_tmps = kernel_blocks * sizeof (lastpass_tmp_t);      break;
-        case  7100: size_tmps = kernel_blocks * sizeof (pbkdf2_sha512_tmp_t); break;
-        case  7200: size_tmps = kernel_blocks * sizeof (pbkdf2_sha512_tmp_t); break;
-        case  7400: size_tmps = kernel_blocks * sizeof (sha256crypt_tmp_t);   break;
-        case  7900: size_tmps = kernel_blocks * sizeof (drupal7_tmp_t);       break;
-        case  8200: size_tmps = kernel_blocks * sizeof (pbkdf2_sha512_tmp_t); break;
-        case  8800: size_tmps = kernel_blocks * sizeof (androidfde_tmp_t);    break;
-        case  8900: size_tmps = kernel_blocks * sizeof (scrypt_tmp_t);        break;
-        case  9000: size_tmps = kernel_blocks * sizeof (pwsafe2_tmp_t);       break;
-        case  9100: size_tmps = kernel_blocks * sizeof (lotus8_tmp_t);        break;
-        case  9200: size_tmps = kernel_blocks * sizeof (pbkdf2_sha256_tmp_t); break;
-        case  9300: size_tmps = kernel_blocks * sizeof (scrypt_tmp_t);        break;
-        case  9400: size_tmps = kernel_blocks * sizeof (office2007_tmp_t);    break;
-        case  9500: size_tmps = kernel_blocks * sizeof (office2010_tmp_t);    break;
-        case  9600: size_tmps = kernel_blocks * sizeof (office2013_tmp_t);    break;
-        case 10000: size_tmps = kernel_blocks * sizeof (pbkdf2_sha256_tmp_t); break;
-        case 10200: size_tmps = kernel_blocks * sizeof (cram_md5_t);          break;
-        case 10300: size_tmps = kernel_blocks * sizeof (saph_sha1_tmp_t);     break;
-        case 10500: size_tmps = kernel_blocks * sizeof (pdf14_tmp_t);         break;
-        case 10700: size_tmps = kernel_blocks * sizeof (pdf17l8_tmp_t);       break;
-        case 10900: size_tmps = kernel_blocks * sizeof (pbkdf2_sha256_tmp_t); break;
-        case 11300: size_tmps = kernel_blocks * sizeof (bitcoin_wallet_tmp_t); break;
-        case 11600: size_tmps = kernel_blocks * sizeof (seven_zip_tmp_t);     break;
-        case 11900: size_tmps = kernel_blocks * sizeof (pbkdf2_md5_tmp_t);    break;
-        case 12000: size_tmps = kernel_blocks * sizeof (pbkdf2_sha1_tmp_t);   break;
-        case 12100: size_tmps = kernel_blocks * sizeof (pbkdf2_sha512_tmp_t); break;
-        case 12200: size_tmps = kernel_blocks * sizeof (ecryptfs_tmp_t);      break;
-        case 12300: size_tmps = kernel_blocks * sizeof (oraclet_tmp_t);       break;
-        case 12400: size_tmps = kernel_blocks * sizeof (bsdicrypt_tmp_t);     break;
-        case 12500: size_tmps = kernel_blocks * sizeof (rar3_tmp_t);          break;
-        case 12700: size_tmps = kernel_blocks * sizeof (mywallet_tmp_t);      break;
-        case 12800: size_tmps = kernel_blocks * sizeof (pbkdf2_sha256_tmp_t); break;
-        case 12900: size_tmps = kernel_blocks * sizeof (pbkdf2_sha256_tmp_t); break;
-        case 13000: size_tmps = kernel_blocks * sizeof (pbkdf2_sha256_tmp_t); break;
-      };
-
-      uint size_hooks = 4;
-
-      if ((opts_type & OPTS_TYPE_HOOK12) || (opts_type & OPTS_TYPE_HOOK23))
-      {
-        // insert correct hook size
-      }
-
-      // we can optimize some stuff here...
-
-      device_param->size_pws   = size_pws;
-      device_param->size_tmps  = size_tmps;
-      device_param->size_hooks = size_hooks;
-
       uint size_root_css   = SP_PW_MAX *           sizeof (cs_t);
       uint size_markov_css = SP_PW_MAX * CHARSIZ * sizeof (cs_t);
 
@@ -13421,14 +13496,14 @@ int main (int argc, char **argv)
 
       uint size_results = KERNEL_THREADS * sizeof (uint);
 
-      device_param->size_results  = size_results;
+      device_param->size_results = size_results;
 
       uint size_rules   = kernel_rules_cnt * sizeof (kernel_rule_t);
       uint size_rules_c = KERNEL_RULES     * sizeof (kernel_rule_t);
 
       uint size_plains  = digests_cnt * sizeof (plain_t);
-      uint size_salts   = salts_cnt * sizeof (salt_t);
-      uint size_esalts  = salts_cnt * esalt_size;
+      uint size_salts   = salts_cnt   * sizeof (salt_t);
+      uint size_esalts  = salts_cnt   * esalt_size;
 
       device_param->size_plains   = size_plains;
       device_param->size_digests  = size_digests;
@@ -13437,7 +13512,9 @@ int main (int argc, char **argv)
 
       uint size_combs = KERNEL_COMBS * sizeof (comb_t);
       uint size_bfs   = KERNEL_BFS   * sizeof (bf_t);
-      uint size_tm    = 32        * sizeof (bs_word_t);
+      uint size_tm    = 32           * sizeof (bs_word_t);
+
+      // scryptV stuff
 
       u64 size_scryptV = 1;
 
@@ -13531,6 +13608,259 @@ int main (int argc, char **argv)
         if (quiet == 0) log_info ("SCRYPT tmto optimizer value set to: %u, mem: %u\n", data.salts_buf[0].scrypt_tmto, size_scryptV);
       }
 
+      /**
+       * create input buffers on device : calculate size of dynamic size memory buffers
+       */
+
+      uint kernel_threads = KERNEL_THREADS;
+
+      // some algorithms need a fixed kernel-threads count (mostly because of shared memory usage)
+
+      if (hash_mode == 3200) kernel_threads = 8;
+      if (hash_mode == 9000) kernel_threads = 8;
+
+      /**
+       * some algorithms need a fixed kernel-loops count
+       */
+
+      if (hash_mode == 1500)
+      {
+        const u32 kernel_loops_fixed = 1024;
+
+        device_param->kernel_loops_min = kernel_loops_fixed;
+        device_param->kernel_loops_max = kernel_loops_fixed;
+      }
+
+      if (hash_mode == 3000)
+      {
+        const u32 kernel_loops_fixed = 1024;
+
+        device_param->kernel_loops_min = kernel_loops_fixed;
+        device_param->kernel_loops_max = kernel_loops_fixed;
+      }
+
+      if (hash_mode == 8900)
+      {
+        const u32 kernel_loops_fixed = 1;
+
+        device_param->kernel_loops_min = kernel_loops_fixed;
+        device_param->kernel_loops_max = kernel_loops_fixed;
+      }
+
+      if (hash_mode == 9300)
+      {
+        const u32 kernel_loops_fixed = 1;
+
+        device_param->kernel_loops_min = kernel_loops_fixed;
+        device_param->kernel_loops_max = kernel_loops_fixed;
+      }
+
+      if (hash_mode == 12500)
+      {
+        const u32 kernel_loops_fixed = ROUNDS_RAR3 / 16;
+
+        device_param->kernel_loops_min = kernel_loops_fixed;
+        device_param->kernel_loops_max = kernel_loops_fixed;
+      }
+
+      /**
+       * some algorithms have a maximum kernel-loops count
+       */
+
+      if (attack_exec == ATTACK_EXEC_OUTSIDE_KERNEL)
+      {
+        if (data.salts_buf[0].salt_iter < device_param->kernel_loops_max)
+        {
+          device_param->kernel_loops_max = data.salts_buf[0].salt_iter;
+        }
+      }
+
+      /**
+       * some algorithms need a special kernel-accel
+       */
+
+      if (hash_mode == 8900)
+      {
+        device_param->kernel_accel_min = 1;
+        device_param->kernel_accel_max = 64;
+      }
+
+      if (hash_mode == 9300)
+      {
+        device_param->kernel_accel_min = 1;
+        device_param->kernel_accel_max = 64;
+      }
+
+      u32 kernel_accel_min = device_param->kernel_accel_min;
+      u32 kernel_accel_max = device_param->kernel_accel_max;
+
+      // find out if we would request too much memory on memory blocks which are based on kernel_accel
+
+      uint size_pws   = 4;
+      uint size_tmps  = 4;
+      uint size_hooks = 4;
+
+      while (kernel_accel_max >= kernel_accel_min)
+      {
+        uint kernel_power_max = device_processors * kernel_threads * kernel_accel_max;
+
+        // size_pws
+
+        size_pws = kernel_power_max * sizeof (pw_t);
+
+        // size_tmps
+
+        switch (hash_mode)
+        {
+          case   400: size_tmps = kernel_power_max * sizeof (phpass_tmp_t);          break;
+          case   500: size_tmps = kernel_power_max * sizeof (md5crypt_tmp_t);        break;
+          case   501: size_tmps = kernel_power_max * sizeof (md5crypt_tmp_t);        break;
+          case  1600: size_tmps = kernel_power_max * sizeof (md5crypt_tmp_t);        break;
+          case  1800: size_tmps = kernel_power_max * sizeof (sha512crypt_tmp_t);     break;
+          case  2100: size_tmps = kernel_power_max * sizeof (dcc2_tmp_t);            break;
+          case  2500: size_tmps = kernel_power_max * sizeof (wpa_tmp_t);             break;
+          case  3200: size_tmps = kernel_power_max * sizeof (bcrypt_tmp_t);          break;
+          case  5200: size_tmps = kernel_power_max * sizeof (pwsafe3_tmp_t);         break;
+          case  5800: size_tmps = kernel_power_max * sizeof (androidpin_tmp_t);      break;
+          case  6211: size_tmps = kernel_power_max * sizeof (tc_tmp_t);              break;
+          case  6212: size_tmps = kernel_power_max * sizeof (tc_tmp_t);              break;
+          case  6213: size_tmps = kernel_power_max * sizeof (tc_tmp_t);              break;
+          case  6221: size_tmps = kernel_power_max * sizeof (tc64_tmp_t);            break;
+          case  6222: size_tmps = kernel_power_max * sizeof (tc64_tmp_t);            break;
+          case  6223: size_tmps = kernel_power_max * sizeof (tc64_tmp_t);            break;
+          case  6231: size_tmps = kernel_power_max * sizeof (tc_tmp_t);              break;
+          case  6232: size_tmps = kernel_power_max * sizeof (tc_tmp_t);              break;
+          case  6233: size_tmps = kernel_power_max * sizeof (tc_tmp_t);              break;
+          case  6241: size_tmps = kernel_power_max * sizeof (tc_tmp_t);              break;
+          case  6242: size_tmps = kernel_power_max * sizeof (tc_tmp_t);              break;
+          case  6243: size_tmps = kernel_power_max * sizeof (tc_tmp_t);              break;
+          case  6300: size_tmps = kernel_power_max * sizeof (md5crypt_tmp_t);        break;
+          case  6400: size_tmps = kernel_power_max * sizeof (sha256aix_tmp_t);       break;
+          case  6500: size_tmps = kernel_power_max * sizeof (sha512aix_tmp_t);       break;
+          case  6600: size_tmps = kernel_power_max * sizeof (agilekey_tmp_t);        break;
+          case  6700: size_tmps = kernel_power_max * sizeof (sha1aix_tmp_t);         break;
+          case  6800: size_tmps = kernel_power_max * sizeof (lastpass_tmp_t);        break;
+          case  7100: size_tmps = kernel_power_max * sizeof (pbkdf2_sha512_tmp_t);   break;
+          case  7200: size_tmps = kernel_power_max * sizeof (pbkdf2_sha512_tmp_t);   break;
+          case  7400: size_tmps = kernel_power_max * sizeof (sha256crypt_tmp_t);     break;
+          case  7900: size_tmps = kernel_power_max * sizeof (drupal7_tmp_t);         break;
+          case  8200: size_tmps = kernel_power_max * sizeof (pbkdf2_sha512_tmp_t);   break;
+          case  8800: size_tmps = kernel_power_max * sizeof (androidfde_tmp_t);      break;
+          case  8900: size_tmps = kernel_power_max * sizeof (scrypt_tmp_t);          break;
+          case  9000: size_tmps = kernel_power_max * sizeof (pwsafe2_tmp_t);         break;
+          case  9100: size_tmps = kernel_power_max * sizeof (lotus8_tmp_t);          break;
+          case  9200: size_tmps = kernel_power_max * sizeof (pbkdf2_sha256_tmp_t);   break;
+          case  9300: size_tmps = kernel_power_max * sizeof (scrypt_tmp_t);          break;
+          case  9400: size_tmps = kernel_power_max * sizeof (office2007_tmp_t);      break;
+          case  9500: size_tmps = kernel_power_max * sizeof (office2010_tmp_t);      break;
+          case  9600: size_tmps = kernel_power_max * sizeof (office2013_tmp_t);      break;
+          case 10000: size_tmps = kernel_power_max * sizeof (pbkdf2_sha256_tmp_t);   break;
+          case 10200: size_tmps = kernel_power_max * sizeof (cram_md5_t);            break;
+          case 10300: size_tmps = kernel_power_max * sizeof (saph_sha1_tmp_t);       break;
+          case 10500: size_tmps = kernel_power_max * sizeof (pdf14_tmp_t);           break;
+          case 10700: size_tmps = kernel_power_max * sizeof (pdf17l8_tmp_t);         break;
+          case 10900: size_tmps = kernel_power_max * sizeof (pbkdf2_sha256_tmp_t);   break;
+          case 11300: size_tmps = kernel_power_max * sizeof (bitcoin_wallet_tmp_t);  break;
+          case 11600: size_tmps = kernel_power_max * sizeof (seven_zip_tmp_t);       break;
+          case 11900: size_tmps = kernel_power_max * sizeof (pbkdf2_md5_tmp_t);      break;
+          case 12000: size_tmps = kernel_power_max * sizeof (pbkdf2_sha1_tmp_t);     break;
+          case 12100: size_tmps = kernel_power_max * sizeof (pbkdf2_sha512_tmp_t);   break;
+          case 12200: size_tmps = kernel_power_max * sizeof (ecryptfs_tmp_t);        break;
+          case 12300: size_tmps = kernel_power_max * sizeof (oraclet_tmp_t);         break;
+          case 12400: size_tmps = kernel_power_max * sizeof (bsdicrypt_tmp_t);       break;
+          case 12500: size_tmps = kernel_power_max * sizeof (rar3_tmp_t);            break;
+          case 12700: size_tmps = kernel_power_max * sizeof (mywallet_tmp_t);        break;
+          case 12800: size_tmps = kernel_power_max * sizeof (pbkdf2_sha256_tmp_t);   break;
+          case 12900: size_tmps = kernel_power_max * sizeof (pbkdf2_sha256_tmp_t);   break;
+          case 13000: size_tmps = kernel_power_max * sizeof (pbkdf2_sha256_tmp_t);   break;
+        };
+
+        // size_hooks
+
+        if ((opts_type & OPTS_TYPE_HOOK12) || (opts_type & OPTS_TYPE_HOOK23))
+        {
+          // none yet
+        }
+
+        // now check if all device-memory sizes which depend on the kernel_accel_max amplifier are within its boundaries
+        // if not, decrease amplifier and try again
+
+        int skip = 0;
+
+        if (size_pws   > device_param->device_maxmem_alloc) skip = 1;
+        if (size_tmps  > device_param->device_maxmem_alloc) skip = 1;
+        if (size_hooks > device_param->device_maxmem_alloc) skip = 1;
+
+        if (( bitmap_size
+            + bitmap_size
+            + bitmap_size
+            + bitmap_size
+            + bitmap_size
+            + bitmap_size
+            + bitmap_size
+            + bitmap_size
+            + size_bfs
+            + size_combs
+            + size_digests
+            + size_esalts
+            + size_hooks
+            + size_markov_css
+            + size_plains
+            + size_pws
+            + size_results
+            + size_root_css
+            + size_rules
+            + size_rules_c
+            + size_salts
+            + size_scryptV
+            + size_shown
+            + size_tm
+            + size_tmps) > device_param->device_global_mem) skip = 1;
+
+        if (skip == 1)
+        {
+          kernel_accel_max--;
+
+          continue;
+        }
+
+        break;
+      }
+
+      /*
+      if (kernel_accel_max == 0)
+      {
+        log_error ("Device #%u: Device does not provide enough allocatable device-memory to handle hash-type %u", device_id + 1, data.hash_mode);
+
+        return -1;
+      }
+      */
+
+      device_param->kernel_accel_min = kernel_accel_min;
+      device_param->kernel_accel_max = kernel_accel_max;
+
+      /*
+      if (kernel_accel_max < kernel_accel)
+      {
+        if (quiet == 0) log_info ("Device #%u: Reduced maximum kernel-accel to %u", device_id + 1, kernel_accel_max);
+
+        device_param->kernel_accel = kernel_accel_max;
+      }
+      */
+
+      device_param->size_pws   = size_pws;
+      device_param->size_tmps  = size_tmps;
+      device_param->size_hooks = size_hooks;
+
+      // do not confuse kernel_accel_max with kernel_accel here
+
+      const u32 kernel_power = device_processors * kernel_threads * kernel_accel_max;
+
+      device_param->kernel_threads    = kernel_threads;
+      device_param->kernel_power_user = kernel_power;
+
+      kernel_power_all += kernel_power;
+
       /**
        * default building options
        */
@@ -13539,7 +13869,7 @@ int main (int argc, char **argv)
 
       // we don't have sm_* on vendors not NV but it doesn't matter
 
-      snprintf (build_opts, sizeof (build_opts) - 1, "-I%s/ -DVENDOR_ID=%d -DCUDA_ARCH=%d -DVECT_SIZE=%u -DDEVICE_TYPE=%u", shared_dir, device_param->vendor_id, (device_param->sm_major * 100) + device_param->sm_minor, device_param->vector_width, (u32) device_param->device_type);
+      snprintf (build_opts, sizeof (build_opts) - 1, "-I%s/ -DVENDOR_ID=%u -DCUDA_ARCH=%d -DVECT_SIZE=%u -DDEVICE_TYPE=%u", shared_dir, device_param->vendor_id, (device_param->sm_major * 100) + device_param->sm_minor, device_param->vector_width, (u32) device_param->device_type);
 
       /**
        * main kernel
@@ -13598,7 +13928,14 @@ int main (int argc, char **argv)
 
             device_param->program = hc_clCreateProgramWithSource (data.ocl, device_param->context, 1, (const char **) kernel_sources, NULL);
 
-            hc_clBuildProgram (data.ocl, device_param->program, 1, &device_param->device, build_opts, NULL, NULL);
+            int rc = hc_clBuildProgram (data.ocl, device_param->program, 1, &device_param->device, build_opts, NULL, NULL, false);
+
+            if (rc != 0)
+            {
+              device_param->skipped = true;
+              log_info ("Device #%u: Kernel %s build failure. Proceed without this device.", device_id + 1, source_file);
+              continue;
+            }
 
             size_t binary_size;
 
@@ -13620,7 +13957,7 @@ int main (int argc, char **argv)
 
             device_param->program = hc_clCreateProgramWithBinary (data.ocl, device_param->context, 1, &device_param->device, kernel_lengths, (const u8 **) kernel_sources, NULL);
 
-            hc_clBuildProgram (data.ocl, device_param->program, 1, &device_param->device, build_opts, NULL, NULL);
+            hc_clBuildProgram (data.ocl, device_param->program, 1, &device_param->device, build_opts, NULL, NULL, true);
           }
         }
         else
@@ -13642,7 +13979,13 @@ int main (int argc, char **argv)
             snprintf (build_opts_update, sizeof (build_opts_update) - 1, "%s -DSCRYPT_N=%d -DSCRYPT_R=%d -DSCRYPT_P=%d -DSCRYPT_TMTO=%d", build_opts, data.salts_buf[0].scrypt_N, data.salts_buf[0].scrypt_r, data.salts_buf[0].scrypt_p, 1 << data.salts_buf[0].scrypt_tmto);
           }
 
-          hc_clBuildProgram (data.ocl, device_param->program, 1, &device_param->device, build_opts_update, NULL, NULL);
+          int rc = hc_clBuildProgram (data.ocl, device_param->program, 1, &device_param->device, build_opts_update, NULL, NULL, false);
+
+          if (rc != 0)
+          {
+            device_param->skipped = true;
+            log_info ("Device #%u: Kernel %s build failure. Proceed without this device.", device_id + 1, source_file);
+          }
         }
 
         local_free (kernel_lengths);
@@ -13706,7 +14049,14 @@ int main (int argc, char **argv)
 
           device_param->program_mp = hc_clCreateProgramWithSource (data.ocl, device_param->context, 1, (const char **) kernel_sources, NULL);
 
-          hc_clBuildProgram (data.ocl, device_param->program_mp, 1, &device_param->device, build_opts, NULL, NULL);
+          int rc = hc_clBuildProgram (data.ocl, device_param->program_mp, 1, &device_param->device, build_opts, NULL, NULL, false);
+
+          if (rc != 0)
+          {
+            device_param->skipped = true;
+            log_info ("Device #%u: Kernel %s build failure. Proceed without this device.", device_id + 1, source_file);
+            continue;
+          }
 
           size_t binary_size;
 
@@ -13728,7 +14078,7 @@ int main (int argc, char **argv)
 
           device_param->program_mp = hc_clCreateProgramWithBinary (data.ocl, device_param->context, 1, &device_param->device, kernel_lengths, (const u8 **) kernel_sources, NULL);
 
-          hc_clBuildProgram (data.ocl, device_param->program_mp, 1, &device_param->device, build_opts, NULL, NULL);
+          hc_clBuildProgram (data.ocl, device_param->program_mp, 1, &device_param->device, build_opts, NULL, NULL, true);
         }
 
         local_free (kernel_lengths);
@@ -13796,7 +14146,14 @@ int main (int argc, char **argv)
 
           device_param->program_amp = hc_clCreateProgramWithSource (data.ocl, device_param->context, 1, (const char **) kernel_sources, NULL);
 
-          hc_clBuildProgram (data.ocl, device_param->program_amp, 1, &device_param->device, build_opts, NULL, NULL);
+          int rc = hc_clBuildProgram (data.ocl, device_param->program_amp, 1, &device_param->device, build_opts, NULL, NULL, false);
+
+          if (rc != 0)
+          {
+            device_param->skipped = true;
+            log_info ("Device #%u: Kernel %s build failure. Proceed without this device.", device_id + 1, source_file);
+            continue;
+          }
 
           size_t binary_size;
 
@@ -13818,7 +14175,7 @@ int main (int argc, char **argv)
 
           device_param->program_amp = hc_clCreateProgramWithBinary (data.ocl, device_param->context, 1, &device_param->device, kernel_lengths, (const u8 **) kernel_sources, NULL);
 
-          hc_clBuildProgram (data.ocl, device_param->program_amp, 1, &device_param->device, build_opts, NULL, NULL);
+          hc_clBuildProgram (data.ocl, device_param->program_amp, 1, &device_param->device, build_opts, NULL, NULL, true);
         }
 
         local_free (kernel_lengths);
@@ -13826,6 +14183,16 @@ int main (int argc, char **argv)
         local_free (kernel_sources);
       }
 
+      // some algorithm collide too fast, make that impossible
+
+      if (benchmark == 1)
+      {
+        ((uint *) digests_buf)[0] = -1;
+        ((uint *) digests_buf)[1] = -1;
+        ((uint *) digests_buf)[2] = -1;
+        ((uint *) digests_buf)[3] = -1;
+      }
+
       /**
        * global buffers
        */
@@ -14236,7 +14603,7 @@ int main (int argc, char **argv)
               uint cur_temp = 0;
               uint default_temp = 0;
 
-              int ADL_rc = hc_ADL_Overdrive6_TargetTemperatureData_Get (data.hm_dll_amd, data.hm_device[device_id].adapter_index.amd, (int *) &cur_temp, (int *) &default_temp);
+              int ADL_rc = hm_ADL_Overdrive6_TargetTemperatureData_Get (data.hm_amd, data.hm_device[device_id].adapter_index.amd, (int *) &cur_temp, (int *) &default_temp);
 
               if (ADL_rc == ADL_OK)
               {
@@ -14292,7 +14659,7 @@ int main (int argc, char **argv)
 
           int powertune_supported = 0;
 
-          if ((ADL_rc = hc_ADL_Overdrive6_PowerControl_Caps (data.hm_dll_amd, data.hm_device[device_id].adapter_index.amd, &powertune_supported)) != ADL_OK)
+          if ((ADL_rc = hm_ADL_Overdrive6_PowerControl_Caps (data.hm_amd, data.hm_device[device_id].adapter_index.amd, &powertune_supported)) != ADL_OK)
           {
             log_error ("ERROR: Failed to get ADL PowerControl Capabilities");
 
@@ -14305,9 +14672,9 @@ int main (int argc, char **argv)
 
             ADLOD6PowerControlInfo powertune = {0, 0, 0, 0, 0};
 
-            if ((ADL_rc = hc_ADL_Overdrive_PowerControlInfo_Get (data.hm_dll_amd, data.hm_device[device_id].adapter_index.amd, &powertune)) == ADL_OK)
+            if ((ADL_rc = hm_ADL_Overdrive_PowerControlInfo_Get (data.hm_amd, data.hm_device[device_id].adapter_index.amd, &powertune)) == ADL_OK)
             {
-              ADL_rc = hc_ADL_Overdrive_PowerControl_Get (data.hm_dll_amd, data.hm_device[device_id].adapter_index.amd, &od_power_control_status[device_id]);
+              ADL_rc = hm_ADL_Overdrive_PowerControl_Get (data.hm_amd, data.hm_device[device_id].adapter_index.amd, &od_power_control_status[device_id]);
             }
 
             if (ADL_rc != ADL_OK)
@@ -14317,7 +14684,7 @@ int main (int argc, char **argv)
               return (-1);
             }
 
-            if ((ADL_rc = hc_ADL_Overdrive_PowerControl_Set (data.hm_dll_amd, data.hm_device[device_id].adapter_index.amd, powertune.iMaxValue)) != ADL_OK)
+            if ((ADL_rc = hm_ADL_Overdrive_PowerControl_Set (data.hm_amd, data.hm_device[device_id].adapter_index.amd, powertune.iMaxValue)) != ADL_OK)
             {
               log_error ("ERROR: Failed to set new ADL PowerControl values");
 
@@ -14330,7 +14697,7 @@ int main (int argc, char **argv)
 
             od_clock_mem_status[device_id].state.iNumberOfPerformanceLevels = 2;
 
-            if ((ADL_rc = hc_ADL_Overdrive_StateInfo_Get (data.hm_dll_amd, data.hm_device[device_id].adapter_index.amd, ADL_OD6_GETSTATEINFO_CUSTOM_PERFORMANCE, &od_clock_mem_status[device_id])) != ADL_OK)
+            if ((ADL_rc = hm_ADL_Overdrive_StateInfo_Get (data.hm_amd, data.hm_device[device_id].adapter_index.amd, ADL_OD6_GETSTATEINFO_CUSTOM_PERFORMANCE, &od_clock_mem_status[device_id])) != ADL_OK)
             {
               log_error ("ERROR: Failed to get ADL memory and engine clock frequency");
 
@@ -14341,7 +14708,7 @@ int main (int argc, char **argv)
 
             ADLOD6Capabilities caps = {0, 0, 0, {0, 0, 0}, {0, 0, 0}, 0, 0};
 
-            if ((ADL_rc = hc_ADL_Overdrive_Capabilities_Get (data.hm_dll_amd, data.hm_device[device_id].adapter_index.amd, &caps)) != ADL_OK)
+            if ((ADL_rc = hm_ADL_Overdrive_Capabilities_Get (data.hm_amd, data.hm_device[device_id].adapter_index.amd, &caps)) != ADL_OK)
             {
               log_error ("ERROR: Failed to get ADL device capabilities");
 
@@ -14378,7 +14745,7 @@ int main (int argc, char **argv)
             performance_state->aLevels[0].iMemoryClock = memory_clock_profile_max;
             performance_state->aLevels[1].iMemoryClock = memory_clock_profile_max;
 
-            if ((ADL_rc = hc_ADL_Overdrive_State_Set (data.hm_dll_amd, data.hm_device[device_id].adapter_index.amd, ADL_OD6_SETSTATE_PERFORMANCE, performance_state)) != ADL_OK)
+            if ((ADL_rc = hm_ADL_Overdrive_State_Set (data.hm_amd, data.hm_device[device_id].adapter_index.amd, ADL_OD6_SETSTATE_PERFORMANCE, performance_state)) != ADL_OK)
             {
               log_info ("ERROR: Failed to set ADL performance state");
 
@@ -14394,7 +14761,7 @@ int main (int argc, char **argv)
       #endif // HAVE_HWMON && HAVE_ADL
     }
 
-    data.kernel_blocks_all = kernel_blocks_all;
+    data.kernel_power_all = kernel_power_all;
 
     if (data.quiet == 0) log_info ("");
 
@@ -14411,7 +14778,6 @@ int main (int argc, char **argv)
       char *hash_type = strhashtype (data.hash_mode); // not a bug
 
       log_info ("Hashtype: %s", hash_type);
-      log_info ("Workload: %u loops, %u accel", kernel_loops, kernel_accel);
       log_info ("");
     }
 
@@ -15229,16 +15595,14 @@ int main (int argc, char **argv)
 
     if (weak_hash_threshold >= salts_cnt)
     {
-      uint first_device_id = 0;
+      hc_device_param_t *device_param = NULL;
 
-      for (uint device_id = 0; device_id < devices_cnt; device_id++)
+      for (uint device_id = 0; device_id < data.devices_cnt; device_id++)
       {
-        hc_device_param_t *device_param = &data.devices_param[device_id];
+        device_param = &data.devices_param[device_id];
 
         if (device_param->skipped) continue;
 
-        first_device_id = device_id;
-
         break;
       }
 
@@ -15246,7 +15610,7 @@ int main (int argc, char **argv)
 
       for (uint salt_pos = 0; salt_pos < salts_cnt; salt_pos++)
       {
-        weak_hash_check (&data.devices_param[first_device_id], salt_pos, kernel_loops);
+        weak_hash_check (device_param, salt_pos);
       }
     }
 
@@ -15472,7 +15836,7 @@ int main (int argc, char **argv)
 
           // args
 
-          for (uint device_id = 0; device_id < devices_cnt; device_id++)
+          for (uint device_id = 0; device_id < data.devices_cnt; device_id++)
           {
             hc_device_param_t *device_param = &data.devices_param[device_id];
 
@@ -15627,8 +15991,11 @@ int main (int argc, char **argv)
           memset (device_param->speed_ms,  0, SPEED_CACHE * sizeof (float));
           memset (device_param->speed_rec, 0, SPEED_CACHE * sizeof (hc_timer_t));
 
-          device_param->kernel_power  = device_param->kernel_power_user;
-          device_param->kernel_blocks = device_param->kernel_blocks_user;
+          device_param->exec_pos = 0;
+
+          memset (device_param->exec_ms, 0, EXEC_CACHE * sizeof (double));
+
+          device_param->kernel_power = device_param->kernel_power_user;
 
           device_param->outerloop_pos  = 0;
           device_param->outerloop_left = 0;
@@ -15648,7 +16015,7 @@ int main (int argc, char **argv)
           device_param->words_done = 0;
         }
 
-        data.kernel_blocks_div = 0;
+        data.kernel_power_div = 0;
 
         // figure out some workload
 
@@ -15981,7 +16348,7 @@ int main (int argc, char **argv)
 
           data.bfs_cnt = sp_get_sum (0, css_cnt_r, root_css_buf);
 
-          for (uint device_id = 0; device_id < devices_cnt; device_id++)
+          for (uint device_id = 0; device_id < data.devices_cnt; device_id++)
           {
             hc_device_param_t *device_param = &data.devices_param[device_id];
 
@@ -16097,7 +16464,7 @@ int main (int argc, char **argv)
 
         if ((wordlist_mode == WL_MODE_FILE) || (wordlist_mode == WL_MODE_MASK))
         {
-          if (data.words_base < kernel_blocks_all)
+          if (data.words_base < kernel_power_all)
           {
             if (quiet == 0)
             {
@@ -16182,9 +16549,9 @@ int main (int argc, char **argv)
          * create cracker threads
          */
 
-        hc_thread_t *c_threads = (hc_thread_t *) mycalloc (devices_cnt, sizeof (hc_thread_t));
+        hc_thread_t *c_threads = (hc_thread_t *) mycalloc (data.devices_cnt, sizeof (hc_thread_t));
 
-        for (uint device_id = 0; device_id < devices_cnt; device_id++)
+        for (uint device_id = 0; device_id < data.devices_cnt; device_id++)
         {
           hc_device_param_t *device_param = &devices_param[device_id];
 
@@ -16200,7 +16567,7 @@ int main (int argc, char **argv)
 
         // wait for crack threads to exit
 
-        hc_thread_wait (devices_cnt, c_threads);
+        hc_thread_wait (data.devices_cnt, c_threads);
 
         local_free (c_threads);
 
@@ -16423,7 +16790,7 @@ int main (int argc, char **argv)
       if (quiet == 0) log_info ("");
     }
 
-    for (uint device_id = 0; device_id < devices_cnt; device_id++)
+    for (uint device_id = 0; device_id < data.devices_cnt; device_id++)
     {
       hc_device_param_t *device_param = &data.devices_param[device_id];
 
@@ -16528,9 +16895,9 @@ int main (int argc, char **argv)
       #endif // HAVE_ADL
     }
 
+    #ifdef HAVE_ADL
     // reset power tuning
 
-    #ifdef HAVE_ADL
     if (powertune_enable == 1) // VENDOR_ID_AMD is implied here
     {
       hc_thread_mutex_lock (mux_adl);
@@ -16547,7 +16914,7 @@ int main (int argc, char **argv)
 
           int powertune_supported = 0;
 
-          if ((hc_ADL_Overdrive6_PowerControl_Caps (data.hm_dll_amd, data.hm_device[device_id].adapter_index.amd, &powertune_supported)) != ADL_OK)
+          if ((hm_ADL_Overdrive6_PowerControl_Caps (data.hm_amd, data.hm_device[device_id].adapter_index.amd, &powertune_supported)) != ADL_OK)
           {
             log_error ("ERROR: Failed to get ADL PowerControl Capabilities");
 
@@ -16558,7 +16925,7 @@ int main (int argc, char **argv)
           {
             // powercontrol settings
 
-            if ((hc_ADL_Overdrive_PowerControl_Set (data.hm_dll_amd, data.hm_device[device_id].adapter_index.amd, od_power_control_status[device_id])) != ADL_OK)
+            if ((hm_ADL_Overdrive_PowerControl_Set (data.hm_amd, data.hm_device[device_id].adapter_index.amd, od_power_control_status[device_id])) != ADL_OK)
             {
               log_info ("ERROR: Failed to restore the ADL PowerControl values");
 
@@ -16576,7 +16943,7 @@ int main (int argc, char **argv)
             performance_state->aLevels[0].iMemoryClock = od_clock_mem_status[device_id].state.aLevels[0].iMemoryClock;
             performance_state->aLevels[1].iMemoryClock = od_clock_mem_status[device_id].state.aLevels[1].iMemoryClock;
 
-            if ((hc_ADL_Overdrive_State_Set (data.hm_dll_amd, data.hm_device[device_id].adapter_index.amd, ADL_OD6_SETSTATE_PERFORMANCE, performance_state)) != ADL_OK)
+            if ((hm_ADL_Overdrive_State_Set (data.hm_amd, data.hm_device[device_id].adapter_index.amd, ADL_OD6_SETSTATE_PERFORMANCE, performance_state)) != ADL_OK)
             {
               log_info ("ERROR: Failed to restore ADL performance state");
 
@@ -16594,25 +16961,34 @@ int main (int argc, char **argv)
 
     if (gpu_temp_disable == 0)
     {
-      #if defined(LINUX) && defined(HAVE_NVML)
-      if (data.hm_dll_nv)
+      #if defined(HAVE_NVML) || defined(HAVE_NVAPI)
+      if (data.hm_nv)
       {
-        hc_NVML_nvmlShutdown (data.hm_dll_nv);
+        #if defined(LINUX) && defined(HAVE_NVML)
 
-        hm_close (data.hm_dll_nv);
-      }
-      #endif
+        hm_NVML_nvmlShutdown (data.hm_nv);
+
+        nvml_close (data.hm_nv);
 
-      #if defined(WIN) && (HAVE_NVAPI)
-      NvAPI_Unload ();
+        #elif defined(WIN) && (HAVE_NVAPI)
+
+        hm_NvAPI_Unload (data.hm_nv);
+
+        nvapi_close (data.hm_nv);
+
+        #endif
+
+        data.hm_nv = NULL;
+      }
       #endif
 
       #ifdef HAVE_ADL
-      if (data.hm_dll_amd)
+      if (data.hm_amd)
       {
-        hc_ADL_Main_Control_Destroy (data.hm_dll_amd);
+        hm_ADL_Main_Control_Destroy (data.hm_amd);
 
-        hm_close (data.hm_dll_amd);
+        adl_close (data.hm_amd);
+        data.hm_amd = NULL;
       }
       #endif
     }
@@ -16702,6 +17078,10 @@ int main (int argc, char **argv)
 
   local_free (rd);
 
+  // tuning db
+
+  tuning_db_destroy (tuning_db);
+
   // loopback
 
   local_free (loopback_file);