[新手上路]批处理新手入门导读[视频教程]批处理基础视频教程[视频教程]VBS基础视频教程[批处理精品]批处理版照片整理器
[批处理精品]纯批处理备份&还原驱动[批处理精品]CMD命令50条不能说的秘密[在线下载]第三方命令行工具[在线帮助]VBScript / JScript 在线参考
返回列表 发帖
本帖最后由 523066680 于 2019-1-25 20:38 编辑

用了两天 VS2015,不愧是宇宙最强编辑器(是我墨守成规,一直装在系统,从来不用)

s_minus 函数和 vec_minus 函数性能分析,1W位数,各调用2W次的分析结果:





可以看到涉及数组[]操作的语句消耗较高,采用高的进制(base)处理势在必行。

再聊聊《Modern Computer Arithmetic》(后面简称MCA)
对于64位平台 unsigned long long int 支持的最大数字是 2^64-1=18446744073709551615,20位,如果我们充分利用,使用2^64作为进制,或者10^19作为进制,
很容易会遇到溢出问题,MCA中给出了几种方案:
Let T be the number of different values taken by the data type representing the coefficients ai, bi. (Clearly, β ≤ T, but equality does not necessarily hold,
for example β = 10^9 and T = 2^32.) At step 3, the value of s can be as large as 2β − 1, which is not representable if β = T. Several workarounds
are possible:
either use a machine instruction that gives the possible carry of ai + bi,
or use the fact that, if a carry occurs in ai + bi, then the computed sum – if performed modulo T – equals t := ai +bi −T < ai; thus, comparing t and ai will determine if a carry occurred.
A third solution is to keep a bit in reserve, taking β ≤ T/2.

用 T 表示一个存储单元所能表示的数的量,则有 β <= T (这里 β 表示采用的进制,以及β不一定等于T,例如T=2^32,但采用的进制为10^9)。
考虑加法操作 s=a+b+d (d为1或0,是上一次加法补进的数值),s的最大可能值为 2*β-1,当 β = T 时该公式无法正确计算。考虑以下方案:
1. 内部编码实现 (水平有限,暂时这么翻译)
2. 通过-T取余数判断,t := a + b - T < a ,对比 t 和 a 断定是否进 1
3. 采用一个保守的进制数 β,令 β ≤ T/2.

TOP

回复 44# 老刘1号

      这个牛跌前面楼层有提到过呀。
我们主要在折腾大数字和浮点数。如果只在signed int范围内开整数根,早就可以结帖了。
小程说他要写一个版本的时候我以为“硬核”要来了,结果 ……
那本《MCA》非常硬核!打算用C艹实践其中一小部分算法(大部分看不懂,小部分似懂非懂)。

回复 43# SQYSQYSQY

批处理的“数组”属于伪数组,和其他语言的数组有本质的区别,因为批的“索引”不只是数字,还有字母和符号,变量长度亦不确定,这和其他语言的哈希表(hash table)相似。
在C/C++里面的数组操作不需要“搜索”,可直接通过计算偏移量取内存地址,因为定长的数组在初始化后占用一段连续内存空间,且每个单元占用相同字节,
给定一个编号,通过 编号*字节大小+起点地址 可得目标内存地址,直接存取。
C++ vector容器的 [] 操作符消耗占用高,和其独立的实现有关(可能做了各种判断和转换处理),换用更纯粹的C语言数组可以减少这种消耗。

实测,元素数量为80W的容器/数组,1000次遍历每一个元素并写入int值,
  1. #include <iostream>
  2. #include <vector>
  3. #include <chrono>
  4. using namespace std;
  5. using namespace std::chrono;
  6. const int SIZE = 800000;
  7. void vec_test(void);
  8. void c_array_test(void);
  9. void c_pointer_test(void);
  10. void time_used(system_clock::time_point& time_a);
  11. vector<int> vec(SIZE);
  12. int array1[SIZE];
  13. int array2[SIZE];
  14. int main(int argc, char *argv[])
  15. {
  16.     system_clock::time_point start = system_clock::now();
  17.     for (int i = 0; i < 1000; i++) vec_test();
  18.     time_used(start);
  19.     for (int i = 0; i < 1000; i++)  c_array_test();
  20.     time_used(start);
  21.     for (int i = 0; i < 1000; i++)  c_pointer_test();
  22.     time_used(start);
  23.     return 0;
  24. }
  25. void vec_test(void) {
  26.     register int it;
  27.     for (it = 0; it < SIZE; it++) vec[it] = it;
  28. }
  29. void c_array_test(void) {
  30.     register int it;
  31.     for (it = 0; it < SIZE; it++) array1[it] = it;
  32. }
  33. void c_pointer_test(void) {
  34.     register int it;
  35.     register int *pt = array2;
  36.     for (it = 0; it < SIZE; it++) *(pt + it) = it;
  37. }
  38. void time_used(system_clock::time_point& time_a) {
  39.     duration<double> diff;
  40.     diff = chrono::system_clock::now() - time_a;
  41.     cout << "Time used: " << diff.count() << endl;
  42.     time_a = chrono::system_clock::now();
  43. }
复制代码
g++编译测试结果:
Time used: 1.24807
Time used: 0.400023
Time used: 0.393022

Visual Studio编译,差距更明显
Time used: 3.92722
Time used: 0.0410024
Time used: 0.0360021


如果一定要用 vector 又不想用它的 [] ,可以申请一个指针,int *vp = vec.data()
通过*vp指针直接算地址读写内存,速度和c_array一样快,想要更快,申请一个寄存器指针 register int *vp。
这就是自由度,可定制,可接管。

讨论算法,可以不分语言。谈论模块化思想,也可以不分语言,用批处理同样可以表达。
但要说极限压榨性能,怎么也轮不到批处理。

TOP

本帖最后由 523066680 于 2019-2-9 21:35 编辑

回复 48# 老刘1号


      这都是牛顿的功劳呀,牛迭是N次方,1/N次方都可以算,很多其他方程的根也可以算。因为他是在一个函数曲线任意一点(x)上求切线,然后这个切线不断迭代,向函数曲线和x轴的交点(根或者函数的解)逼近。
逼近速度非常快(差不多精度翻倍)。

现在回想,happy886r 当时写了好多大工程
happy886r - 数学计算工具 i 的重构版new i

TOP

返回列表