Repository files navigation

What is this?

A non-practical solution to the problem of finding five English words with 25 distinct characters, using the C++ constexpr technique, in pursuit of the fastest execution time.

This is a fork of the algorithm authored by @ilyanikolaevsky, but forced into a C++ constexpr/consteval constrains, which resulted in the execution time around 200µs.

Reasoning behind this project

The code was an experiment induced by this video: https://youtu.be/c33AZBnRHks. The base algorithm chosen for conversion to the constexpr mode must have been using standard C++ operations (no third party libraries, no compilator-specific extensions), must have been single threaded and must have been relatively fast in execution.

This project should produce the binary with - most probably - the fastest execution time in the world. The code is arranged in such way that all the computations are done in the compile time. All the application has to do in the runtime, is to read and push the compile-time-calculated data to the file.

Does this makes sense from any practical point of view? It could have, but at this moment - absolutely not. To achieve such minimal execution time, the compile time is humungus: on my Ryzen 7 3700X system it takes around 345 minutes! Moreover - this is achievable only using proper compiler - clang has gave up at the point before even can_construct_2 was fully calculated. This project should be considered as some kind of proof of concept rather than anything useful.

meme of Homer comparing execution time vs compilation time

Requirements

This code at this moment couldn't be compiled with clang. The only compiler which allows to do such big job at compile time is GCC. In order to prepare input data to be processed at compile time, a small trick with generating an intermediate input file must be done via CMake, thus please don't ignore the first step from the "Building and running" section.

Requirements:

  • GCC 12 (earlier versions were not tested, thus I don't recommend trying - unless your machine has some working hours to waste; that being said - earlier versions could work, as I abandoned pretty much all newest constexpr stuff during the evolution of this experiment),
  • CMake,
  • make,
  • recommended at least 16 GiB of RAM,
  • Linux environment (other OSes should be also ok, as long as you would have available GCC and CMake; building instructions were written using Ubuntu).

Building and running

First, use CMake to generate the intermediate input file and makefile:

mkdir build_constexpr
cd build_constexpr
cmake -DCMAKE_BUILD_TYPE=Release ..

Now you're ready to fire the compilation torture. Grab your favorite cup of coffee, cast the making spell and watch the dead terminal for about five to six hours:

make constexpr

The compilation process is single threaded - it has to be sequential, as later computations depend on the earlier ones, so throwing more jobs (with make -j500 constexpr) will make no difference.

This will produce the constexpr executable. Measuring its performance via time command is starting to be pointless, given the low precision, so the binary has built-in timer for measuring its own execution time within the nanoseconds' resolution. The output of the application will be saved to the solutions.txt file.

An example output from the application run:

draghan@fortress:~/programming/five_words_constexpr/build_constexpr$ ./constexprTotal time: 194269ns (194us)draghan@fortress:~/programming/five_words_constexpr/build_constexpr$ head solutions.txt &&echo ... && tail solutions.txt && cat ./solutions.txt | wcbawke fldxt gconv jimpy qurshbejig fldxt nymph quack vrowsevang fldxt jumby qophs wrickexptl fconv gawby hdqrs mujikexptl gconv hdqrs jumby wakifexpwy flack hdqrs jumbo vingtexpwy flock hdqrs jumba vingtfldxt gconv herbs jimpy quawkfldxt gconv jerky saqib whumpfldxt gconv jerky squib whamp...fldxt nymph squib vejoz wrackbrock japyx seqwl vingt zhmudglack hdqrs jowpy muntz vibexbrigs fldxt nymph quawk vejozampyx flung hdqrs twick vejozfldxt gconv jerky squiz whampfldxt grimp quawk synch vejozfldxt gryph manqu swick vejozfldxt nymph quags vejoz wrickfrock japyx seqwl vingt zhmud538 2690 16678

The CMake project has also defined the original target, to build the original code written by @ilyanikolaevsky - this could be useful for validating the results.

constexpr fun

At a glance, the idea to use the constexpr feature of C++ looks brilliant, but there are lots of restrictions of what operations and calculations could be done in compile time, not to mention ENORMOUS compilation time.

Not a long ago only very simple stuff could be executed at compile time - some simple expressions like 2+2 and that would be it. Only recently the C++ standard and compilers allow to do more advanced computations in the compile time. To play with this project I went with compiling the newest available GCC and newest available Clang, as I was trying to use newest parts of C++ standard library available for the constexpr context, e.g. std::bitset, which wasn't available in any "stable" release. The process of getting this to work was not easy - this feature of C++ is not meant and is not ready for such extensive string processing, at least at this point in time.

Some lesson learned during this experiment:

  • can't read any file at the compilation time (there is some proposal which allows embedding data to the application, but it's not yet incorporated into the standard): the only option at this moment is the #include directive,
  • there is no way in the standard C++ to include an external file's content into a string to be processed later: thus the only dirty trick in this project with CMake, which generates an intermediate input file; all it does is putting in the begining and the end of the file C++'s raw multiline literal markups (R"( and )") - in this form it could be safely included into a C++ source file,
  • better to avoid using any containers from C++ std::lib: the cost of compilation time and memory footprint is insanely big, compared to the plain arrays,
  • the compiler was sometimes complaining about modifying anything passed by a reference to the consteval function,
  • you can't return an array from the function, but you can return a struct containing array,
  • if you don't use dynamically sized container, you have to use statically sized array (BTW. much better to use plain array than std::array - huge memory savings there),
  • considering the above: you must know the size of your results before you actually compute the results; this seems like a paradox, but the solution is painfully simple: you must do your computations twice - first time in order to count how big your results will be, and second time doing the actual job with assigning and returning your results,
  • better not to place big constexpr evaluated array inside a regular runtime function, as this will most likely corrupt the runtime stack,
  • clang is doing consteval calculations much faster than GCC, but has lower limit of consteval operations' count,
  • writing chars to a file is about 2x slower with C++'s std::ofstream than with C's fputc (at least in my environment).

About

A non-practical solution to the problem of finding five English words with 25 distinct characters, using a constexpr technique.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Repository files navigation

