2
0
Эх сурвалжийг харах

Opimization of mask, bitfieldInsert, bitfieldExtract and bitCount

Christophe Riccio 11 жил өмнө
parent
commit
0c522117c8

+ 13 - 24
glm/detail/func_integer.inl

@@ -39,8 +39,15 @@
 #endif//(GLM_ARCH != GLM_ARCH_PURE)
 #include <limits>
 
-namespace glm
+namespace glm{
+namespace detail
 {
+	GLM_FUNC_QUALIFIER int mask(int Bits)
+	{
+		return Bits >= 32 ? 0xffffffff : (static_cast<int>(1) << Bits) - static_cast<int>(1);
+	}
+}//namespace detail
+
 	// uaddCarry
 	GLM_FUNC_QUALIFIER uint uaddCarry(uint const & x, uint const & y, uint & Carry)
 	{
@@ -139,16 +146,8 @@ namespace glm
 	{
 		GLM_STATIC_ASSERT(std::numeric_limits<T>::is_integer, "'bitfieldExtract' only accept integer inputs");
 
-		int GenSize = int(sizeof(T)) << int(3);
-
-		assert(Offset + Bits <= GenSize);
-
-		vecType<T, P> ShiftLeft(0);
-		if(Bits)
-			ShiftLeft = Value << static_cast<T>(GenSize - (Bits + Offset));
-		vecType<T, P> const ShiftBack = ShiftLeft >> static_cast<T>(GenSize - Bits);
-
-		return ShiftBack;
+		int const Mask = detail::mask(Bits);
+		return (Value >> static_cast<T>(Offset)) & static_cast<T>(Mask);
 	}
 
 	// bitfieldInsert
@@ -163,13 +162,7 @@ namespace glm
 	{
 		GLM_STATIC_ASSERT(std::numeric_limits<T>::is_integer, "'bitfieldInsert' only accept integer values");
 
-		if(Bits == 0)
-			return Base;
-
-		vecType<T, P> Mask(0);
-		for(int Bit = Offset; Bit < Offset + Bits; ++Bit)
-			Mask |= (static_cast<int>(1) << Bit);
-
+		T Mask = static_cast<T>(detail::mask(Bits) << Offset);
 		return (Base & ~Mask) | (Insert & Mask);
 	}
 
@@ -186,7 +179,6 @@ namespace glm
 		GLM_STATIC_ASSERT(std::numeric_limits<T>::is_integer, "'bitfieldReverse' only accept integer values");
 
 		vecType<T, P> Result(0);
-		vecType<T, P> const Null(0);
 		T const BitSize = static_cast<T>(sizeof(T) * 8);
 		for(T i = 0; i < BitSize; ++i)
 		{
@@ -210,11 +202,8 @@ namespace glm
 		GLM_STATIC_ASSERT(std::numeric_limits<T>::is_integer, "'bitCount' only accept integer values");
 
 		vecType<int, P> Count(0);
-		for(std::size_t i = 0; i < sizeof(T) * std::size_t(8); ++i)
-		{
-			if(v & (static_cast<T>(1) << i))
-				++Count;
-		}
+		for(T i = 0, n = static_cast<T>(sizeof(T) * 8); i < n; ++i)
+			Count += vecType<int, P>((v >> i) & static_cast<T>(1));
 		return Count;
 	}
 

+ 5 - 2
glm/gtx/bit.hpp

@@ -41,6 +41,7 @@
 // Dependencies
 #include "../detail/type_int.hpp"
 #include "../detail/setup.hpp"
+#include "../detail/precision.hpp"
 #include <cstddef>
 
 #if(defined(GLM_MESSAGES) && !defined(GLM_EXT_INCLUDED))
@@ -54,8 +55,10 @@ namespace glm
 
 	/// Build a mask of 'count' bits
 	/// @see gtx_bit
-	template <typename genIType>
-	GLM_FUNC_DECL genIType mask(genIType const & count);
+	GLM_FUNC_DECL int mask(int Bits);
+
+	template <precision P, template <typename, precision> class vecType>
+	GLM_FUNC_DECL vecType<int, P> mask(vecType<int, P> const & v);
 
 	//! Find the highest bit set to 1 in a integer variable and return its value. 
 	/// @see gtx_bit

+ 7 - 7
glm/gtx/bit.inl

@@ -12,16 +12,16 @@
 
 namespace glm
 {
-	template <typename genIType>
-	GLM_FUNC_QUALIFIER genIType mask
-	(
-		genIType const & count
-	)
+	GLM_FUNC_QUALIFIER int mask(int Bits)
 	{
-		return ((genIType(1) << (count)) - genIType(1));
+		return Bits >= sizeof(Bits) * 8 ? ~static_cast<int>(0) : (static_cast<int>(1) << Bits) - static_cast<int>(1);
 	}
 
-	VECTORIZE_VEC(mask)
+	template <precision P, template <typename, precision> class vecType>
+	GLM_FUNC_QUALIFIER vecType<int, P> mask(vecType<int, P> const & v)
+	{
+		return detail::functor1<int, int, P, vecType>::call(mask, v);
+	}
 
 	// highestBitValue
 	template <typename genType>

+ 1 - 0
readme.txt

@@ -76,6 +76,7 @@ GLM 0.9.6.0: 2014-XX-XX
 - Added GLM_FORCE_NO_CTOR_INIT
 - Added 'uninitialize' to explicitly not initialize a GLM type
 - Added not function (from GLSL specification) on VC12
+- Optimized bitfield operations
 
 ================================================================================
 GLM 0.9.5.4: 2014-06-21

+ 135 - 25
test/core/core_func_integer.cpp

@@ -10,6 +10,8 @@
 #include <glm/integer.hpp>
 #include <glm/gtc/vec1.hpp>
 #include <iostream>
+#include <vector>
+#include <ctime>
 
 enum result
 {
@@ -29,18 +31,22 @@ namespace bitfieldInsert
 		sizeType	Offset;
 		sizeType	Bits;
 		genType		Return;
-		result		Result;
 	};
 
 	typedef type<glm::uint, glm::uint> typeU32;
 
 	typeU32 const Data32[] =
 	{
-		{0xffffffff, 8,24, 0xffffff00, SUCCESS},
+		{0xff000000, 0x0000ff00,  8,  8, 0xff00ff00},
+		{0xffff0000, 0x0000ffff, 16, 16, 0x00000000},
+		{0x0000ffff, 0xffff0000, 16, 16, 0xffffffff},
+		{0x00000000, 0xffffffff,  0, 32, 0xffffffff},
+		{0x00000000, 0xffffffff,  0,  0, 0x00000000}
 	};
 
 	int test()
 	{
+		int Error = 0;
 		glm::uint count = sizeof(Data32) / sizeof(typeU32);
 		
 		for(glm::uint i = 0; i < count; ++i)
@@ -50,19 +56,11 @@ namespace bitfieldInsert
 				Data32[i].Insert,
 				Data32[i].Offset,
 				Data32[i].Bits);
-			
-			bool Compare = Data32[i].Return == Return;
-			
-			if(Data32[i].Result == SUCCESS && Compare)
-				continue;
-			else if(Data32[i].Result == FAIL && !Compare)
-				continue;
-			
-			std::cout << "glm::bitfieldInsert test fail on test " << i << std::endl;
-			return 1;
+
+			Error += Data32[i].Return == Return ? 0 : 1;
 		}
 		
-		return 0;
+		return Error;
 	}
 }//bitfieldInsert
 
