LINUX.ORG.RU

История изменений

Исправление i-rinat, (текущая версия) :

Набросал с хешами, получилось в 10 раз быстрее. Не параллелится, правда.

#include <omp.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#define ARRAY_SIZE UINT64_C(100000)
#define HAYSTACK_LENGTH UINT64_C(100)
#define NEEDLE_LENGTH UINT64_C(20)
#define NUM_SEARCHES UINT64_C(1000)
#define BLOOM_ORDER UINT64_C(20)
#define BLOOM_SZ (UINT64_C(1) << BLOOM_ORDER)
#define BLOOM_HASHCOUNT 2
#define BLOOM_MAGIC 0x9747b28c
#define NUM_THREADS 1

typedef uint8_t u8;
typedef uint32_t u32;

static u32 mm2(const void *key, u32 len, const u32 seed) {
  const u32 *data = key;
  return *data ^ seed;
}

static void bloom_add(u32 *bloom, u8 *buf, u32 len) {
  u32 a = mm2(buf, len, BLOOM_MAGIC);
  u32 b = mm2(buf, len, a);

  for (u32 k = 0; k < BLOOM_HASHCOUNT; k++) {
    u32 x = (a + b * k) % BLOOM_SZ;
    bloom[x / 32] |= 1 << (x % 32);
  }
}

static u32 bloom_has(u32 *bloom, u8 *buf, u32 len) {
  u32 a = mm2(buf, len, BLOOM_MAGIC);
  u32 b = mm2(buf, len, a);
  u32 hits = 0;

  for (u32 k = 0; k < BLOOM_HASHCOUNT; k++) {
    u32 x = (a + b * k) % BLOOM_SZ;
    hits = !!(bloom[x / 32] & (1 << (x % 32)));
  }

  return hits == BLOOM_HASHCOUNT;
}

int main(void) {

  static const u8 xlat[] = //
      "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghij"
      "klmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrst"
      "uvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCD"
      "EFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefgh";

  for (size_t try = 0; try < 10; try ++) {
    struct timespec ts;
    clock_gettime(CLOCK_MONOTONIC, &ts);
    u8 **array = malloc(sizeof(u8 *) * ARRAY_SIZE);
    u32 *bloom = calloc(1, BLOOM_SZ / 8);
    u32 seed = time(NULL);

    for (size_t k = 0; k < ARRAY_SIZE; k++) {
      u8 *hs = malloc(HAYSTACK_LENGTH + 1);
      for (size_t j = 0; j < HAYSTACK_LENGTH; j += sizeof(u32)) {
        const u32 r = rand_r(&seed);
        *(u32 *)(hs + j) = xlat[(r >> 24)] << 24 |
                           xlat[(r >> 16) & 0xffu] << 16 |
                           xlat[(r >> 8) & 0xffu] << 8 | xlat[r & 0xffu];
      }
      for (size_t j = 0; j < HAYSTACK_LENGTH - NEEDLE_LENGTH; j++)
        bloom_add(bloom, hs + j, NEEDLE_LENGTH);
      hs[HAYSTACK_LENGTH] = 0;
      array[k] = hs;
    }

    char needle[NEEDLE_LENGTH + 1];
    for (size_t k = 0; k < NEEDLE_LENGTH; k++)
      needle[k] = xlat[k & 0xffu];
    needle[NEEDLE_LENGTH] = 0;

    size_t count = 0;
    for (size_t iter = 0; iter < NUM_SEARCHES; iter++) {
      if (bloom_has(bloom, needle, NEEDLE_LENGTH))
        for (size_t k = 0; k < ARRAY_SIZE; k++)
          count += !!strstr(array[k], needle);
    }
    for (size_t k = 0; k < ARRAY_SIZE; k++)
      free(array[k]);
    free(array);
    free(bloom);

    struct timespec te;
    clock_gettime(CLOCK_MONOTONIC, &te);
    printf("count = %zu; %.3f secs\n", count,
           (te.tv_sec - ts.tv_sec) + 1e-9 * (te.tv_nsec - ts.tv_nsec));
  }
}
count = 0; 0.047 secs
count = 0; 0.047 secs
count = 0; 0.046 secs
count = 0; 0.048 secs
count = 0; 0.046 secs
count = 0; 0.047 secs
count = 0; 0.046 secs
count = 0; 0.047 secs
count = 0; 0.047 secs
count = 0; 0.047 secs

Вариант с простым поиском, выполняющийся в восемь потоков, тратил примерно 450-530 мс на итерацию.

Я думал, что смысл теста был в том, чтобы не убегать от вычислений.