What is this?

A non-practical solution to the problem of finding five English words with 25 distinct characters, using the C++ constexpr technique, in pursuit of the fastest execution time.

This is a fork of the algorithm authored by @ilyanikolaevsky, but forced into a C++ constexpr/consteval constrains, which resulted in the execution time around 200µs.

Reasoning behind this project

The code was an experiment induced by this video: https://youtu.be/c33AZBnRHks. The base algorithm chosen for conversion to the constexpr mode must have been using standard C++ operations (no third party libraries, no compilator-specific extensions), must have been single threaded and must have been relatively fast in execution.

This project should produce the binary with - most probably - the fastest execution time in the world. The code is arranged in such way that all the computations are done in the compile time. All the application has to do in the runtime, is to read and push the compile-time-calculated data to the file.

Does this makes sense from any practical point of view? It could have, but at this moment - absolutely not. To achieve such minimal execution time, the compile time is humungus: on my Ryzen 7 3700X system it takes around 345 minutes! Moreover - this is achievable only using proper compiler - clang has gave up at the point before even can_construct_2 was fully calculated. This project should be considered as some kind of proof of concept rather than anything useful.

meme of Homer comparing execution time vs compilation time

Requirements

This code at this moment couldn't be compiled with clang. The only compiler which allows to do such big job at compile time is GCC. In order to prepare input data to be processed at compile time, a small trick with generating an intermediate input file must be done via CMake, thus please don't ignore the first step from the "Building and running" section.

Requirements:

  • GCC 12 (earlier versions were not tested, thus I don't recommend trying - unless your machine has some working hours to waste; that being said - earlier versions could work, as I abandoned pretty much all newest constexpr stuff during the evolution of this experiment),
  • CMake,
  • make,
  • recommended at least 16 GiB of RAM,
  • Linux environment (other OSes should be also ok, as long as you would have available GCC and CMake; building instructions were written using Ubuntu).

Building and running

First, use CMake to generate the intermediate input file and makefile:

mkdir build_constexpr
cd build_constexpr
cmake -DCMAKE_BUILD_TYPE=Release ..

Now you're ready to fire the compilation torture. Grab your favorite cup of coffee, cast the making spell and watch the dead terminal for about five to six hours:

make constexpr

The compilation process is single threaded - it has to be sequential, as later computations depend on the earlier ones, so throwing more jobs (with make -j500 constexpr) will make no difference.

This will produce the constexpr executable. Measuring its performance via time command is starting to be pointless, given the low precision, so the binary has built-in timer for measuring its own execution time within the nanoseconds' resolution. The output of the application will be saved to the solutions.txt file.

An example output from the application run:

draghan@fortress:~/programming/five_words_constexpr/build_constexpr$ ./constexprTotal time: 194269ns (194us)draghan@fortress:~/programming/five_words_constexpr/build_constexpr$ head solutions.txt &&echo ... && tail solutions.txt && cat ./solutions.txt | wcbawke fldxt gconv jimpy qurshbejig fldxt nymph quack vrowsevang fldxt jumby qophs wrickexptl fconv gawby hdqrs mujikexptl gconv hdqrs jumby wakifexpwy flack hdqrs jumbo vingtexpwy flock hdqrs jumba vingtfldxt gconv herbs jimpy quawkfldxt gconv jerky saqib whumpfldxt gconv jerky squib whamp...fldxt nymph squib vejoz wrackbrock japyx seqwl vingt zhmudglack hdqrs jowpy muntz vibexbrigs fldxt nymph quawk vejozampyx flung hdqrs twick vejozfldxt gconv jerky squiz whampfldxt grimp quawk synch vejozfldxt gryph manqu swick vejozfldxt nymph quags vejoz wrickfrock japyx seqwl vingt zhmud538 2690 16678

The CMake project has also defined the original target, to build the original code written by @ilyanikolaevsky - this could be useful for validating the results.

constexpr fun

At a glance, the idea to use the constexpr feature of C++ looks brilliant, but there are lots of restrictions of what operations and calculations could be done in compile time, not to mention ENORMOUS compilation time.

Not a long ago only very simple stuff could be executed at compile time - some simple expressions like 2+2 and that would be it. Only recently the C++ standard and compilers allow to do more advanced computations in the compile time. To play with this project I went with compiling the newest available GCC and newest available Clang, as I was trying to use newest parts of C++ standard library available for the constexpr context, e.g. std::bitset, which wasn't available in any "stable" release. The process of getting this to work was not easy - this feature of C++ is not meant and is not ready for such extensive string processing, at least at this point in time.

Some lesson learned during this experiment:

  • can't read any file at the compilation time (there is some proposal which allows embedding data to the application, but it's not yet incorporated into the standard): the only option at this moment is the #include directive,
  • there is no way in the standard C++ to include an external file's content into a string to be processed later: thus the only dirty trick in this project with CMake, which generates an intermediate input file; all it does is putting in the begining and the end of the file C++'s raw multiline literal markups (R"( and )") - in this form it could be safely included into a C++ source file,
  • better to avoid using any containers from C++ std::lib: the cost of compilation time and memory footprint is insanely big, compared to the plain arrays,
  • the compiler was sometimes complaining about modifying anything passed by a reference to the consteval function,
  • you can't return an array from the function, but you can return a struct containing array,
  • if you don't use dynamically sized container, you have to use statically sized array (BTW. much better to use plain array than std::array - huge memory savings there),
  • considering the above: you must know the size of your results before you actually compute the results; this seems like a paradox, but the solution is painfully simple: you must do your computations twice - first time in order to count how big your results will be, and second time doing the actual job with assigning and returning your results,
  • better not to place big constexpr evaluated array inside a regular runtime function, as this will most likely corrupt the runtime stack,
  • clang is doing consteval calculations much faster than GCC, but has lower limit of consteval operations' count,
  • writing chars to a file is about 2x slower with C++'s std::ofstream than with C's fputc (at least in my environment).

About

A non-practical solution to the problem of finding five English words with 25 distinct characters, using a constexpr technique.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