@@ -72,8 +70,8 @@ namespace bitfieldExtract
 	struct type
 	{
 		genType		Value;
-		sizeType	BitFirst;
-		sizeType	BitCount;
+		sizeType	Offset;
+		sizeType	Bits;
 		genType		Return;
 		result		Result;
 	};
@@ -82,9 +80,9 @@ namespace bitfieldExtract
 
 	typeU32 const Data32[] =
 	{
+		{0xffffffff, 0,32, 0xffffffff, SUCCESS},
 		{0xffffffff, 8, 0, 0x00000000, SUCCESS},
 		{0x00000000, 0,32, 0x00000000, SUCCESS},
-		{0xffffffff, 0,32, 0xffffffff, SUCCESS},
 		{0x0f0f0f0f, 0,32, 0x0f0f0f0f, SUCCESS},
 		{0x00000000, 8, 0, 0x00000000, SUCCESS},
 		{0x80000000,31, 1, 0x00000001, SUCCESS},
@@ -106,27 +104,28 @@ namespace bitfieldExtract
 
 	int test()
 	{
+		int Error = 0;
+
 		glm::uint count = sizeof(Data32) / sizeof(typeU32);
-		
+
 		for(glm::uint i = 0; i < count; ++i)
 		{
 			glm::uint Return = glm::bitfieldExtract(
 				Data32[i].Value, 
-				Data32[i].BitFirst, 
-				Data32[i].BitCount);
+				Data32[i].Offset, 
+				Data32[i].Bits);
 			
 			bool Compare = Data32[i].Return == Return;
-			
+
 			if(Data32[i].Result == SUCCESS && Compare)
 				continue;
 			else if(Data32[i].Result == FAIL && !Compare)
 				continue;
-			
-			std::cout << "glm::bitfieldExtract test fail on test " << i << std::endl;
-			return 1;
+
+			Error += 1;
 		}
-		
-		return 0;
+
+		return Error;
 	}
 }//extractField
 
@@ -517,6 +516,115 @@ namespace imulExtended
 	}
 }//namespace imulExtended
 
