{"id":17317367,"url":"https://github.com/nmoinvaz/strcasecmp","last_synced_at":"2026-04-25T11:33:04.405Z","repository":{"id":149452379,"uuid":"557400907","full_name":"nmoinvaz/strcasecmp","owner":"nmoinvaz","description":"strcasecmp benchmarks","archived":false,"fork":false,"pushed_at":"2022-10-27T18:33:54.000Z","size":38,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-06-11T22:49:35.957Z","etag":null,"topics":["c","comparison","string"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nmoinvaz.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2022-10-25T16:10:05.000Z","updated_at":"2023-05-05T23:26:08.000Z","dependencies_parsed_at":null,"dependency_job_id":"7a916a4b-4b6a-47eb-a299-5ec97baa15a2","html_url":"https://github.com/nmoinvaz/strcasecmp","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/nmoinvaz/strcasecmp","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nmoinvaz%2Fstrcasecmp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nmoinvaz%2Fstrcasecmp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nmoinvaz%2Fstrcasecmp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nmoinvaz%2Fstrcasecmp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nmoinvaz","download_url":"https://codeload.github.com/nmoinvaz/strcasecmp/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nmoinvaz%2Fstrcasecmp/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32261110,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-25T09:15:33.318Z","status":"ssl_error","status_checked_at":"2026-04-25T09:15:31.997Z","response_time":59,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["c","comparison","string"],"created_at":"2024-10-15T13:16:24.122Z","updated_at":"2026-04-25T11:33:04.399Z","avatar_url":"https://github.com/nmoinvaz.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# strcasecmp\n\nBenchmark for `strcasecmp` to determine whether or not there is a better implementation.\n\nMost implementations will convert each character to `tolower` without checking to see if the characters are first the same. If the characters are the same then no lowercase conversion is needed.\n\nThis code uses Google Benchmark to perform tests against several 256 character strings of varying similarity.\n\n## Popular implementations\n\n**glibc**\n```c\nint\n__strcasecmp (const char *s1, const char *s2 LOCALE_PARAM)\n{\n    const unsigned char *p1 = (const unsigned char *) s1;\n    const unsigned char *p2 = (const unsigned char *) s2;\n    int result;\n\n    if (p1 == p2)\n        return 0;\n\n    while ((result = TOLOWER (*p1) - TOLOWER (*p2++)) == 0)\n        if (*p1++ == '\\0')\n        break;\n\n    return result;\n}\n```\nhttps://github.com/bminor/glibc/blob/b92a49359f33a461db080a33940d73f47c756126/string/strcasecmp.c\n\nThe check for `p1 == p2` can add an additional benefit for user-mode applications that are compiled with string pooling enabled.\n\n**Apple**\n\n```c\nint\nstrcasecmp(s1, s2)\n\tconst char *s1, *s2;\n{\n    const u_char\n            *us1 = (const u_char *)s1,\n            *us2 = (const u_char *)s2;\n\n    while (tolower(*us1) == tolower(*us2++))\n        if (*us1++ == '\\0')\n            return (0);\n    return (tolower(*us1) - tolower(*--us2));\n}\n\nhttps://opensource.apple.com/source/Libc/Libc-583/string/FreeBSD/strcasecmp.c.auto.html\n\n```\n\n**OpenSSL**\n\n```c\nint OPENSSL_strncasecmp(const char *s1, const char *s2, size_t n)\n{\n    int t;\n    size_t i;\n\n    for (i = 0; i \u003c n; i++)\n        if ((t = ossl_tolower(*s1) - ossl_tolower(*s2++)) != 0)\n            return t;\n        else if (*s1++ == '\\0')\n            return 0;\n    return 0;\n}\n```\n\nhttps://github.com/openssl/openssl/blob/79c8dcf3985a7b75eac8e53eb8652728af6c5d3d/crypto/o_str.c\n\n**cURL**\n\n```c\nint Curl_strcasecompare(const char *first, const char *second)\n{\n    while(*first \u0026\u0026 *second) {\n        if(Curl_raw_toupper(*first) != Curl_raw_toupper(*second))\n            /* get out of the loop as soon as they don't match */\n            return 0;\n        first++;\n        second++;\n    }\n    /* If we're here either the strings are the same or the length is different.\n       We can just test if the \"current\" character is non-zero for one and zero\n       for the other. Note that the characters may not be exactly the same even\n       if they match, we only want to compare zero-ness. */\n    return !*first == !*second;\n}\n```\n\nhttps://github.com/curl/curl/blob/master/lib/strcase.c\n\n**Linux Kernel**\n\n```c\nint strcasecmp(const char *s1, const char *s2)\n{\n    int c1, c2;\n\n    do {\n        c1 = tolower(*s1++);\n        c2 = tolower(*s2++);\n    } while (c1 == c2 \u0026\u0026 c1 != 0);\n    return c1 - c2;\n}\n```\n\nhttps://github.com/torvalds/linux/blob/master/lib/string.c\n\nInterestingly enough the kernel's implementations for `strcasecmp` and `strncasecmp` will return different negative values for `string1 = \"\", string2 = \"A\"` because the quick return in `strncasecmp` when `!c1 || !c2`. In this case `strcasecmp = -'a'` and `strncasecmp = '-A'`. But perhaps it is left up to the implementation what negative value to return.\n\n## Our implementation\n\nPartially derived from Linux kernel implementations for `strcasecmp` and `strncasecmp`.\n```c\nint strcasecmp_new(const char *s1, const char *s2)\n{\n    unsigned char c1, c2;\n\n    do {\n        c1 = *s1++;\n        c2 = *s2++;\n        if (c1 != c2) {\n            c1 = tolower(c1);\n            c2 = tolower(c2);\n            if (c1 != c2)\n                return (int)c1 - (int)c2;\n        }\n    } while (c1 != 0);\n    return 0;\n}\n```\n\n## Build\n\nCMake instructions:\n```\ncmake -S . -B build -D CMAKE_BUILD_TYPE=Release\ncmake --build build --config Release\n```\n## Running\n\nThe application allows most Google Benchmark arguments including `--benchmark_repetitions=#` which can specify the number of repetitions to perform.\n\n## Results\n\n```\n2022-10-25T08:48:13-07:00\nRunning 20 repetitions\nRun on (12 X 3202.02 MHz CPU s)\nCPU Caches:\n  L1 Data 32 KiB (x6)\n  L1 Instruction 32 KiB (x6)\n  L2 Unified 256 KiB (x6)\n  L3 Unified 12288 KiB (x1)\n```\n\ntwo strings 100% same case = ~48% improvement\n```\nstrcasecmp_bench/100% same:0_mean              300 ns          164 ns           20\nstrcasecmp_bench/100% same:0_median            291 ns          229 ns           20\nstrcasecmp_bench/100% same:0_stddev           23.5 ns          120 ns           20\nstrcasecmp_bench/100% same:0_cv               7.84 %         73.54 %            20\n\nstrcasecmp_new_bench/100% same:0_mean          158 ns         89.8 ns           20\nstrcasecmp_new_bench/100% same:0_median        159 ns          102 ns           20\nstrcasecmp_new_bench/100% same:0_stddev       2.66 ns         53.5 ns           20\nstrcasecmp_new_bench/100% same:0_cv           1.69 %         59.58 %            20\n```\ntwo strings 83% same case = ~32% improvement\n```\nstrcasecmp_bench/83% same:1_mean               304 ns          173 ns           20\nstrcasecmp_bench/83% same:1_median             304 ns          220 ns           20\nstrcasecmp_bench/83% same:1_stddev            6.58 ns          123 ns           20\nstrcasecmp_bench/83% same:1_cv                2.16 %         70.95 %            20\n\nstrcasecmp_new_bench/83% same:1_mean           204 ns          121 ns           20\nstrcasecmp_new_bench/83% same:1_median         203 ns          120 ns           20\nstrcasecmp_new_bench/83% same:1_stddev        3.48 ns         73.3 ns           20\nstrcasecmp_new_bench/83% same:1_cv            1.71 %         60.41 %            20\n```\ntwo strings 75% same case = ~36% improvement\n```\nstrcasecmp_bench/75% same:2_mean               321 ns          183 ns           20\nstrcasecmp_bench/75% same:2_median             322 ns          237 ns           20\nstrcasecmp_bench/75% same:2_stddev            5.37 ns          129 ns           20\nstrcasecmp_bench/75% same:2_cv                1.67 %         70.76 %            20\n\nstrcasecmp_new_bench/75% same:2_mean           205 ns          127 ns           20\nstrcasecmp_new_bench/75% same:2_median         205 ns          173 ns           20\nstrcasecmp_new_bench/75% same:2_stddev        4.52 ns         81.0 ns           20\nstrcasecmp_new_bench/75% same:2_cv            2.21 %         63.68 %            20\n```\ntwo strings 66% same case = ~32% improvement\n```\nstrcasecmp_bench/66% same:3_mean               351 ns          197 ns           20\nstrcasecmp_bench/66% same:3_median             348 ns          199 ns           20\nstrcasecmp_bench/66% same:3_stddev            22.8 ns         37.4 ns           20\nstrcasecmp_bench/66% same:3_cv                6.50 %         18.92 %            20\n\nstrcasecmp_new_bench/66% same:3_mean           236 ns          138 ns           20\nstrcasecmp_new_bench/66% same:3_median         232 ns          141 ns           20\nstrcasecmp_new_bench/66% same:3_stddev        10.9 ns         57.4 ns           20\nstrcasecmp_new_bench/66% same:3_cv            4.60 %         41.62 %            20\n```\ntwo strings 50% same case = ~32% improvement\n```\nstrcasecmp_bench/50% same:4_mean               384 ns          217 ns           20\nstrcasecmp_bench/50% same:4_median             371 ns          220 ns           20\nstrcasecmp_bench/50% same:4_stddev            27.9 ns         21.2 ns           20\nstrcasecmp_bench/50% same:4_cv                7.26 %          9.77 %            20\n\nstrcasecmp_new_bench/50% same:4_mean           259 ns          147 ns           20\nstrcasecmp_new_bench/50% same:4_median         259 ns          175 ns           20\nstrcasecmp_new_bench/50% same:4_stddev        4.08 ns          102 ns           20\nstrcasecmp_new_bench/50% same:4_cv            1.57 %         69.35 %            20\n```\ntwo strings 33% same case = ~13% improvement\n```\nstrcasecmp_bench/33% same:5_mean               303 ns          169 ns           20\nstrcasecmp_bench/33% same:5_median             299 ns          159 ns           20\nstrcasecmp_bench/33% same:5_stddev            15.7 ns         54.2 ns           20\nstrcasecmp_bench/33% same:5_cv                5.17 %         32.01 %            20\n\nstrcasecmp_new_bench/33% same:5_mean           263 ns          156 ns           20\nstrcasecmp_new_bench/33% same:5_median         264 ns          166 ns           20\nstrcasecmp_new_bench/33% same:5_stddev        14.7 ns         64.0 ns           20\nstrcasecmp_new_bench/33% same:5_cv            5.57 %         41.05 %            20\n```\ntwo strings 25% same case = ~5% improvement\n```\nstrcasecmp_bench/25% same:6_mean               287 ns          163 ns           20\nstrcasecmp_bench/25% same:6_median             287 ns          161 ns           20\nstrcasecmp_bench/25% same:6_stddev            2.99 ns         59.9 ns           20\nstrcasecmp_bench/25% same:6_cv                1.04 %         36.73 %            20\n\nstrcasecmp_new_bench/25% same:6_mean           269 ns          159 ns           20\nstrcasecmp_new_bench/25% same:6_median         268 ns          158 ns           20\nstrcasecmp_new_bench/25% same:6_stddev        3.46 ns         8.69 ns           20\nstrcasecmp_new_bench/25% same:6_cv            1.29 %          5.47 %            20\n```\ntwo strings 15% same case = ~2% decrease\n```\nstrcasecmp_bench/15% same:7_mean               292 ns          168 ns           20\nstrcasecmp_bench/15% same:7_median             291 ns          169 ns           20\nstrcasecmp_bench/15% same:7_stddev            2.76 ns         14.9 ns           20\nstrcasecmp_bench/15% same:7_cv                0.95 %          8.83 %            20\n\nstrcasecmp_new_bench/15% same:7_mean           299 ns          161 ns           20\nstrcasecmp_new_bench/15% same:7_median         300 ns          139 ns           20\nstrcasecmp_new_bench/15% same:7_stddev        6.81 ns          122 ns           20\nstrcasecmp_new_bench/15% same:7_cv            2.28 %         75.59 %            20\n```\ntwo strings 0% same case = ~11% decrease\n```\nstrcasecmp_bench/0% same:8_mean                297 ns          155 ns           20\nstrcasecmp_bench/0% same:8_median              293 ns          193 ns           20\nstrcasecmp_bench/0% same:8_stddev             14.1 ns          118 ns           20\nstrcasecmp_bench/0% same:8_cv                 4.75 %         76.46 %            20\n\nstrcasecmp_new_bench/0% same:8_mean            332 ns          209 ns           20\nstrcasecmp_new_bench/0% same:8_median          331 ns          242 ns           20\nstrcasecmp_new_bench/0% same:8_stddev         4.96 ns          111 ns           20\nstrcasecmp_new_bench/0% same:8_cv             1.50 %         52.89 %            20\n```\n\nFor strings that are over 25% the same case our implementation provides up to 48% performance benefits over other implementations. However if the two strings are less than 25% there can be a similar up to 11% performance decrease.\n\nWe surmise that perhaps CPU caching does not play a significant role in string case comparison, but only branches and perdictions. `tolower` has its own branch to determine if the character is uppercase or lowercase which results in a lookup to a hash map.\n\nIf we can reduce the total number of branches, by skipping an extra branch when characters match, we can speed up the function in corrolation to the similarity of the two strings.\n\n## Hash Maps\n\nThere is another way that involves reducing the number of branches and that is by using a different character hash map.\n\nAs stated previously, some implementations of `tolower` first check if the character is uppercase and if so convert it to lowercase.\n\nThis is because these `tolower` implementations rely on the `_ctype` hash map which is a 256 character map that can be used to lookup what each character's type is - digit, upper, lower, etc.\n\nHowever, it is possible to use hash maps that are dedicated to the uppercase/lowercase conversion where the results of `tolower(c)` and `toupper(c)` are precalculated for each of the 256 characters.\n\n## Resources\n\n* [strcasecmp](https://pubs.opengroup.org/onlinepubs/9699919799/functions/strcasecmp.html)\n* [Case-insensitive string comparisons in C](https://daniel.haxx.se/blog/2022/05/19/case-insensitive-string-comparisons-in-c/)\n* [strcmpcase in Turkish](https://daniel.haxx.se/blog/2008/10/15/strcasecmp-in-turkish/)","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnmoinvaz%2Fstrcasecmp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnmoinvaz%2Fstrcasecmp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnmoinvaz%2Fstrcasecmp/lists"}