1
0

q8dot.cpp 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. #include <cstdio>
  2. #include <type_traits>
  3. #include <vector>
  4. #include <random>
  5. #include <chrono>
  6. #include <cstdlib>
  7. #include <cmath>
  8. #include <cassert>
  9. #include <cstring>
  10. #include <array>
  11. #include <type_traits>
  12. #include <ggml.h>
  13. #include <ggml-cpu.h>
  14. constexpr int kVecSize = 1 << 16;
  15. // Copy-pasted from ggml.c
  16. #define QK4_0 32
  17. typedef struct {
  18. float d; // delta
  19. uint8_t qs[QK4_0 / 2]; // nibbles / quants
  20. } block_q4_0;
  21. static_assert(sizeof(block_q4_0) == sizeof(float) + QK4_0 / 2, "wrong q4_0 block size/padding");
  22. #define QK4_1 32
  23. typedef struct {
  24. float d; // delta
  25. float m; // min
  26. uint8_t qs[QK4_1 / 2]; // nibbles / quants
  27. } block_q4_1;
  28. static_assert(sizeof(block_q4_1) == sizeof(float) * 2 + QK4_1 / 2, "wrong q4_1 block size/padding");
  29. // Copy-pasted from ggml.c
  30. #define QK8_0 32
  31. typedef struct {
  32. float d; // delta
  33. float s; // d * sum(qs[i])
  34. int8_t qs[QK8_0]; // quants
  35. } block_q8_0;
  36. static_assert(sizeof(block_q8_0) == 2*sizeof(float) + QK8_0, "wrong q8_0 block size/padding");
  37. static_assert(QK4_1 == QK8_0, "QK4_1 and QK8_0 must be the same");
  38. static_assert(QK4_0 == QK8_0, "QK4_0 and QK8_0 must be the same");
  39. template <typename T>
  40. static void fillQ4blocks(std::vector<T>& blocks, std::mt19937& rndm) {
  41. for (auto& b : blocks) {
  42. b.d = 1;
  43. for (int i=0; i<QK4_1/2; ++i) {
  44. uint8_t v1 = rndm() >> 28;
  45. uint8_t v2 = rndm() >> 28;
  46. b.qs[i] = v1 | (v2 << 4);
  47. }
  48. }
  49. }
  50. static void fillQ80blocks(std::vector<block_q8_0>& blocks, std::mt19937& rndm) {
  51. for (auto& b : blocks) {
  52. b.d = 1;
  53. int sum = 0;
  54. for (int i=0; i<QK8_0; ++i) {
  55. b.qs[i] = (rndm() >> 24) - 128;
  56. sum += b.qs[i];
  57. }
  58. b.s = b.d * sum;
  59. }
  60. }
  61. static float simpleDot(const block_q4_0& x, const block_q8_0& y) {
  62. int s1 = 0; //, s2 = 0;
  63. for (int i=0; i<QK4_1/2; i+=2) {
  64. int v1 = x.qs[i+0] & 0xf;
  65. int v2 = x.qs[i+0] >> 4;
  66. int v3 = x.qs[i+1] & 0xf;
  67. int v4 = x.qs[i+1] >> 4;
  68. int j = 2*i;
  69. s1 += v1*y.qs[j] + v2*y.qs[j+1] + v3*y.qs[j+2] + v4*y.qs[j+3];
  70. //s2 += y.qs[j] + y.qs[j+1] + y.qs[j+2] + y.qs[j+3];
  71. }
  72. return y.d * x.d * s1 - 8 * x.d * y.s;
  73. //return y.d * x.d * (s1 - 8 * s2);
  74. }
  75. static float simpleDot(const block_q4_1& x, const block_q8_0& y) {
  76. int s1 = 0; //, s2 = 0;
  77. for (int i=0; i<QK4_1/2; i+=2) {
  78. int v1 = x.qs[i+0] & 0xf;
  79. int v2 = x.qs[i+0] >> 4;
  80. int v3 = x.qs[i+1] & 0xf;
  81. int v4 = x.qs[i+1] >> 4;
  82. int j = 2*i;
  83. s1 += v1*y.qs[j] + v2*y.qs[j+1] + v3*y.qs[j+2] + v4*y.qs[j+3];
  84. //s2 += y.qs[j] + y.qs[j+1] + y.qs[j+2] + y.qs[j+3];
  85. }
  86. return y.d * x.d * s1 + y.s * x.m;
  87. //return y.d * (x.d * s1 + x.m * s2);
  88. }
  89. struct Stat {
  90. double sum = 0, sumt = 0, sumt2 = 0, maxt = 0;
  91. int nloop = 0;
  92. void addResult(double s, double t) {
  93. sum += s;
  94. sumt += t; sumt2 += t*t; maxt = std::max(maxt, t);
  95. ++nloop;
  96. }
  97. void reportResult(const char* title) const {
  98. if (nloop < 1) {
  99. printf("%s(%s): no result\n",__func__,title);
  100. return;
  101. }
  102. printf("============ %s\n",title);
  103. printf("<dot> = %g\n",sum/nloop);
  104. auto t = sumt/nloop, dt = sumt2/nloop - t*t;
  105. if (dt > 0) dt = sqrt(dt);
  106. printf("<time> = %g +/- %g us. Max. time = %g us.\n",t,dt,maxt);
  107. }
  108. };
  109. int main(int argc, char** argv) {
  110. int nloop = argc > 1 ? atoi(argv[1]) : 10;
  111. int type = argc > 2 ? atoi(argv[2]) : 1;
  112. std::mt19937 rndm(1234);
  113. std::vector<block_q4_1> x41;
  114. std::vector<block_q4_0> x40;
  115. std::vector<block_q8_0> y(kVecSize);
  116. if (type == 0) x40.resize(kVecSize);
  117. else {
  118. x41.resize(kVecSize);
  119. for (auto& b : x41) b.m = 1;
  120. }
  121. auto ggml_type = type == 0 ? GGML_TYPE_Q4_0 : GGML_TYPE_Q4_1;
  122. const auto * funcs = ggml_get_type_traits_cpu(ggml_type);
  123. Stat simple, ggml;
  124. for (int iloop=0; iloop<nloop; ++iloop) {
  125. if (type == 0) fillQ4blocks(x40, rndm);
  126. else fillQ4blocks(x41, rndm);
  127. fillQ80blocks(y, rndm);
  128. auto t1 = std::chrono::high_resolution_clock::now();
  129. double s = 0;
  130. if (type == 0) for (int i=0; i<kVecSize; ++i) s += simpleDot(x40[i], y[i]);
  131. else for (int i=0; i<kVecSize; ++i) s += simpleDot(x41[i], y[i]);
  132. auto t2 = std::chrono::high_resolution_clock::now();
  133. auto t = 1e-3*std::chrono::duration_cast<std::chrono::nanoseconds>(t2-t1).count();
  134. if (iloop > 3) simple.addResult(s, t);
  135. t1 = std::chrono::high_resolution_clock::now();
  136. float fs;
  137. if (type == 0) funcs->vec_dot(kVecSize * QK4_1, &fs, 0, x40.data(), 0, y.data(), 0, 1);
  138. else funcs->vec_dot(kVecSize * QK4_1, &fs, 0, x41.data(), 0, y.data(), 0, 1);
  139. t2 = std::chrono::high_resolution_clock::now();
  140. t = 1e-3*std::chrono::duration_cast<std::chrono::nanoseconds>(t2-t1).count();
  141. if (iloop > 3) ggml.addResult(fs, t);
  142. }
  143. // Report the time (and the average of the dot products so the compiler does not come up with the idea
  144. // of optimizing away the function calls after figuring that the result is not used).
  145. simple.reportResult("Simple");
  146. ggml.reportResult("ggml");
  147. return 0;
  148. }