What is this?

A non-practical solution to the problem of finding five English words with 25 distinct characters, using the C++ constexpr technique, in pursuit of the fastest execution time.

This is a fork of the algorithm authored by @ilyanikolaevsky, but forced into a C++ constexpr/consteval constrains, which resulted in the execution time around 200µs.

Reasoning behind this project

The code was an experiment induced by this video: https://youtu.be/c33AZBnRHks. The base algorithm chosen for conversion to the constexpr mode must have been using standard C++ operations (no third party libraries, no compilator-specific extensions), must have been single threaded and must have been relatively fast in execution.

This project should produce the binary with - most probably - the fastest execution time in the world. The code is arranged in such way that all the computations are done in the compile time. All the application has to do in the runtime, is to read and push the compile-time-calculated data to the file.

Does this makes sense from any practical point of view? It could have, but at this moment - absolutely not. To achieve such minimal execution time, the compile time is humungus: on my Ryzen 7 3700X system it takes around 345 minutes! Moreover - this is achievable only using proper compiler - clang has gave up at the point before even can_construct_2 was fully calculated. This project should be considered as some kind of proof of concept rather than anything useful.

meme of Homer comparing execution time vs compilation time

Requirements

This code at this moment couldn't be compiled with clang. The only compiler which allows to do such big job at compile time is GCC. In order to prepare input data to be processed at compile time, a small trick with generating an intermediate input file must be done via CMake, thus please don't ignore the first step from the "Building and running" section.

Requirements:

  • GCC 12 (earlier versions were not tested, thus I don't recommend trying - unless your machine has some working hours to waste; that being said - earlier versions could work, as I abandoned pretty much all newest constexpr stuff during the evolution of this experiment),
  • CMake,
  • make,
  • recommended at least 16 GiB of RAM,
  • Linux environment (other OSes should be also ok, as long as you would have available GCC and CMake; building instructions were written using Ubuntu).

Building and running

First, use CMake to generate the intermediate input file and makefile:

mkdir build_constexpr
cd build_constexpr
cmake -DCMAKE_BUILD_TYPE=Release ..

Now you're ready to fire the compilation torture. Grab your favorite cup of coffee, cast the making spell and watch the dead terminal for about five to six hours:

make constexpr

The compilation process is single threaded - it has to be sequential, as later computations depend on the earlier ones, so throwing more jobs (with make -j500 constexpr) will make no difference.

This will produce the constexpr executable. Measuring its performance via time command is starting to be pointless, given the low precision, so the binary has built-in timer for measuring its own execution time within the nanoseconds' resolution. The output of the application will be saved to the solutions.txt file.

An example output from the application run:

draghan@fortress:~/programming/five_words_constexpr/build_constexpr$ ./constexprTotal time: 194269ns (194us)draghan@fortress:~/programming/five_words_constexpr/build_constexpr$ head solutions.txt &&echo ... && tail solutions.txt && cat ./solutions.txt | wcbawke fldxt gconv jimpy qurshbejig fldxt nymph quack vrowsevang fldxt jumby qophs wrickexptl fconv gawby hdqrs mujikexptl gconv hdqrs jumby wakifexpwy flack hdqrs jumbo vingtexpwy flock hdqrs jumba vingtfldxt gconv herbs jimpy quawkfldxt gconv jerky saqib whumpfldxt gconv jerky squib whamp...fldxt nymph squib vejoz wrackbrock japyx seqwl vingt zhmudglack hdqrs jowpy muntz vibexbrigs fldxt nymph quawk vejozampyx flung hdqrs twick vejozfldxt gconv jerky squiz whampfldxt grimp quawk synch vejozfldxt gryph manqu swick vejozfldxt nymph quags vejoz wrickfrock japyx seqwl vingt zhmud538 2690 16678

The CMake project has also defined the original target, to build the original code written by @ilyanikolaevsky - this could be useful for validating the results.

constexpr fun

At a glance, the idea to use the constexpr feature of C++ looks brilliant, but there are lots of restrictions of what operations and calculations could be done in compile time, not to mention ENORMOUS compilation time.

Not a long ago only very simple stuff could be executed at compile time - some simple expressions like 2+2 and that would be it. Only recently the C++ standard and compilers allow to do more advanced computations in the compile time. To play with this project I went with compiling the newest available GCC and newest available Clang, as I was trying to use newest parts of C++ standard library available for the constexpr context, e.g. std::bitset, which wasn't available in any "stable" release. The process of getting this to work was not easy - this feature of C++ is not meant and is not ready for such extensive string processing, at least at this point in time.

Some lesson learned during this experiment:

  • can't read any file at the compilation time (there is some proposal which allows embedding data to the application, but it's not yet incorporated into the standard): the only option at this moment is the #include directive,
  • there is no way in the standard C++ to include an external file's content into a string to be processed later: thus the only dirty trick in this project with CMake, which generates an intermediate input file; all it does is putting in the begining and the end of the file C++'s raw multiline literal markups (R"( and )") - in this form it could be safely included into a C++ source file,
  • better to avoid using any containers from C++ std::lib: the cost of compilation time and memory footprint is insanely big, compared to the plain arrays,
  • the compiler was sometimes complaining about modifying anything passed by a reference to the consteval function,
  • you can't return an array from the function, but you can return a struct containing array,
  • if you don't use dynamically sized container, you have to use statically sized array (BTW. much better to use plain array than std::array - huge memory savings there),
  • considering the above: you must know the size of your results before you actually compute the results; this seems like a paradox, but the solution is painfully simple: you must do your computations twice - first time in order to count how big your results will be, and second time doing the actual job with assigning and returning your results,
  • better not to place big constexpr evaluated array inside a regular runtime function, as this will most likely corrupt the runtime stack,
  • clang is doing consteval calculations much faster than GCC, but has lower limit of consteval operations' count,
  • writing chars to a file is about 2x slower with C++'s std::ofstream than with C's fputc (at least in my environment).

About

A non-practical solution to the problem of finding five English words with 25 distinct characters, using a constexpr technique.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

