LINUX.ORG.RU

Задачка на самый быстрый подсчет встречаемости слов

 


2

2

Привет,

По мотивам темы: Различия между macOS и GNU/Linux

Есть файлик. Вот он: https://disk.yandex.ru/d/XaavsEkOvCT4HQ

Нужно пройтись по файлику и посчитать встречаемость каждого слова в тексте. Словом считается любая последовательность букв от a до z. Регистр нужно привести к одному. Любой другой символ прерывает слово.

Результат записать в другой файл в формате: <количество> <слово>. Например, текст: «cat, cat, cat». Ответ будет такой: «3 cat». Также, слова при выводе нужно отсортировать по их встречаемости.

Например первые несколько строк вывода из приведенного выше файла будут такими:

3343241 the
1852717 and
1715705 of
1560152 to
1324244 a

Дополнительное условие, нужно, чтобы ваша программа отрабатывала быстрее, чем за 7 секунд на Core i5-4690 @ 3.7 GHz.

Понятно, что железяки у всех разные, поэтому если участвуете, пишите на каком оборудовании вы запускали свою программку.

Вроде как BceM_IIpuBeT хотел поучаствовать. Может еще кто-то присоединится.

Я свою штуку написал. Отрабатывает примерно за 5 секунд на Мак-мини 2012-го года, core i7 @ 2,3 GHz.

★★★★★

Последнее исправление: hibou (всего исправлений: 1)
Ответ на: комментарий от sislochka

Ну, все возможные алгоритмы тут уже разобрали и задача теперь сводится только к оптимизации под конкретную платформу с сужением условий до минимума, работоспособного на конкретном наборе данных.

anonymous
()
Ответ на: комментарий от anonymous

Вот на рабочем ноутбуке, тут чуть пошустрей получилось.

$ time vel/vel huge.txt > vel/result.txt

real	0m1.329s
user	0m1.287s
sys	0m0.041s
$ time vel/vel huge.txt > vel/result.txt

real	0m1.342s
user	0m1.291s
sys	0m0.049s
$ time vel/vel huge.txt > vel/result.txt

real	0m1.355s
user	0m1.306s
sys	0m0.047s
$ time java -cp java1 test.WordsCounter
Part 1: 2932748790
Part 2: 50273073
Part 3: 60734710
Total: 3043756573

real	0m3.096s
user	0m3.763s
sys	0m0.144s
$ time java -cp java1 test.WordsCounter
Part 1: 2928547603
Part 2: 66246698
Part 3: 55755136
Total: 3050549437

real	0m3.103s
user	0m3.769s
sys	0m0.126s
$ time java -cp java1 test.WordsCounter
Part 1: 2942000355
Part 2: 50381341
Part 3: 52098460
Total: 3044480156

real	0m3.104s
user	0m3.782s
sys	0m0.130s
$ time java -cp java2 test.WordsCounter
start
Total: 1171030442

real	0m1.261s
user	0m7.586s
sys	0m0.356s
$ time java -cp java2 test.WordsCounter
start
Total: 1182168293

real	0m1.278s
user	0m7.602s
sys	0m0.333s
$ time java -cp java2 test.WordsCounter
start
Total: 1525662912

real	0m1.625s
user	0m9.237s
sys	0m0.367s
Legioner ★★★★★
()
Ответ на: комментарий от Legioner

Кстати вариант vel-а выдаёт неправильный ответ, по крайней мере мой ответ совпадает с ответом топикстартера, где-то там у него баг. Компилировал с -O3, может если флажки потщательней выбрать, будет быстрей. Мой вариант 2 глючит на 8 ядрах, если вдруг кто будет пробовать, надо заменить 31 строчку на int end = limit / nThreads * i;. На моём рабочем ноутбуке i7-1165G7 с 4 ядрами и гиперпотоками.

Legioner ★★★★★
()
Последнее исправление: Legioner (всего исправлений: 4)
Ответ на: комментарий от Legioner

Кстати вариант vel-а выдаёт неправильный ответ

В исходном файле есть косяки и результат может зависеть от того, как ты интерпретируешь юникод. Когда я проверял, у меня совпадало, вроде бы.

anonymous
()
Ответ на: комментарий от vel

Голый C с использованием qsort(), чтение файла целиком через mmap.

Малось оптимизировал по памяти и убрал микроплюху. Память теперь под слова выделяется только при выводе этих 6 строк, без дурацкого buf[1024], сортируются указатели на node, а плюха типичная для mmap-а: если в конце файла не будет типа '\n' (ну то есть последний символ [a-z] последнего слова), то всё последнее слово проигнорируется, в PROT_READ '\n' ведь не допишешь. Разница в скорости, конечно, копеечная, но есть.

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <stdarg.h>
#include <string.h>

#define A_Z 26

#define SHOW_CNT 7

