diff foosdk/sdk/foobar2000/SDK/foosort.cpp @ 1:20d02a178406 default tip

*: check in everything else yay
author Paper <paper@tflc.us>
date Mon, 05 Jan 2026 02:15:46 -0500
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/foosdk/sdk/foobar2000/SDK/foosort.cpp	Mon Jan 05 02:15:46 2026 -0500
@@ -0,0 +1,162 @@
+#include "foobar2000-sdk-pch.h"
+#include "foosort.h"
+#include "threadPool.h"
+#include "genrand.h"
+
+#define FOOSORT_PROFILING 0
+
+namespace {
+#if FOOSORT_PROFILING
+	typedef pfc::hires_timer foosort_timer;
+#endif
+
+	class foosort {
+	public:
+		foosort(pfc::sort_callback & cb, abort_callback & a) : m_abort(a), p_callback(cb) {}
+
+		foosort fork() {
+			foosort ret ( * this );
+			ret.genrand = genrand_service::g_create();
+			return ret;
+		}
+
+		size_t myrand(size_t cnt) {
+			PFC_ASSERT(cnt == (uint32_t)cnt);
+			return genrand->genrand((uint32_t)cnt);
+		}
+
+		void squaresort(t_size const p_base, t_size const p_count) {
+			const t_size max = p_base + p_count;
+			for (t_size walk = p_base + 1; walk < max; ++walk) {
+				for (t_size prev = p_base; prev < walk; ++prev) {
+					p_callback.swap_check(prev, walk);
+				}
+			}
+		}
+
+
+		void _sort_2elem_helper(t_size & p_elem1, t_size & p_elem2) {
+			if (p_callback.compare(p_elem1, p_elem2) > 0) pfc::swap_t(p_elem1, p_elem2);
+		}
+
+		t_size _pivot_helper(t_size const p_base, t_size const p_count) {
+			PFC_ASSERT(p_count > 2);
+
+			//t_size val1 = p_base, val2 = p_base + (p_count / 2), val3 = p_base + (p_count - 1);
+
+			t_size val1 = myrand(p_count), val2 = myrand(p_count - 1), val3 = myrand(p_count - 2);
+			if (val2 >= val1) val2++;
+			if (val3 >= val1) val3++;
+			if (val3 >= val2) val3++;
+
+			val1 += p_base; val2 += p_base; val3 += p_base;
+
+			_sort_2elem_helper(val1, val2);
+			_sort_2elem_helper(val1, val3);
+			_sort_2elem_helper(val2, val3);
+
+			return val2;
+		}
+
+		void newsort(t_size const p_base, t_size const p_count, size_t concurrency) {
+			if (p_count <= 4) {
+				squaresort(p_base, p_count);
+				return;
+			}
+			
+			this->m_abort.check();
+#if FOOSORT_PROFILING
+			foosort_timer t;
+			if ( concurrency > 1 ) t.start();
+#endif
+
+			t_size pivot = _pivot_helper(p_base, p_count);
+
+			{
+				const t_size target = p_base + p_count - 1;
+				if (pivot != target) {
+					p_callback.swap(pivot, target); pivot = target;
+				}
+			}
+
+
+			t_size partition = p_base;
+			{
+				bool asdf = false;
+				for (t_size walk = p_base; walk < pivot; ++walk) {
+					const int comp = p_callback.compare(walk, pivot);
+					bool trigger = false;
+					if (comp == 0) {
+						trigger = asdf;
+						asdf = !asdf;
+					} else if (comp < 0) {
+						trigger = true;
+					}
+					if (trigger) {
+						if (partition != walk) p_callback.swap(partition, walk);
+						partition++;
+					}
+				}
+			}
+			if (pivot != partition) {
+				p_callback.swap(pivot, partition); pivot = partition;
+			}
+
+			const auto base1 = p_base, count1 = pivot - p_base;
+			const auto base2 = pivot + 1, count2 = p_count - (pivot + 1 - p_base);
+
+			if (concurrency > 1) {
+
+				size_t total = count1 + count2;
+				size_t con1 = (size_t)((uint64_t)count1 * (uint64_t)concurrency / (uint64_t)total);
+				if (con1 < 1) con1 = 1;
+				if (con1 > concurrency - 1) con1 = concurrency - 1;
+				size_t con2 = concurrency - con1;
+
+#if FOOSORT_PROFILING
+				FB2K_console_formatter() << "foosort pass: " << p_base << "+" << p_count << "(" << concurrency << ") took " << t.queryString();
+				FB2K_console_formatter() << "foosort forking: " << base1 << "+" << count1 << "(" << con1 << ") + " << base2 << "+" << count2 << "(" << con2 << ")";
+#endif
+				pfc::counter cnt;
+				fb2k::cpuThreadPool::runMultiHelper([&] {
+					try {
+						switch (cnt++) {
+						case 0:
+							{ foosort subsort = fork(); subsort.newsort(base1, count1, con1); }
+							break;
+						case 1:
+							newsort(base2, count2, con2);
+							break;
+						}
+					} catch (exception_aborted const &) {}
+				}, 2);
+				m_abort.check();
+			} else {
+				newsort(base1, count1, 1);
+				newsort(base2, count2, 1);
+			}
+		}
+	private:
+		abort_callback & m_abort;
+		pfc::sort_callback & p_callback;
+		genrand_service::ptr genrand = genrand_service::g_create();
+	};
+
+
+
+}
+namespace fb2k {
+	void sort(pfc::sort_callback & cb, size_t count, size_t concurrency, abort_callback & aborter) {
+#ifdef FOOSORT_LIMIT_THREADS
+		if (concurrency > FOOSORT_LIMIT_THREADS) concurrency = FOOSORT_LIMIT_THREADS;
+#endif
+#if FOOSORT_PROFILING
+		foosort_timer t; t.start();
+#endif
+		foosort theFooSort(cb, aborter);
+		theFooSort.newsort(0, count, concurrency);
+#if FOOSORT_PROFILING
+		FB2K_console_formatter() << "foosort took: " << t.queryString();
+#endif
+	}
+}