What is this?

A non-practical solution to the problem of finding five English words with 25 distinct characters, using the C++ constexpr technique, in pursuit of the fastest execution time.

This is a fork of the algorithm authored by @ilyanikolaevsky, but forced into a C++ constexpr/consteval constrains, which resulted in the execution time around 200µs.

Reasoning behind this project

The code was an experiment induced by this video: https://youtu.be/c33AZBnRHks. The base algorithm chosen for conversion to the constexpr mode must have been using standard C++ operations (no third party libraries, no compilator-specific extensions), must have been single threaded and must have been relatively fast in execution.

This project should produce the binary with - most probably - the fastest execution time in the world. The code is arranged in such way that all the computations are done in the compile time. All the application has to do in the runtime, is to read and push the compile-time-calculated data to the file.

Does this makes sense from any practical point of view? It could have, but at this moment - absolutely not. To achieve such minimal execution time, the compile time is humungus: on my Ryzen 7 3700X system it takes around 345 minutes! Moreover - this is achievable only using proper compiler - clang has gave up at the point before even can_construct_2 was fully calculated. This project should be considered as some kind of proof of concept rather than anything useful.

meme of Homer comparing execution time vs compilation time

Requirements

This code at this moment couldn't be compiled with clang. The only compiler which allows to do such big job at compile time is GCC. In order to prepare input data to be processed at compile time, a small trick with generating an intermediate input file must be done via CMake, thus please don't ignore the first step from the "Building and running" section.

Requirements:

  • GCC 12 (earlier versions were not tested, thus I don't recommend trying - unless your machine has some working hours to waste; that being said - earlier versions could work, as I abandoned pretty much all newest constexpr stuff during the evolution of this experiment),
  • CMake,
  • make,
  • recommended at least 16 GiB of RAM,
  • Linux environment (other OSes should be also ok, as long as you would have available GCC and CMake; building instructions were written using Ubuntu).

Building and running

First, use CMake to generate the intermediate input file and makefile:

mkdir build_constexpr
cd build_constexpr
cmake -DCMAKE_BUILD_TYPE=Release ..

Now you're ready to fire the compilation torture. Grab your favorite cup of coffee, cast the making spell and watch the dead terminal for about five to six hours:

make constexpr

The compilation process is single threaded - it has to be sequential, as later computations depend on the earlier ones, so throwing more jobs (with make -j500 constexpr) will make no difference.

This will produce the constexpr executable. Measuring its performance via time command is starting to be pointless, given the low precision, so the binary has built-in timer for measuring its own execution time within the nanoseconds' resolution. The output of the application will be saved to the solutions.txt file.

An example output from the application run:

draghan@fortress:~/programming/five_words_constexpr/build_constexpr$ ./constexprTotal time: 194269ns (194us)draghan@fortress:~/programming/five_words_constexpr/build_constexpr$ head solutions.txt &&echo ... && tail solutions.txt && cat ./solutions.txt | wcbawke fldxt gconv jimpy qurshbejig fldxt nymph quack vrowsevang fldxt jumby qophs wrickexptl fconv gawby hdqrs mujikexptl gconv hdqrs jumby wakifexpwy flack hdqrs jumbo vingtexpwy flock hdqrs jumba vingtfldxt gconv herbs jimpy quawkfldxt gconv jerky saqib whumpfldxt gconv jerky squib whamp...fldxt nymph squib vejoz wrackbrock japyx seqwl vingt zhmudglack hdqrs jowpy muntz vibexbrigs fldxt nymph quawk vejozampyx flung hdqrs twick vejozfldxt gconv jerky squiz whampfldxt grimp quawk synch vejozfldxt gryph manqu swick vejozfldxt nymph quags vejoz wrickfrock japyx seqwl vingt zhmud538 2690 16678

The CMake project has also defined the original target, to build the original code written by @ilyanikolaevsky - this could be useful for validating the results.

constexpr fun

At a glance, the idea to use the constexpr feature of C++ looks brilliant, but there are lots of restrictions of what operations and calculations could be done in compile time, not to mention ENORMOUS compilation time.

Not a long ago only very simple stuff could be executed at compile time - some simple expressions like 2+2 and that would be it. Only recently the C++ standard and compilers allow to do more advanced computations in the compile time. To play with this project I went with compiling the newest available GCC and newest available Clang, as I was trying to use newest parts of C++ standard library available for the constexpr context, e.g. std::bitset, which wasn't available in any "stable" release. The process of getting this to work was not easy - this feature of C++ is not meant and is not ready for such extensive string processing, at least at this point in time.

Some lesson learned during this experiment:

  • can't read any file at the compilation time (there is some proposal which allows embedding data to the application, but it's not yet incorporated into the standard): the only option at this moment is the #include directive,
  • there is no way in the standard C++ to include an external file's content into a string to be processed later: thus the only dirty trick in this project with CMake, which generates an intermediate input file; all it does is putting in the begining and the end of the file C++'s raw multiline literal markups (R"( and )") - in this form it could be safely included into a C++ source file,
  • better to avoid using any containers from C++ std::lib: the cost of compilation time and memory footprint is insanely big, compared to the plain arrays,
  • the compiler was sometimes complaining about modifying anything passed by a reference to the consteval function,
  • you can't return an array from the function, but you can return a struct containing array,
  • if you don't use dynamically sized container, you have to use statically sized array (BTW. much better to use plain array than std::array - huge memory savings there),
  • considering the above: you must know the size of your results before you actually compute the results; this seems like a paradox, but the solution is painfully simple: you must do your computations twice - first time in order to count how big your results will be, and second time doing the actual job with assigning and returning your results,
  • better not to place big constexpr evaluated array inside a regular runtime function, as this will most likely corrupt the runtime stack,
  • clang is doing consteval calculations much faster than GCC, but has lower limit of consteval operations' count,
  • writing chars to a file is about 2x slower with C++'s std::ofstream than with C's fputc (at least in my environment).

About

A non-practical solution to the problem of finding five English words with 25 distinct characters, using a constexpr technique.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Repository files navigation

