| /* Measure strcoll execution time in different locales. |
| Copyright (C) 2015-2018 Free Software Foundation, Inc. |
| This file is part of the GNU C Library. |
| |
| The GNU C Library is free software; you can redistribute it and/or |
| modify it under the terms of the GNU Lesser General Public |
| License as published by the Free Software Foundation; either |
| version 2.1 of the License, or (at your option) any later version. |
| |
| The GNU C Library is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| Lesser General Public License for more details. |
| |
| You should have received a copy of the GNU Lesser General Public |
| License along with the GNU C Library; if not, see |
| <http://www.gnu.org/licenses/>. */ |
| |
| #include <stdio.h> |
| #include <fcntl.h> |
| #include <assert.h> |
| #include <stdlib.h> |
| #include <locale.h> |
| #include <unistd.h> |
| #include "json-lib.h" |
| #include "bench-timing.h" |
| #include <string.h> |
| |
| /* Many thanks to http://generator.lorem-ipsum.info/ */ |
| #define INPUT_PREFIX "strcoll-inputs/" |
| |
| static const char *const input_files[] = { |
| "filelist#C", |
| "filelist#en_US.UTF-8", |
| "lorem_ipsum#vi_VN.UTF-8", |
| "lorem_ipsum#ar_SA.UTF-8", |
| "lorem_ipsum#en_US.UTF-8", |
| "lorem_ipsum#zh_CN.UTF-8", |
| "lorem_ipsum#cs_CZ.UTF-8", |
| "lorem_ipsum#en_GB.UTF-8", |
| "lorem_ipsum#da_DK.UTF-8", |
| "lorem_ipsum#pl_PL.UTF-8", |
| "lorem_ipsum#fr_FR.UTF-8", |
| "lorem_ipsum#pt_PT.UTF-8", |
| "lorem_ipsum#el_GR.UTF-8", |
| "lorem_ipsum#ru_RU.UTF-8", |
| "lorem_ipsum#he_IL.UTF-8", |
| "lorem_ipsum#es_ES.UTF-8", |
| "lorem_ipsum#hi_IN.UTF-8", |
| "lorem_ipsum#sv_SE.UTF-8", |
| "lorem_ipsum#hu_HU.UTF-8", |
| "lorem_ipsum#tr_TR.UTF-8", |
| "lorem_ipsum#is_IS.UTF-8", |
| "lorem_ipsum#it_IT.UTF-8", |
| "lorem_ipsum#sr_RS.UTF-8", |
| "lorem_ipsum#ja_JP.UTF-8" |
| }; |
| |
| #define TEXTFILE_DELIMITER " \n\r\t.,?!" |
| |
| static char * |
| read_file (const char *filename) |
| { |
| struct stat stats; |
| char *buffer = NULL; |
| int fd = open (filename, O_CLOEXEC); |
| |
| if (fd >= 0) |
| { |
| if (fstat (fd, &stats) == 0) |
| { |
| buffer = malloc (stats.st_size + 1); |
| if (buffer) |
| { |
| if (read (fd, buffer, stats.st_size) == stats.st_size) |
| buffer[stats.st_size] = '\0'; |
| else |
| { |
| free (buffer); |
| buffer = NULL; |
| } |
| } |
| } |
| close (fd); |
| } |
| |
| return buffer; |
| } |
| |
| static size_t |
| count_words (const char *text, const char *delim) |
| { |
| size_t wordcount = 0; |
| char *tmp = strdup (text); |
| |
| char *token = strtok (tmp, delim); |
| while (token != NULL) |
| { |
| if (*token != '\0') |
| wordcount++; |
| token = strtok (NULL, delim); |
| } |
| |
| free (tmp); |
| return wordcount; |
| } |
| |
| typedef struct |
| { |
| size_t size; |
| char **words; |
| } word_list; |
| |
| static word_list * |
| new_word_list (size_t size) |
| { |
| word_list *list = malloc (sizeof (word_list)); |
| assert (list != NULL); |
| list->size = size; |
| list->words = malloc (size * sizeof (char *)); |
| assert (list->words != NULL); |
| return list; |
| } |
| |
| static word_list * |
| str_word_list (const char *str, const char *delim) |
| { |
| size_t n = 0; |
| word_list *list = new_word_list (count_words (str, delim)); |
| |
| char *toks = strdup (str); |
| char *word = strtok (toks, delim); |
| while (word != NULL && n < list->size) |
| { |
| if (*word != '\0') |
| list->words[n++] = strdup (word); |
| word = strtok (NULL, delim); |
| } |
| |
| free (toks); |
| return list; |
| } |
| |
| static word_list * |
| copy_word_list (const word_list *list) |
| { |
| size_t i; |
| word_list *copy = new_word_list (list->size); |
| |
| for (i = 0; i < list->size; i++) |
| copy->words[i] = strdup (list->words[i]); |
| |
| return copy; |
| } |
| |
| static void |
| free_word_list (word_list *list) |
| { |
| size_t i; |
| for (i = 0; i < list->size; i++) |
| free (list->words[i]); |
| |
| free (list->words); |
| free (list); |
| } |
| |
| static int |
| compare_words (const void *a, const void *b) |
| { |
| const char *s1 = *(char **) a; |
| const char *s2 = *(char **) b; |
| return strcoll (s1, s2); |
| } |
| |
| #undef INNER_LOOP_ITERS |
| #define INNER_LOOP_ITERS 16 |
| |
| static void |
| bench_list (json_ctx_t *json_ctx, word_list *list) |
| { |
| size_t i; |
| timing_t start, stop, cur; |
| |
| word_list **tests = malloc (INNER_LOOP_ITERS * sizeof (word_list *)); |
| assert (tests != NULL); |
| for (i = 0; i < INNER_LOOP_ITERS; i++) |
| tests[i] = copy_word_list (list); |
| |
| TIMING_NOW (start); |
| for (i = 0; i < INNER_LOOP_ITERS; i++) |
| qsort (tests[i]->words, tests[i]->size, sizeof (char *), compare_words); |
| TIMING_NOW (stop); |
| |
| TIMING_DIFF (cur, start, stop); |
| setlocale (LC_ALL, "en_US.UTF-8"); |
| json_attr_double (json_ctx, "duration", cur); |
| json_attr_double (json_ctx, "iterations", i); |
| json_attr_double (json_ctx, "mean", (double) cur / i); |
| |
| for (i = 0; i < INNER_LOOP_ITERS; i++) |
| free_word_list (tests[i]); |
| free (tests); |
| } |
| |
| typedef enum |
| { |
| OK, |
| ERROR_FILENAME, |
| ERROR_LOCALE, |
| ERROR_IO |
| } result_t; |
| |
| static result_t |
| bench_file (json_ctx_t *json_ctx, const char *testname, const char *filename, |
| const char *locale) |
| { |
| if (setlocale (LC_ALL, locale) == NULL) |
| return ERROR_LOCALE; |
| |
| char *text = read_file (filename); |
| if (text == NULL) |
| return ERROR_IO; |
| |
| word_list *list = str_word_list (text, TEXTFILE_DELIMITER); |
| |
| json_attr_object_begin (json_ctx, testname); |
| bench_list (json_ctx, list); |
| json_attr_object_end (json_ctx); |
| |
| free_word_list (list); |
| free (text); |
| return OK; |
| } |
| |
| int |
| main (void) |
| { |
| json_ctx_t *json_ctx = malloc (sizeof (json_ctx_t)); |
| assert (json_ctx != NULL); |
| json_init (json_ctx, 2, stdout); |
| json_attr_object_begin (json_ctx, "strcoll"); |
| |
| size_t i; |
| result_t result = OK; |
| for (i = 0; i < (sizeof (input_files) / sizeof (input_files[0])); i++) |
| { |
| char *locale = strchr (input_files[i], '#'); |
| if (locale == NULL) |
| { |
| printf ("Failed to get locale from filename %s, aborting!\n", |
| input_files[i]); |
| return ERROR_FILENAME; |
| } |
| |
| char *filename; |
| asprintf (&filename, INPUT_PREFIX "%s", input_files[i]); |
| result = bench_file (json_ctx, input_files[i], filename, locale + 1); |
| |
| if (result != OK) |
| { |
| if (result == ERROR_LOCALE) |
| printf ("Failed to set locale %s, aborting!\n", locale); |
| else if (result == ERROR_IO) |
| printf ("Failed to read file %s, aborting!\n", filename); |
| free (filename); |
| goto out; |
| } |
| free (filename); |
| } |
| |
| out: |
| json_attr_object_end (json_ctx); |
| free (json_ctx); |
| return result; |
| } |