Исходная версия i-rinat, :

Набросал с хешами, получилось в 10 раз быстрее. Не параллелится, правда.

#include <omp.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#define ARRAY_SIZE UINT64_C(100000)
#define HAYSTACK_LENGTH UINT64_C(100)
#define NEEDLE_LENGTH UINT64_C(20)
#define NUM_SEARCHES UINT64_C(1000)
#define BLOOM_ORDER UINT64_C(20)
#define BLOOM_SZ (UINT64_C(1) << BLOOM_ORDER)
#define BLOOM_HASHCOUNT 2
#define BLOOM_MAGIC 0x9747b28c
#define NUM_THREADS 1

typedef uint8_t u8;
typedef uint32_t u32;

static u32 mm2(const void *key, u32 len, const u32 seed) {
  const u32 *data = key;
  return *data ^ seed;
}

static void bloom_add(u32 *bloom, u8 *buf, u32 len) {
  u32 a = mm2(buf, len, BLOOM_MAGIC);
  u32 b = mm2(buf, len, a);

  for (u32 k = 0; k < BLOOM_HASHCOUNT; k++) {
    u32 x = (a + b * k) % BLOOM_SZ;
    bloom[x / 32] |= 1 << (x % 32);
  }
}

static u32 bloom_has(u32 *bloom, u8 *buf, u32 len) {
  u32 a = mm2(buf, len, BLOOM_MAGIC);
  u32 b = mm2(buf, len, a);
  u32 hits = 0;

  for (u32 k = 0; k < BLOOM_HASHCOUNT; k++) {
    u32 x = (a + b * k) % BLOOM_SZ;
    hits = !!(bloom[x / 32] & (1 << (x % 32)));
  }

  return hits == BLOOM_HASHCOUNT;
}

int main(void) {

  static const u8 xlat[] = //
      "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghij"
      "klmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrst"
      "uvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCD"
      "EFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefgh";

  for (size_t try = 0; try < 10; try ++) {
    struct timespec ts;
    clock_gettime(CLOCK_MONOTONIC, &ts);
    u8 **array = malloc(sizeof(u8 *) * ARRAY_SIZE);
    u32 *bloom = calloc(1, BLOOM_SZ / 8);
    u32 seed = time(NULL);

    for (size_t k = 0; k < ARRAY_SIZE; k++) {
      u8 *hs = malloc(HAYSTACK_LENGTH + 1);
      for (size_t j = 0; j < HAYSTACK_LENGTH; j += sizeof(u32)) {
        const u32 r = rand_r(&seed);
        *(u32 *)(hs + j) = xlat[(r >> 24)] << 24 |
                           xlat[(r >> 16) & 0xffu] << 16 |
                           xlat[(r >> 8) & 0xffu] << 8 | xlat[r & 0xffu];
      }
      for (size_t j = 0; j < HAYSTACK_LENGTH - NEEDLE_LENGTH; j++)
        bloom_add(bloom, hs + j, NEEDLE_LENGTH);
      hs[HAYSTACK_LENGTH] = 0;
      array[k] = hs;
    }

    char needle[NEEDLE_LENGTH + 1];
    for (size_t k = 0; k < NEEDLE_LENGTH; k++)
      needle[k] = xlat[k & 0xffu];
    needle[NEEDLE_LENGTH] = 0;

    size_t count = 0;
    for (size_t iter = 0; iter < NUM_SEARCHES; iter++) {
      if (bloom_has(bloom, needle, NEEDLE_LENGTH))
        for (size_t k = 0; k < ARRAY_SIZE; k++)
          count += !!strstr(array[k], needle);
    }
    for (size_t k = 0; k < ARRAY_SIZE; k++)
      free(array[k]);
    free(array);
    free(bloom);

    struct timespec te;
    clock_gettime(CLOCK_MONOTONIC, &te);
    printf("count = %zu; %.3f secs\n", count,
           (te.tv_sec - ts.tv_sec) + 1e-9 * (te.tv_nsec - ts.tv_nsec));
  }
}
count = 0; 0.047 secs
count = 0; 0.047 secs
count = 0; 0.046 secs
count = 0; 0.048 secs
count = 0; 0.046 secs
count = 0; 0.047 secs
count = 0; 0.046 secs
count = 0; 0.047 secs
count = 0; 0.047 secs
count = 0; 0.047 secs

Вариант с простым поиском, выполняющийся в восемь потоков тратил примерно 450-530 мс на итерацию.

Я думал, что смысл теста был в том, чтобы не убегать от вычислений.