What is this?

A non-practical solution to the problem of finding five English words with 25 distinct characters, using the C++ constexpr technique, in pursuit of the fastest execution time.

This is a fork of the algorithm authored by @ilyanikolaevsky, but forced into a C++ constexpr/consteval constrains, which resulted in the execution time around 200µs.

Reasoning behind this project

The code was an experiment induced by this video: https://youtu.be/c33AZBnRHks. The base algorithm chosen for conversion to the constexpr mode must have been using standard C++ operations (no third party libraries, no compilator-specific extensions), must have been single threaded and must have been relatively fast in execution.

This project should produce the binary with - most probably - the fastest execution time in the world. The code is arranged in such way that all the computations are done in the compile time. All the application has to do in the runtime, is to read and push the compile-time-calculated data to the file.

Does this makes sense from any practical point of view? It could have, but at this moment - absolutely not. To achieve such minimal execution time, the compile time is humungus: on my Ryzen 7 3700X system it takes around 345 minutes! Moreover - this is achievable only using proper compiler - clang has gave up at the point before even can_construct_2 was fully calculated. This project should be considered as some kind of proof of concept rather than anything useful.

meme of Homer comparing execution time vs compilation time

Requirements

This code at this moment couldn't be compiled with clang. The only compiler which allows to do such big job at compile time is GCC. In order to prepare input data to be processed at compile time, a small trick with generating an intermediate input file must be done via CMake, thus please don't ignore the first step from the "Building and running" section.

Requirements:

  • GCC 12 (earlier versions were not tested, thus I don't recommend trying - unless your machine has some working hours to waste; that being said - earlier versions could work, as I abandoned pretty much all newest constexpr stuff during the evolution of this experiment),
  • CMake,
  • make,
  • recommended at least 16 GiB of RAM,
  • Linux environment (other OSes should be also ok, as long as you would have available GCC and CMake; building instructions were written using Ubuntu).

Building and running

First, use CMake to generate the intermediate input file and makefile:

mkdir build_constexpr
cd build_constexpr
cmake -DCMAKE_BUILD_TYPE=Release ..

Now you're ready to fire the compilation torture. Grab your favorite cup of coffee, cast the making spell and watch the dead terminal for about five to six hours:

make constexpr

The compilation process is single threaded - it has to be sequential, as later computations depend on the earlier ones, so throwing more jobs (with make -j500 constexpr) will make no difference.

This will produce the constexpr executable. Measuring its performance via time command is starting to be pointless, given the low precision, so the binary has built-in timer for measuring its own execution time within the nanoseconds' resolution. The output of the application will be saved to the solutions.txt file.

An example output from the application run:

draghan@fortress:~/programming/five_words_constexpr/build_constexpr$ ./constexprTotal time: 194269ns (194us)draghan@fortress:~/programming/five_words_constexpr/build_constexpr$ head solutions.txt &&echo ... && tail solutions.txt && cat ./solutions.txt | wcbawke fldxt gconv jimpy qurshbejig fldxt nymph quack vrowsevang fldxt jumby qophs wrickexptl fconv gawby hdqrs mujikexptl gconv hdqrs jumby wakifexpwy flack hdqrs jumbo vingtexpwy flock hdqrs jumba vingtfldxt gconv herbs jimpy quawkfldxt gconv jerky saqib whumpfldxt gconv jerky squib whamp...fldxt nymph squib vejoz wrackbrock japyx seqwl vingt zhmudglack hdqrs jowpy muntz vibexbrigs fldxt nymph quawk vejozampyx flung hdqrs twick vejozfldxt gconv jerky squiz whampfldxt grimp quawk synch vejozfldxt gryph manqu swick vejozfldxt nymph quags vejoz wrickfrock japyx seqwl vingt zhmud538 2690 16678

The CMake project has also defined the original target, to build the original code written by @ilyanikolaevsky - this could be useful for validating the results.

constexpr fun

At a glance, the idea to use the constexpr feature of C++ looks brilliant, but there are lots of restrictions of what operations and calculations could be done in compile time, not to mention ENORMOUS compilation time.

Not a long ago only very simple stuff could be executed at compile time - some simple expressions like 2+2 and that would be it. Only recently the C++ standard and compilers allow to do more advanced computations in the compile time. To play with this project I went with compiling the newest available GCC and newest available Clang, as I was trying to use newest parts of C++ standard library available for the constexpr context, e.g. std::bitset, which wasn't available in any "stable" release. The process of getting this to work was not easy - this feature of C++ is not meant and is not ready for such extensive string processing, at least at this point in time.

Some lesson learned during this experiment:

  • can't read any file at the compilation time (there is some proposal which allows embedding data to the application, but it's not yet incorporated into the standard): the only option at this moment is the #include directive,
  • there is no way in the standard C++ to include an external file's content into a string to be processed later: thus the only dirty trick in this project with CMake, which generates an intermediate input file; all it does is putting in the begining and the end of the file C++'s raw multiline literal markups (R"( and )") - in this form it could be safely included into a C++ source file,
  • better to avoid using any containers from C++ std::lib: the cost of compilation time and memory footprint is insanely big, compared to the plain arrays,
  • the compiler was sometimes complaining about modifying anything passed by a reference to the consteval function,
  • you can't return an array from the function, but you can return a struct containing array,
  • if you don't use dynamically sized container, you have to use statically sized array (BTW. much better to use plain array than std::array - huge memory savings there),
  • considering the above: you must know the size of your results before you actually compute the results; this seems like a paradox, but the solution is painfully simple: you must do your computations twice - first time in order to count how big your results will be, and second time doing the actual job with assigning and returning your results,
  • better not to place big constexpr evaluated array inside a regular runtime function, as this will most likely corrupt the runtime stack,
  • clang is doing consteval calculations much faster than GCC, but has lower limit of consteval operations' count,
  • writing chars to a file is about 2x slower with C++'s std::ofstream than with C's fputc (at least in my environment).

About

A non-practical solution to the problem of finding five English words with 25 distinct characters, using a constexpr technique.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