typedef struct node {
	struct node *next[A_Z];
	struct node *prev;
	int count;
} node_t;

static node_t root;

static unsigned char tbl_a_z[256] = {
['A'] =  1, ['B'] =  2, ['C'] =  3, ['D'] =  4, ['E'] =  5, ['F'] = 6,
['G'] =  7, ['H'] =  8, ['I'] =  9, ['J'] = 10, ['K'] = 11, ['L'] = 12,
['M'] = 13, ['N'] = 14, ['O'] = 15, ['P'] = 16, ['Q'] = 17, ['R'] = 18,
['S'] = 19, ['T'] = 20, ['U'] = 21, ['V'] = 22, ['W'] = 23, ['X'] = 24,
['Y'] = 25, ['Z'] = 26,
['a'] =  1, ['b'] =  2, ['c'] =  3, ['d'] =  4, ['e'] =  5, ['f'] = 6,
['g'] =  7, ['h'] =  8, ['i'] =  9, ['j'] = 10, ['k'] = 11, ['l'] = 12,
['m'] = 13, ['n'] = 14, ['o'] = 15, ['p'] = 16, ['q'] = 17, ['r'] = 18,
['s'] = 19, ['t'] = 20, ['u'] = 21, ['v'] = 22, ['w'] = 23, ['x'] = 24,
['y'] = 25, ['z'] = 26 };

static void __attribute__ ((noreturn, format (printf, 1, 2)))
error_d(const char *s, ...)
{
	va_list p;

	va_start(p, s);
	vfprintf(stderr, s, p);
	va_end(p);
	putc('\n', stderr);
	exit(1);
}

static void *xzalloc(size_t sz)
{
	void *p = calloc(1, sz);

	if (p == NULL)
		error_d("memory exhausted");
	return p;
}

static node_t **unfold_tree(node_t **m, node_t *start)
{
	node_t **n;

	for (n = start->next; n < start->next + A_Z; n++) {
		if (*n != NULL)
			m = unfold_tree(m, *n);
	}
	if (start->count != 0)
		*m++ = start;
	return m;
}

static int cmp_cnt(const void *a, const void *b) {
    return (*(node_t **)b)->count - (*(node_t **)a)->count;
}

static char *unfold_word(node_t *n)
{
	char *s, *e;
	int c;
	size_t wl = 0;
	node_t *wn;

	for (wn = n; wn != &root; wn = wn->prev)
		wl++;
	s = xzalloc(wl + 1);
	e = s + wl;
	for (; n != &root; n = wn) {
		wn = n->prev;
		for(c = 0; c < A_Z; c++) {
			if (wn->next[c] == n)
				break;
		}
		*--e = c + 'a';
	}
	return s;
}

int main(int argc, char **argv)
{
  struct stat st;
  off_t fsize;
  int fd;
  char *map, *mptr, *mend;
  unsigned long count = 0;
  size_t mapsize, cw;
  node_t **word_pn;

  if(argc != 2)
	error_d("Usage %s file.txt", argv[0]);

  fd = open(argv[1], O_RDONLY);
  if(fd < 0)
	error_d("open(%s): %m", argv[1]);
  if(fstat(fd, &st))
	error_d("stat(%s): %m", argv[1]);
  fsize = st.st_size;

  mapsize = getpagesize() - 1;
  mapsize = (fsize+mapsize) & ~mapsize;
  map = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, fd, 0);
  if (map == MAP_FAILED)
	error_d("mmap(%s): %m", argv[1]);
  mptr = map;
  mend = mptr + fsize;

  for (;;) {
      node_t *n = &root;

      while (mptr < mend) {
	  int c = tbl_a_z[(unsigned char)*mptr++];
	  if (c) {
		  node_t *m = n->next[--c];
		  if (m == NULL) {
			  n->next[c] = m = xzalloc(sizeof(node_t));
			  m->prev = n;
		  }
		  n = m;
	  } else if (n != &root)
		break;
      }

      if (n == &root)
	break;
      /* may be have not last [^a-zA-Z] */
      if (!n->count)
	count++;        /* uniq word */
      n->count++;       /* count by a word usage */
  }

  munmap(map, mapsize);
  close(fd);

  word_pn = xzalloc(count * sizeof(node_t *));
  unfold_tree (word_pn, &root);
  qsort(word_pn, count, sizeof(node_t *), cmp_cnt);

  for(cw = 0; cw < SHOW_CNT && cw != count; cw++) {
	mptr = unfold_word(word_pn[cw]);
	printf("%7d %s\n", word_pn[cw]->count, mptr);
	// free(mptr);
  }
  return 0;
}

vodz ★★★★★
()
Ответ на: комментарий от Legioner

И для полноты на макбуке.

% time java -cp out/production/java-17-idea-test test.WordsCounter
start
Total: 698561667
4.86s user 0.29s system 686% cpu 0.751 total
Legioner ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.