fork download
  1. //=============================================================================
  2. // performance.h --------------------------------------------------------------
  3.  
  4. #include <stdio.h>
  5. #include <stdlib.h>
  6. #include <string.h>
  7. #include <algorithm>
  8.  
  9. #define CSTR // constructor place holder
  10. #define DSTR // destructor place holder
  11. #define _____ // place holder
  12.  
  13. using i8 = char;
  14. using u8 = unsigned char;
  15. using i16 = short;
  16. using u16 = unsigned short;
  17. using i32 = int;
  18. using i64 = long long;
  19. using u32 = unsigned int;
  20. using u64 = unsigned long long;
  21. using f32 = float;
  22. using f64 = double;
  23.  
  24. #ifdef _MSC_VER
  25. #include <intrin.h>
  26. #else
  27. #include <x86intrin.h>
  28. #define CHAR_BIT 8
  29. #endif
  30.  
  31. #define SYS_BITS ( sizeof( size_t ) * CHAR_BIT )
  32.  
  33. //-----------------------------------------------------------------------------
  34.  
  35. class RUNNER
  36. {
  37. private:
  38. const char* function_name;
  39. u64 elapsed;
  40.  
  41. protected:
  42. bool is_success;
  43.  
  44. public:
  45. CSTR RUNNER
  46. (
  47. const char* function_name,
  48. void( *on_pre )( ),
  49. void( *on_run )( ),
  50. bool( *on_post )( )
  51. )
  52. : function_name( function_name )
  53. , on_pre( on_pre )
  54. , on_run( on_run )
  55. , on_post( on_post )
  56. {
  57. init();
  58. }
  59. DSTR virtual ~RUNNER()
  60. {};
  61.  
  62. virtual void init()
  63. {
  64. elapsed = -1;
  65. is_success = on_run != NULL;
  66. }
  67.  
  68. void( *on_pre )( );
  69. void( *on_run )( );
  70. bool( *on_post )( );
  71.  
  72. void check()
  73. {
  74. if( !on_run )
  75. return;
  76. if( on_pre )
  77. on_pre();
  78.  
  79. u64 begin = __rdtsc();
  80. on_run();
  81. u64 duration = __rdtsc() - begin;
  82. elapsed = std::min( elapsed, duration );
  83.  
  84. if( on_post )
  85. is_success &= on_post();
  86. }
  87. const auto set_length( const char* str, const size_t size )
  88. {
  89. static char temp[ 80 ];
  90. size_t index;
  91. for( index = 0; str[ index ]; ++index ) temp[ index ] = str[ index ];
  92. for( ; index < size; ++index ) temp[ index ] = ' ';
  93. temp[ index ] = 0;
  94. return temp;
  95. }
  96. const auto report()
  97. {
  98. const char* judge_strings[] = { " FAILED ", " PASSED " };
  99. static char temp[ 80 ];
  100. sprintf( temp, "[%s] %s %15llu clocks", judge_strings[ is_success ],
  101. set_length( function_name, 20 ), elapsed );
  102. return temp;
  103. }
  104. const auto elapsed_clocks()
  105. {
  106. return elapsed;
  107. }
  108. const auto name()
  109. {
  110. return function_name;
  111. }
  112.  
  113. };
  114. //-----------------------------------------------------------------------------
  115.  
  116. #include <vector>
  117. template < size_t REPEAT_COUNT = 1 >
  118. class BENCH
  119. {
  120. private:
  121. std::vector< RUNNER* > runner_list;
  122.  
  123. public:
  124. CSTR BENCH()
  125. {};
  126. DSTR ~BENCH()
  127. {};
  128.  
  129. void add( RUNNER* runner )
  130. {
  131. runner_list.push_back( runner );
  132. }
  133.  
  134. void run()
  135. {
  136. if( runner_list.size() == 0 ) return;
  137. printf( "< %d bits > %d trials", (int)SYS_BITS, (int)REPEAT_COUNT );
  138. puts( "" );
  139. puts( "-------------------------------------------------------------" );
  140. puts( "| CHECKER | Function name | minimum clocks |" );
  141. puts( "-------------------------------------------------------------" );
  142. u64 min_value = -1;
  143. u64 max_value = 0;
  144. u32 min_index = 0;
  145. u32 max_index = 0;
  146. for( u32 test_case = 0; test_case < runner_list.size(); ++test_case )
  147. {
  148. runner_list[ test_case ]->init();
  149. for( u32 test_count = 0; test_count < REPEAT_COUNT; ++test_count )
  150. runner_list[ test_case ]->check();
  151. puts( runner_list[ test_case ]->report() );
  152. if( min_value > runner_list[ test_case ]->elapsed_clocks() )
  153. {
  154. min_index = test_case;
  155. min_value = runner_list[ test_case ]->elapsed_clocks();
  156. }
  157. if( max_value < runner_list[ test_case ]->elapsed_clocks() )
  158. {
  159. max_index = test_case;
  160. max_value = runner_list[ test_case ]->elapsed_clocks();
  161. }
  162. }
  163. puts( "-------------------------------------------------------------" );
  164. printf( "Winner is %s ( %.2f times faster than loooser )",
  165. runner_list[ min_index ]->name(),
  166. float( runner_list[ max_index ]->elapsed_clocks() ) /
  167. runner_list[ min_index ]->elapsed_clocks() );
  168. puts( "" );
  169. }
  170. };
  171. //-----------------------------------------------------------------------------
  172.  
  173. #define COOL_FUN( function_name, ... ) \
  174.   new RUNNER( #function_name, nil, []{ function_name(__VA_ARGS__); }, nil )
  175.  
  176. #define SO_FUN( pre, function_name, ... ) \
  177.   new RUNNER( #function_name, pre, []{ function_name(__VA_ARGS__); }, nil )
  178.  
  179. #define FUN( pre, run, post, function_name ) \
  180.   new RUNNER( #function_name, pre, run, post )
  181.  
  182. //=============================================================================
  183. // kukyakya
  184.  
  185. #include <memory>
  186. #include <cassert>
  187.  
  188. using byte = char;
  189. using word = std::size_t;
  190.  
  191. word merge_word( const word& w1, const word& w2, std::size_t byte_offset )
  192. {
  193. assert( byte_offset && ( byte_offset < sizeof( word ) ) );
  194.  
  195. const std::size_t shift_1 = CHAR_BIT * byte_offset;
  196. const std::size_t shift_2 = CHAR_BIT * sizeof( word ) - shift_1;
  197.  
  198. #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
  199. return ( w1 >> shift_1 ) | ( w2 << shift_2 );
  200. #else
  201. return ( w1 << shift_1 ) | ( w2 >> shift_2 );
  202. #endif // __BYTE_ORDER
  203. }
  204.  
  205. std::size_t get_byte_offset( const word* ptr )
  206. {
  207. void *p = (void*)ptr;
  208. std::size_t sz = -1;
  209.  
  210. std::align( alignof( word ), 0, p, sz );
  211.  
  212. const auto diff = (char*)p - (char*)ptr;
  213.  
  214. return ( diff == 0 ) ? 0 : ( sizeof( word ) - diff );
  215. }
  216.  
  217. byte* copy_byte( byte* dest, const byte* src, std::size_t n_byte )
  218. {
  219. // Don't fuck with loop-unrolling, just trust your compiler
  220. while( n_byte-- )
  221. *dest++ = *src++;
  222.  
  223. return dest;
  224. }
  225.  
  226. word* copy_word_both_aligned( word* dest, const word* src, std::size_t n_word )
  227. {
  228. /* */ word* d = (word*)dest;
  229. const word* s = (const word*)src;
  230. while( n_word-- )
  231. *d++ = *s++;
  232.  
  233. return dest;
  234. }
  235.  
  236. // src is not aligned
  237. word* copy_word_dest_aligned( word* dest, const word* src, std::size_t n_word )
  238. {
  239. word *d = dest;
  240. const word *s = src;
  241.  
  242. // get byte-offset of src
  243. std::size_t src_offset = get_byte_offset( s );
  244.  
  245. // if src is aligned, call copy_word_both_aligned
  246. if( src_offset == 0 )
  247. {
  248. return copy_word_both_aligned( d, s, n_word );
  249. }
  250.  
  251. const word* aligned_src = (const word*)( (char*)s - src_offset );
  252. assert( get_byte_offset( aligned_src ) == 0 );
  253.  
  254. word buf = *aligned_src++;
  255. while( n_word-- )
  256. {
  257. word buf2 = *aligned_src++;
  258. *d++ = merge_word( buf, buf2, src_offset );
  259. buf = buf2;
  260. }
  261.  
  262. return dest;
  263. }
  264.  
  265. void *kukyakya_memcpy( void* dest, const void* src, std::size_t n )
  266. {
  267. /* */ void* d = dest;
  268. const void* s = src;
  269. std::size_t sz = n;
  270.  
  271. if( !std::align( alignof( word ), 0, d, sz ) )
  272. {
  273. return copy_byte( (byte*)dest, (const byte*)src, n );
  274. }
  275. s = (const byte*)s + ( n - sz );
  276.  
  277. // byte copy until dest is aligned
  278. copy_byte( (byte*)dest, (const byte*)src, n - sz );
  279.  
  280. // copy words
  281. const std::size_t n_word = sz / sizeof( word );
  282. copy_word_dest_aligned( (word*)d, (const word*)s, n_word );
  283. d = /* */ (word*)d + n_word;
  284. s = (const word*)s + n_word;
  285. sz -= sizeof( word ) * n_word;
  286.  
  287. // copy remaining bytes
  288. copy_byte( (byte*)d, (const byte*)s, sz );
  289.  
  290. return dest;
  291. }
  292.  
  293. void *kukyakya_memcpy_unaligned( void* dest, const void* src, std::size_t n_byte )
  294. {
  295. /* */ word *d = (/* */ word*)dest;
  296. const word* s = (const word*)src;
  297.  
  298. // copy word
  299. const std::size_t n_word = n_byte / sizeof( word );
  300. copy_word_both_aligned( d, s, n_word );
  301.  
  302. d += n_word;
  303. s += n_word;
  304. n_byte -= n_word * sizeof( word );
  305.  
  306. // copy remaining bytes
  307. copy_byte( (byte*)d, (const byte*)s, n_byte );
  308.  
  309. return dest;
  310. }
  311.  
  312. void *my_memcpy_byte_only( void* dest, const void* src, std::size_t n )
  313. {
  314. return copy_byte( (byte*)dest, (const byte*)src, n );
  315. }
  316. //-----------------------------------------------------------------------------
  317.  
  318. void* codesafer_memcpy_unaligned1( void* dst, const void* src, std::size_t size )
  319. {
  320. using step_t = u32;
  321.  
  322. _____ step_t* d = (step_t*)dst;
  323. const step_t* s = (step_t*)src;
  324. const std::size_t step_count = size / sizeof(step_t);
  325. const u32 off_road = size % sizeof(step_t);
  326.  
  327. for( std::size_t i = 0; i < step_count; ++i )
  328. d[i] = s[i];
  329.  
  330. u8* db = (u8*)( d + step_count );
  331. u8* sb = (u8*)( s + step_count );
  332.  
  333. for( u32 i = 0; i < off_road; ++i )
  334. db[i] = sb[i];
  335.  
  336. return dst;
  337. }
  338. //=============================================================================
  339. // test case
  340.  
  341. const int test_count = 1000;
  342. const int test_size = 1000000;
  343.  
  344. #ifdef _MSC_VER
  345. __declspec( align( 4 ) ) char src[ test_size + 4 ];
  346. __declspec( align( 4 ) ) char dst[ test_size + 4 ];
  347. #else
  348. char src[ test_size + 4 ] __attribute__((aligned(4)));
  349. char dst[ test_size + 4 ] __attribute__((aligned(4)));
  350. #endif
  351.  
  352. int result1, result2, result3, result4;
  353.  
  354. void init()
  355. {
  356. }
  357.  
  358. void pre()
  359. {
  360. }
  361.  
  362. void test1()
  363. {
  364. result1 += (int)kukyakya_memcpy( dst + 1, src + 3, test_size );
  365. }
  366.  
  367. void test2()
  368. {
  369. result2 += (int)kukyakya_memcpy_unaligned( dst + 1, src + 3, test_size );
  370. }
  371.  
  372. void test3()
  373. {
  374. result3 += (int)memcpy( dst + 1, src + 3, test_size );
  375. }
  376.  
  377. void test4()
  378. {
  379. result4 += (int)codesafer_memcpy_unaligned1( dst + 1, src + 3, test_size );
  380. }
  381.  
  382. bool post()
  383. {
  384. return true;
  385. }
  386. //-----------------------------------------------------------------------------
  387.  
  388. #include <iostream>
  389.  
  390. using namespace std;
  391.  
  392. int main()
  393. {
  394. init();
  395. BENCH< test_count > bench;
  396. bench.add( FUN( pre, test1, post, kukyakya_aligned ) );
  397. bench.add( FUN( pre, test2, post, kukyakya_unaligned ) );
  398. bench.add( FUN( pre, test3, post, std_memcpy ) );
  399. bench.add( FUN( pre, test4, post, codesafer_unaligned1 ) );
  400. bench.run();
  401.  
  402. getchar();
  403. return 0;
  404. }
  405. //-----------------------------------------------------------------------------
  406.  
Success #stdin #stdout 3.5s 5420KB
stdin
Standard input is empty
stdout
< 32 bits > 1000 trials
-------------------------------------------------------------
|  CHECKER      | Function  name      |   minimum  clocks   |
-------------------------------------------------------------
[        PASSED ] kukyakya_aligned             2212883 clocks
[        PASSED ] kukyakya_unaligned           1644863 clocks
[        PASSED ] std_memcpy                   1476585 clocks
[        PASSED ] codesafer_unaligned1         1525716 clocks
-------------------------------------------------------------
Winner is std_memcpy  ( 1.50 times faster than loooser )