What is this?

A non-practical solution to the problem of finding five English words with 25 distinct characters, using the C++ constexpr technique, in pursuit of the fastest execution time.

This is a fork of the algorithm authored by @ilyanikolaevsky, but forced into a C++ constexpr/consteval constrains, which resulted in the execution time around 200µs.

Reasoning behind this project

The code was an experiment induced by this video: https://youtu.be/c33AZBnRHks. The base algorithm chosen for conversion to the constexpr mode must have been using standard C++ operations (no third party libraries, no compilator-specific extensions), must have been single threaded and must have been relatively fast in execution.

This project should produce the binary with - most probably - the fastest execution time in the world. The code is arranged in such way that all the computations are done in the compile time. All the application has to do in the runtime, is to read and push the compile-time-calculated data to the file.

Does this makes sense from any practical point of view? It could have, but at this moment - absolutely not. To achieve such minimal execution time, the compile time is humungus: on my Ryzen 7 3700X system it takes around 345 minutes! Moreover - this is achievable only using proper compiler - clang has gave up at the point before even can_construct_2 was fully calculated. This project should be considered as some kind of proof of concept rather than anything useful.

meme of Homer comparing execution time vs compilation time

Requirements

This code at this moment couldn't be compiled with clang. The only compiler which allows to do such big job at compile time is GCC. In order to prepare input data to be processed at compile time, a small trick with generating an intermediate input file must be done via CMake, thus please don't ignore the first step from the "Building and running" section.

Requirements:

  • GCC 12 (earlier versions were not tested, thus I don't recommend trying - unless your machine has some working hours to waste; that being said - earlier versions could work, as I abandoned pretty much all newest constexpr stuff during the evolution of this experiment),
  • CMake,
  • make,
  • recommended at least 16 GiB of RAM,
  • Linux environment (other OSes should be also ok, as long as you would have available GCC and CMake; building instructions were written using Ubuntu).

Building and running

First, use CMake to generate the intermediate input file and makefile:

mkdir build_constexpr
cd build_constexpr
cmake -DCMAKE_BUILD_TYPE=Release ..

Now you're ready to fire the compilation torture. Grab your favorite cup of coffee, cast the making spell and watch the dead terminal for about five to six hours:

make constexpr

The compilation process is single threaded - it has to be sequential, as later computations depend on the earlier ones, so throwing more jobs (with make -j500 constexpr) will make no difference.

This will produce the constexpr executable. Measuring its performance via time command is starting to be pointless, given the low precision, so the binary has built-in timer for measuring its own execution time within the nanoseconds' resolution. The output of the application will be saved to the solutions.txt file.

An example output from the application run:

draghan@fortress:~/programming/five_words_constexpr/build_constexpr$ ./constexprTotal time: 194269ns (194us)draghan@fortress:~/programming/five_words_constexpr/build_constexpr$ head solutions.txt &&echo ... && tail solutions.txt && cat ./solutions.txt | wcbawke fldxt gconv jimpy qurshbejig fldxt nymph quack vrowsevang fldxt jumby qophs wrickexptl fconv gawby hdqrs mujikexptl gconv hdqrs jumby wakifexpwy flack hdqrs jumbo vingtexpwy flock hdqrs jumba vingtfldxt gconv herbs jimpy quawkfldxt gconv jerky saqib whumpfldxt gconv jerky squib whamp...fldxt nymph squib vejoz wrackbrock japyx seqwl vingt zhmudglack hdqrs jowpy muntz vibexbrigs fldxt nymph quawk vejozampyx flung hdqrs twick vejozfldxt gconv jerky squiz whampfldxt grimp quawk synch vejozfldxt gryph manqu swick vejozfldxt nymph quags vejoz wrickfrock japyx seqwl vingt zhmud538 2690 16678

The CMake project has also defined the original target, to build the original code written by @ilyanikolaevsky - this could be useful for validating the results.

constexpr fun

At a glance, the idea to use the constexpr feature of C++ looks brilliant, but there are lots of restrictions of what operations and calculations could be done in compile time, not to mention ENORMOUS compilation time.

Not a long ago only very simple stuff could be executed at compile time - some simple expressions like 2+2 and that would be it. Only recently the C++ standard and compilers allow to do more advanced computations in the compile time. To play with this project I went with compiling the newest available GCC and newest available Clang, as I was trying to use newest parts of C++ standard library available for the constexpr context, e.g. std::bitset, which wasn't available in any "stable" release. The process of getting this to work was not easy - this feature of C++ is not meant and is not ready for such extensive string processing, at least at this point in time.

Some lesson learned during this experiment:

  • can't read any file at the compilation time (there is some proposal which allows embedding data to the application, but it's not yet incorporated into the standard): the only option at this moment is the #include directive,
  • there is no way in the standard C++ to include an external file's content into a string to be processed later: thus the only dirty trick in this project with CMake, which generates an intermediate input file; all it does is putting in the begining and the end of the file C++'s raw multiline literal markups (R"( and )") - in this form it could be safely included into a C++ source file,
  • better to avoid using any containers from C++ std::lib: the cost of compilation time and memory footprint is insanely big, compared to the plain arrays,
  • the compiler was sometimes complaining about modifying anything passed by a reference to the consteval function,
  • you can't return an array from the function, but you can return a struct containing array,
  • if you don't use dynamically sized container, you have to use statically sized array (BTW. much better to use plain array than std::array - huge memory savings there),
  • considering the above: you must know the size of your results before you actually compute the results; this seems like a paradox, but the solution is painfully simple: you must do your computations twice - first time in order to count how big your results will be, and second time doing the actual job with assigning and returning your results,
  • better not to place big constexpr evaluated array inside a regular runtime function, as this will most likely corrupt the runtime stack,
  • clang is doing consteval calculations much faster than GCC, but has lower limit of consteval operations' count,
  • writing chars to a file is about 2x slower with C++'s std::ofstream than with C's fputc (at least in my environment).

About

A non-practical solution to the problem of finding five English words with 25 distinct characters, using a constexpr technique.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