+namespace bitCount
+{
+	template <typename genType>
+	struct type
+	{
+		genType		Value;
+		genType		Return;
+	};
+
+	type<int> const DataI32[] =
+	{
+		{0x00000001,  1},
+		{0x00000003,  2},
+		{0x00000002,  1},
+		{0xffffffff, 32},
+		{0x00000000,  0}
+	};
+
+	template <typename T>
+	int bitCount_if(T v)
+	{
+		GLM_STATIC_ASSERT(std::numeric_limits<T>::is_integer, "'bitCount' only accept integer values");
+
+		int Count(0);
+		for(T i = 0, n = static_cast<T>(sizeof(T) * 8); i < n; ++i)
+		{
+			if(v & static_cast<T>(1 << i))
+				++Count;
+		}
+		return Count;
+	}
+
+	template <typename T>
+	int bitCount_vec(T v)
+	{
+		GLM_STATIC_ASSERT(std::numeric_limits<T>::is_integer, "'bitCount' only accept integer values");
+
+		int Count(0);
+		for(T i = 0, n = static_cast<T>(sizeof(T) * 8); i < n; ++i)
+		{
+			Count += static_cast<int>((v >> i) & static_cast<T>(1));
+		}
+		return Count;
+	}
+
+	int perf()
+	{
+		int Error(0);
+
+		std::size_t Size = 10000000;
+
+		std::clock_t TimestampsA = std::clock();
+
+		{
+			std::vector<int> v;
+			v.resize(Size);
+
+			for(std::size_t i = 0, n = v.size(); i < n; ++i)
+				v[i] = bitCount_if(i);
+		}
+
+		std::clock_t TimestampsB = std::clock();
+
+		{
+			std::vector<int> v;
+			v.resize(Size);
+
+			for(std::size_t i = 0, n = v.size(); i < n; ++i)
+				v[i] = bitCount_vec(i);
+		}
+
+		std::clock_t TimestampsC = std::clock();
+
+		{
+			std::vector<int> v;
+			v.resize(Size);
+
+			for(std::size_t i = 0, n = v.size(); i < n; ++i)
+				v[i] = glm::bitCount(i);
+		}
+
+		std::clock_t TimestampsD = std::clock();
+
+		std::clock_t TimeIf = TimestampsB - TimestampsA;
+		std::clock_t TimeVec = TimestampsC - TimestampsB;
+		std::clock_t TimeDefault = TimestampsD - TimestampsC;
+
+		printf("TimeIf %d\n", TimeIf);
+		printf("TimeVec %d\n", TimeVec);
+		printf("TimeDefault %d\n", TimeDefault);
+
+		return Error;
+	}
+
+	int test()
+	{
+		int Error(0);
+
+		for(std::size_t i = 0, n = sizeof(DataI32) / sizeof(type<int>); i < n; ++i)
+		{
+			int Result = glm::bitCount(DataI32[i].Value);
+			Error += DataI32[i].Return == Result ? 0 : 1;
+			assert(!Error);
+		}
+
+		return Error;
+	}
+}//bitCount
+
 int main()
 {
 	int Error = 0;
@@ -530,6 +638,8 @@ int main()
 	Error += ::bitfieldInsert::test();
 	Error += ::bitfieldExtract::test();
 	Error += ::bitfieldReverse::test();
+	Error += ::bitCount::test();
+	Error += ::bitCount::perf();
 	Error += ::findMSB::test();
 	Error += ::findLSB::test();
 

+ 61 - 0
test/gtx/gtx_bit.cpp

@@ -501,10 +501,71 @@ namespace bitfieldInterleave4
 	}
 }
 
+namespace mask
+{
+	inline int mask_mix(int Bits)
+	{
+		return Bits >= 32 ? 0xffffffff : (static_cast<int>(1) << Bits) - static_cast<int>(1);
+	}
+
+	inline int mask_loop(int Bits)
+	{
+		int Mask = 0;
+		for(int Bit = 0; Bit < Bits; ++Bit)
+			Mask |= (static_cast<int>(1) << Bit);
+		return Mask;
+	}
+
+	int perf()
+	{
+		int const Count = 100000000;
+
+		std::clock_t Timestamp1 = std::clock();
+
+		{
+			std::vector<int> Mask;
+			Mask.resize(Count);
+			for(int i = 0; i < Count; ++i)
+				Mask[i] = mask_mix(i % 32);
+		}
+
+		std::clock_t Timestamp2 = std::clock();
+
+		{
+			std::vector<int> Mask;
+			Mask.resize(Count);
+			for(int i = 0; i < Count; ++i)
+				Mask[i] = mask_loop(i % 32);
+		}
+
+		std::clock_t Timestamp3 = std::clock();
+
+		{
+			std::vector<int> Mask;
+			Mask.resize(Count);
+			for(int i = 0; i < Count; ++i)
+				Mask[i] = glm::mask(i % 32);
+		}
+
+		std::clock_t Timestamp4 = std::clock();
+
+		std::clock_t TimeMix = Timestamp2 - Timestamp1;
+		std::clock_t TimeLoop = Timestamp3 - Timestamp2;
+		std::clock_t TimeDefault = Timestamp4 - Timestamp3;
+
+		printf("mask[mix]: %d\n", TimeMix);
+		printf("mask[loop]: %d\n", TimeLoop);
+		printf("mask[default]: %d\n", TimeDefault);
+
+		return TimeDefault < TimeLoop ? 0 : 1;
+	}
+}//namespace mask
+
 int main()
 {
 	int Error(0);
 
+	Error += ::mask::perf();
 	Error += ::bitfieldInterleave3::test();
 	Error += ::bitfieldInterleave4::test();
 	Error += ::bitfieldInterleave::test();