What is this?

A non-practical solution to the problem of finding five English words with 25 distinct characters, using the C++ constexpr technique, in pursuit of the fastest execution time.

This is a fork of the algorithm authored by @ilyanikolaevsky, but forced into a C++ constexpr/consteval constrains, which resulted in the execution time around 200µs.

Reasoning behind this project

The code was an experiment induced by this video: https://youtu.be/c33AZBnRHks. The base algorithm chosen for conversion to the constexpr mode must have been using standard C++ operations (no third party libraries, no compilator-specific extensions), must have been single threaded and must have been relatively fast in execution.

This project should produce the binary with - most probably - the fastest execution time in the world. The code is arranged in such way that all the computations are done in the compile time. All the application has to do in the runtime, is to read and push the compile-time-calculated data to the file.

Does this makes sense from any practical point of view? It could have, but at this moment - absolutely not. To achieve such minimal execution time, the compile time is humungus: on my Ryzen 7 3700X system it takes around 345 minutes! Moreover - this is achievable only using proper compiler - clang has gave up at the point before even can_construct_2 was fully calculated. This project should be considered as some kind of proof of concept rather than anything useful.

meme of Homer comparing execution time vs compilation time

Requirements

This code at this moment couldn't be compiled with clang. The only compiler which allows to do such big job at compile time is GCC. In order to prepare input data to be processed at compile time, a small trick with generating an intermediate input file must be done via CMake, thus please don't ignore the first step from the "Building and running" section.

Requirements:

  • GCC 12 (earlier versions were not tested, thus I don't recommend trying - unless your machine has some working hours to waste; that being said - earlier versions could work, as I abandoned pretty much all newest constexpr stuff during the evolution of this experiment),
  • CMake,
  • make,
  • recommended at least 16 GiB of RAM,
  • Linux environment (other OSes should be also ok, as long as you would have available GCC and CMake; building instructions were written using Ubuntu).

Building and running

First, use CMake to generate the intermediate input file and makefile:

mkdir build_constexpr
cd build_constexpr
cmake -DCMAKE_BUILD_TYPE=Release ..

Now you're ready to fire the compilation torture. Grab your favorite cup of coffee, cast the making spell and watch the dead terminal for about five to six hours:

make constexpr

The compilation process is single threaded - it has to be sequential, as later computations depend on the earlier ones, so throwing more jobs (with make -j500 constexpr) will make no difference.

This will produce the constexpr executable. Measuring its performance via time command is starting to be pointless, given the low precision, so the binary has built-in timer for measuring its own execution time within the nanoseconds' resolution. The output of the application will be saved to the solutions.txt file.

An example output from the application run:

draghan@fortress:~/programming/five_words_constexpr/build_constexpr$ ./constexprTotal time: 194269ns (194us)draghan@fortress:~/programming/five_words_constexpr/build_constexpr$ head solutions.txt &&echo ... && tail solutions.txt && cat ./solutions.txt | wcbawke fldxt gconv jimpy qurshbejig fldxt nymph quack vrowsevang fldxt jumby qophs wrickexptl fconv gawby hdqrs mujikexptl gconv hdqrs jumby wakifexpwy flack hdqrs jumbo vingtexpwy flock hdqrs jumba vingtfldxt gconv herbs jimpy quawkfldxt gconv jerky saqib whumpfldxt gconv jerky squib whamp...fldxt nymph squib vejoz wrackbrock japyx seqwl vingt zhmudglack hdqrs jowpy muntz vibexbrigs fldxt nymph quawk vejozampyx flung hdqrs twick vejozfldxt gconv jerky squiz whampfldxt grimp quawk synch vejozfldxt gryph manqu swick vejozfldxt nymph quags vejoz wrickfrock japyx seqwl vingt zhmud538 2690 16678

The CMake project has also defined the original target, to build the original code written by @ilyanikolaevsky - this could be useful for validating the results.

constexpr fun

At a glance, the idea to use the constexpr feature of C++ looks brilliant, but there are lots of restrictions of what operations and calculations could be done in compile time, not to mention ENORMOUS compilation time.

Not a long ago only very simple stuff could be executed at compile time - some simple expressions like 2+2 and that would be it. Only recently the C++ standard and compilers allow to do more advanced computations in the compile time. To play with this project I went with compiling the newest available GCC and newest available Clang, as I was trying to use newest parts of C++ standard library available for the constexpr context, e.g. std::bitset, which wasn't available in any "stable" release. The process of getting this to work was not easy - this feature of C++ is not meant and is not ready for such extensive string processing, at least at this point in time.

Some lesson learned during this experiment:

  • can't read any file at the compilation time (there is some proposal which allows embedding data to the application, but it's not yet incorporated into the standard): the only option at this moment is the #include directive,
  • there is no way in the standard C++ to include an external file's content into a string to be processed later: thus the only dirty trick in this project with CMake, which generates an intermediate input file; all it does is putting in the begining and the end of the file C++'s raw multiline literal markups (R"( and )") - in this form it could be safely included into a C++ source file,
  • better to avoid using any containers from C++ std::lib: the cost of compilation time and memory footprint is insanely big, compared to the plain arrays,
  • the compiler was sometimes complaining about modifying anything passed by a reference to the consteval function,
  • you can't return an array from the function, but you can return a struct containing array,
  • if you don't use dynamically sized container, you have to use statically sized array (BTW. much better to use plain array than std::array - huge memory savings there),
  • considering the above: you must know the size of your results before you actually compute the results; this seems like a paradox, but the solution is painfully simple: you must do your computations twice - first time in order to count how big your results will be, and second time doing the actual job with assigning and returning your results,
  • better not to place big constexpr evaluated array inside a regular runtime function, as this will most likely corrupt the runtime stack,
  • clang is doing consteval calculations much faster than GCC, but has lower limit of consteval operations' count,
  • writing chars to a file is about 2x slower with C++'s std::ofstream than with C's fputc (at least in my environment).

About

A non-practical solution to the problem of finding five English words with 25 distinct characters, using a constexpr technique.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Repository files navigation

What is this?

A non-practical solution to the problem of finding five English words with 25 distinct characters, using the C++ constexpr technique, in pursuit of the fastest execution time.

This is a fork of the algorithm authored by @ilyanikolaevsky, but forced into a C++ constexpr/consteval constrains, which resulted in the execution time around 200µs.

Reasoning behind this project

The code was an experiment induced by this video: https://youtu.be/c33AZBnRHks. The base algorithm chosen for conversion to the constexpr mode must have been using standard C++ operations (no third party libraries, no compilator-specific extensions), must have been single threaded and must have been relatively fast in execution.

This project should produce the binary with - most probably - the fastest execution time in the world. The code is arranged in such way that all the computations are done in the compile time. All the application has to do in the runtime, is to read and push the compile-time-calculated data to the file.

Does this makes sense from any practical point of view? It could have, but at this moment - absolutely not. To achieve such minimal execution time, the compile time is humungus: on my Ryzen 7 3700X system it takes around 345 minutes! Moreover - this is achievable only using proper compiler - clang has gave up at the point before even can_construct_2 was fully calculated. This project should be considered as some kind of proof of concept rather than anything useful.

meme of Homer comparing execution time vs compilation time

Requirements

This code at this moment couldn't be compiled with clang. The only compiler which allows to do such big job at compile time is GCC. In order to prepare input data to be processed at compile time, a small trick with generating an intermediate input file must be done via CMake, thus please don't ignore the first step from the "Building and running" section.

Requirements:

  • GCC 12 (earlier versions were not tested, thus I don't recommend trying - unless your machine has some working hours to waste; that being said - earlier versions could work, as I abandoned pretty much all newest constexpr stuff during the evolution of this experiment),
  • CMake,
  • make,
  • recommended at least 16 GiB of RAM,
  • Linux environment (other OSes should be also ok, as long as you would have available GCC and CMake; building instructions were written using Ubuntu).

Building and running

First, use CMake to generate the intermediate input file and makefile:

mkdir build_constexpr
cd build_constexpr
cmake -DCMAKE_BUILD_TYPE=Release ..

Now you're ready to fire the compilation torture. Grab your favorite cup of coffee, cast the making spell and watch the dead terminal for about five to six hours:

make constexpr

The compilation process is single threaded - it has to be sequential, as later computations depend on the earlier ones, so throwing more jobs (with make -j500 constexpr) will make no difference.

This will produce the constexpr executable. Measuring its performance via time command is starting to be pointless, given the low precision, so the binary has built-in timer for measuring its own execution time within the nanoseconds' resolution. The output of the application will be saved to the solutions.txt file.

An example output from the application run:

draghan@fortress:~/programming/five_words_constexpr/build_constexpr$ ./constexprTotal time: 194269ns (194us)draghan@fortress:~/programming/five_words_constexpr/build_constexpr$ head solutions.txt &&echo ... && tail solutions.txt && cat ./solutions.txt | wcbawke fldxt gconv jimpy qurshbejig fldxt nymph quack vrowsevang fldxt jumby qophs wrickexptl fconv gawby hdqrs mujikexptl gconv hdqrs jumby wakifexpwy flack hdqrs jumbo vingtexpwy flock hdqrs jumba vingtfldxt gconv herbs jimpy quawkfldxt gconv jerky saqib whumpfldxt gconv jerky squib whamp...fldxt nymph squib vejoz wrackbrock japyx seqwl vingt zhmudglack hdqrs jowpy muntz vibexbrigs fldxt nymph quawk vejozampyx flung hdqrs twick vejozfldxt gconv jerky squiz whampfldxt grimp quawk synch vejozfldxt gryph manqu swick vejozfldxt nymph quags vejoz wrickfrock japyx seqwl vingt zhmud538 2690 16678

The CMake project has also defined the original target, to build the original code written by @ilyanikolaevsky - this could be useful for validating the results.

constexpr fun

At a glance, the idea to use the constexpr feature of C++ looks brilliant, but there are lots of restrictions of what operations and calculations could be done in compile time, not to mention ENORMOUS compilation time.

Not a long ago only very simple stuff could be executed at compile time - some simple expressions like 2+2 and that would be it. Only recently the C++ standard and compilers allow to do more advanced computations in the compile time. To play with this project I went with compiling the newest available GCC and newest available Clang, as I was trying to use newest parts of C++ standard library available for the constexpr context, e.g. std::bitset, which wasn't available in any "stable" release. The process of getting this to work was not easy - this feature of C++ is not meant and is not ready for such extensive string processing, at least at this point in time.

Some lesson learned during this experiment:

  • can't read any file at the compilation time (there is some proposal which allows embedding data to the application, but it's not yet incorporated into the standard): the only option at this moment is the #include directive,
  • there is no way in the standard C++ to include an external file's content into a string to be processed later: thus the only dirty trick in this project with CMake, which generates an intermediate input file; all it does is putting in the begining and the end of the file C++'s raw multiline literal markups (R"( and )") - in this form it could be safely included into a C++ source file,
  • better to avoid using any containers from C++ std::lib: the cost of compilation time and memory footprint is insanely big, compared to the plain arrays,
  • the compiler was sometimes complaining about modifying anything passed by a reference to the consteval function,
  • you can't return an array from the function, but you can return a struct containing array,
  • if you don't use dynamically sized container, you have to use statically sized array (BTW. much better to use plain array than std::array - huge memory savings there),
  • considering the above: you must know the size of your results before you actually compute the results; this seems like a paradox, but the solution is painfully simple: you must do your computations twice - first time in order to count how big your results will be, and second time doing the actual job with assigning and returning your results,
  • better not to place big constexpr evaluated array inside a regular runtime function, as this will most likely corrupt the runtime stack,
  • clang is doing consteval calculations much faster than GCC, but has lower limit of consteval operations' count,
  • writing chars to a file is about 2x slower with C++'s std::ofstream than with C's fputc (at least in my environment).

About

A non-practical solution to the problem of finding five English words with 25 distinct characters, using a constexpr technique.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages