我正在编写一个 C# 类来使用整数执行 2D 可分离卷积,以获得比双对应更好的性能。问题是我没有获得真正的性能提升。
这是 X 过滤器代码(它对 int 和 double 情况都有效):
foreach (pixel)
{
int value = 0;
for (int k = 0; k < filterOffsetsX.Length; k++)
{
value += InputImage[index + filterOffsetsX[k]] * filterValuesX[k]; //index is relative to current pixel position
}
tempImage[index] = value;
}
在整数情况下,“value”、“InputImage”和“tempImage”是“int”、“Image <byte>”和“Image <int>”类型。
在双大小写“value”中,“InputImage”和“tempImage”是“double”、“Image <double>”和“Image <double>”类型。
(filterValues 在每种情况下都是 int[])
(图像类 <T> 是外部 dll 的一部分。它应该类似于 .NET 绘图图像类..)。
我的目标是通过 int += (byte * int) 与 double += (double * int) 实现快速性能
以下次数为重复200次的平均值。
过滤器大小 9 = 0.031( double )0.027(整数)
过滤器大小 13 = 0.042( double )0.038(整数)
过滤器尺寸 25 = 0.078 (double) 0.070 (int)
性能提升很小。这可能是由流水线停顿和次优代码引起的吗?
编辑:简化删除不重要变量的代码。
EDIT2:我不认为我有与缓存未命中相关的问题,因为“索引”遍历相邻的内存单元(逐行时尚)。此外,“filterOffstetsX”仅包含相对于同一行像素的小偏移量,最大距离为过滤器大小/2。问题可能出现在第二个可分离过滤器(Y 过滤器)中,但时间并没有太大不同。
最佳答案
使用 Visual C++,因为这样我可以确定我正在计时算术运算而不是其他。
结果(每个操作执行6亿次):
i16 add: 834575
i32 add: 840381
i64 add: 1691091
f32 add: 987181
f64 add: 979725
i16 mult: 850516
i32 mult: 858988
i64 mult: 6526342
f32 mult: 1085199
f64 mult: 1072950
i16 divide: 3505916
i32 divide: 3123804
i64 divide: 10714697
f32 divide: 8309924
f64 divide: 8266111
freq = 1562587
CPU 是英特尔酷睿 i7,睿频加速到 2.53 GHz。
基准代码:
#include <stdio.h>
#include <windows.h>
template<void (*unit)(void)>
void profile( const char* label )
{
static __int64 cumtime;
LARGE_INTEGER before, after;
::QueryPerformanceCounter(&before);
(*unit)();
::QueryPerformanceCounter(&after);
after.QuadPart -= before.QuadPart;
printf("%s: %I64i\n", label, cumtime += after.QuadPart);
}
const unsigned repcount = 10000000;
template<typename T>
void add(volatile T& var, T val) { var += val; }
template<typename T>
void mult(volatile T& var, T val) { var *= val; }
template<typename T>
void divide(volatile T& var, T val) { var /= val; }
template<typename T, void (*fn)(volatile T& var, T val)>
void integer_op( void )
{
unsigned reps = repcount;
do {
volatile T var = 2000;
fn(var,5);
fn(var,6);
fn(var,7);
fn(var,8);
fn(var,9);
fn(var,10);
} while (--reps);
}
template<typename T, void (*fn)(volatile T& var, T val)>
void fp_op( void )
{
unsigned reps = repcount;
do {
volatile T var = (T)2.0;
fn(var,(T)1.01);
fn(var,(T)1.02);
fn(var,(T)1.03);
fn(var,(T)2.01);
fn(var,(T)2.02);
fn(var,(T)2.03);
} while (--reps);
}
int main( void )
{
LARGE_INTEGER freq;
unsigned reps = 10;
do {
profile<&integer_op<__int16,add<__int16>>>("i16 add");
profile<&integer_op<__int32,add<__int32>>>("i32 add");
profile<&integer_op<__int64,add<__int64>>>("i64 add");
profile<&fp_op<float,add<float>>>("f32 add");
profile<&fp_op<double,add<double>>>("f64 add");
profile<&integer_op<__int16,mult<__int16>>>("i16 mult");
profile<&integer_op<__int32,mult<__int32>>>("i32 mult");
profile<&integer_op<__int64,mult<__int64>>>("i64 mult");
profile<&fp_op<float,mult<float>>>("f32 mult");
profile<&fp_op<double,mult<double>>>("f64 mult");
profile<&integer_op<__int16,divide<__int16>>>("i16 divide");
profile<&integer_op<__int32,divide<__int32>>>("i32 divide");
profile<&integer_op<__int64,divide<__int64>>>("i64 divide");
profile<&fp_op<float,divide<float>>>("f32 divide");
profile<&fp_op<double,divide<double>>>("f64 divide");
::QueryPerformanceFrequency(&freq);
putchar('\n');
} while (--reps);
printf("freq = %I64i\n", freq);
}
我使用 Visual C++ 2010 32 位进行了默认优化构建。
每次调用 profile、add、mult 和 divide(在循环内)都被内联。函数调用仍然生成到 profile,但由于每次调用完成 6000 万次操作,我认为函数调用开销并不重要。
即使加入了 volatile,Visual C++ 优化编译器也是SMART。我最初使用小整数作为右手操作数,编译器愉快地使用 lea 和 add 指令来进行整数乘法。调出高度优化的 C++ 代码可能会比常识建议的更有效,这仅仅是因为 C++ 优化器比任何 JIT 做得更好。
最初我在循环外初始化了 var,这使得浮点乘法代码由于常量溢出而运行得非常慢。 FPU 处理 NaN 很慢,这是在编写高性能数字运算例程时要牢记的另一点。
依赖项也以防止流水线的方式设置。如果您想查看流水线的效果,请在评论中说明,我将修改测试台以对多个变量进行操作,而不仅仅是一个变量。
i32 的反汇编:
; COMDAT ??$integer_op@H$1??$mult@H@@YAXACHH@Z@@YAXXZ
_TEXT SEGMENT
_var$66971 = -4 ; size = 4
??$integer_op@H$1??$mult@H@@YAXACHH@Z@@YAXXZ PROC ; integer_op<int,&mult<int> >, COMDAT
; 29 : {
00000 55 push ebp
00001 8b ec mov ebp, esp
00003 51 push ecx
; 30 : unsigned reps = repcount;
00004 b8 80 96 98 00 mov eax, 10000000 ; 00989680H
00009 b9 d0 07 00 00 mov ecx, 2000 ; 000007d0H
0000e 8b ff npad 2
$LL3@integer_op@5:
; 31 : do {
; 32 : volatile T var = 2000;
00010 89 4d fc mov DWORD PTR _var$66971[ebp], ecx
; 33 : fn(var,751);
00013 8b 55 fc mov edx, DWORD PTR _var$66971[ebp]
00016 69 d2 ef 02 00
00 imul edx, 751 ; 000002efH
0001c 89 55 fc mov DWORD PTR _var$66971[ebp], edx
; 34 : fn(var,6923);
0001f 8b 55 fc mov edx, DWORD PTR _var$66971[ebp]
00022 69 d2 0b 1b 00
00 imul edx, 6923 ; 00001b0bH
00028 89 55 fc mov DWORD PTR _var$66971[ebp], edx
; 35 : fn(var,7124);
0002b 8b 55 fc mov edx, DWORD PTR _var$66971[ebp]
0002e 69 d2 d4 1b 00
00 imul edx, 7124 ; 00001bd4H
00034 89 55 fc mov DWORD PTR _var$66971[ebp], edx
; 36 : fn(var,81);
00037 8b 55 fc mov edx, DWORD PTR _var$66971[ebp]
0003a 6b d2 51 imul edx, 81 ; 00000051H
0003d 89 55 fc mov DWORD PTR _var$66971[ebp], edx
; 37 : fn(var,9143);
00040 8b 55 fc mov edx, DWORD PTR _var$66971[ebp]
00043 69 d2 b7 23 00
00 imul edx, 9143 ; 000023b7H
00049 89 55 fc mov DWORD PTR _var$66971[ebp], edx
; 38 : fn(var,101244215);
0004c 8b 55 fc mov edx, DWORD PTR _var$66971[ebp]
0004f 69 d2 37 dd 08
06 imul edx, 101244215 ; 0608dd37H
; 39 : } while (--reps);
00055 48 dec eax
00056 89 55 fc mov DWORD PTR _var$66971[ebp], edx
00059 75 b5 jne SHORT $LL3@integer_op@5
; 40 : }
0005b 8b e5 mov esp, ebp
0005d 5d pop ebp
0005e c3 ret 0
??$integer_op@H$1??$mult@H@@YAXACHH@Z@@YAXXZ ENDP ; integer_op<int,&mult<int> >
; Function compile flags: /Ogtp
_TEXT ENDS
与 f64 相乘:
; COMDAT ??$fp_op@N$1??$mult@N@@YAXACNN@Z@@YAXXZ
_TEXT SEGMENT
_var$67014 = -8 ; size = 8
??$fp_op@N$1??$mult@N@@YAXACNN@Z@@YAXXZ PROC ; fp_op<double,&mult<double> >, COMDAT
; 44 : {
00000 55 push ebp
00001 8b ec mov ebp, esp
00003 83 e4 f8 and esp, -8 ; fffffff8H
; 45 : unsigned reps = repcount;
00006 dd 05 00 00 00
00 fld QWORD PTR __real@4000000000000000
0000c 83 ec 08 sub esp, 8
0000f dd 05 00 00 00
00 fld QWORD PTR __real@3ff028f5c28f5c29
00015 b8 80 96 98 00 mov eax, 10000000 ; 00989680H
0001a dd 05 00 00 00
00 fld QWORD PTR __real@3ff051eb851eb852
00020 dd 05 00 00 00
00 fld QWORD PTR __real@3ff07ae147ae147b
00026 dd 05 00 00 00
00 fld QWORD PTR __real@4000147ae147ae14
0002c dd 05 00 00 00
00 fld QWORD PTR __real@400028f5c28f5c29
00032 dd 05 00 00 00
00 fld QWORD PTR __real@40003d70a3d70a3d
00038 eb 02 jmp SHORT $LN3@fp_op@3
$LN22@fp_op@3:
; 46 : do {
; 47 : volatile T var = (T)2.0;
; 48 : fn(var,(T)1.01);
; 49 : fn(var,(T)1.02);
; 50 : fn(var,(T)1.03);
; 51 : fn(var,(T)2.01);
; 52 : fn(var,(T)2.02);
; 53 : fn(var,(T)2.03);
; 54 : } while (--reps);
0003a d9 ce fxch ST(6)
$LN3@fp_op@3:
0003c 48 dec eax
0003d d9 ce fxch ST(6)
0003f dd 14 24 fst QWORD PTR _var$67014[esp+8]
00042 dd 04 24 fld QWORD PTR _var$67014[esp+8]
00045 d8 ce fmul ST(0), ST(6)
00047 dd 1c 24 fstp QWORD PTR _var$67014[esp+8]
0004a dd 04 24 fld QWORD PTR _var$67014[esp+8]
0004d d8 cd fmul ST(0), ST(5)
0004f dd 1c 24 fstp QWORD PTR _var$67014[esp+8]
00052 dd 04 24 fld QWORD PTR _var$67014[esp+8]
00055 d8 cc fmul ST(0), ST(4)
00057 dd 1c 24 fstp QWORD PTR _var$67014[esp+8]
0005a dd 04 24 fld QWORD PTR _var$67014[esp+8]
0005d d8 cb fmul ST(0), ST(3)
0005f dd 1c 24 fstp QWORD PTR _var$67014[esp+8]
00062 dd 04 24 fld QWORD PTR _var$67014[esp+8]
00065 d8 ca fmul ST(0), ST(2)
00067 dd 1c 24 fstp QWORD PTR _var$67014[esp+8]
0006a dd 04 24 fld QWORD PTR _var$67014[esp+8]
0006d d8 cf fmul ST(0), ST(7)
0006f dd 1c 24 fstp QWORD PTR _var$67014[esp+8]
00072 75 c6 jne SHORT $LN22@fp_op@3
00074 dd d8 fstp ST(0)
00076 dd dc fstp ST(4)
00078 dd da fstp ST(2)
0007a dd d8 fstp ST(0)
0007c dd d8 fstp ST(0)
0007e dd d8 fstp ST(0)
00080 dd d8 fstp ST(0)
; 55 : }
00082 8b e5 mov esp, ebp
00084 5d pop ebp
00085 c3 ret 0
??$fp_op@N$1??$mult@N@@YAXACNN@Z@@YAXXZ ENDP ; fp_op<double,&mult<double> >
; Function compile flags: /Ogtp
_TEXT ENDS
关于c# - 整数与 double 算术性能?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3676567/
如何在ruby中调用C#dll? 最佳答案 我能想到几种可能性:为您的DLL编写(或找人编写)一个COM包装器,如果它还没有,则使用Ruby的WIN32OLE库来调用它;看看RubyCLR,其中一位作者是JohnLam,他继续在Microsoft从事IronRuby方面的工作。(估计不会再维护了,可能不支持.Net2.0以上的版本);正如其他地方已经提到的,看看使用IronRuby,如果这是您的技术选择。有一个主题是here.请注意,最后一篇文章实际上来自JohnLam(看起来像是2009年3月),他似乎很自在地断言RubyCL
我正在尝试在Ruby中复制Convert.ToBase64String()行为。这是我的C#代码:varsha1=newSHA1CryptoServiceProvider();varpasswordBytes=Encoding.UTF8.GetBytes("password");varpasswordHash=sha1.ComputeHash(passwordBytes);returnConvert.ToBase64String(passwordHash);//returns"W6ph5Mm5Pz8GgiULbPgzG37mj9g="当我在Ruby中尝试同样的事情时,我得到了相同sha
C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.
有没有一种简单的方法可以将给定的整数格式化为具有固定长度和前导零的字符串?#convertnumberstostringsoffixedlength3[1,12,123,1234].map{|e|???}=>["001","012","123","234"]我找到了解决方案,但也许还有更聪明的方法。format('%03d',e)[-3..-1] 最佳答案 如何使用%1000而不是进行字符串操作来获取最后三位数字?[1,12,123,1234].map{|e|format('%03d',e%1000)}更新:根据theTinMan的
我有很多这样的文档:foo_1foo_2foo_3bar_1foo_4...我想通过获取foo_[X]的所有实例并将它们中的每一个替换为foo_[X+1]来转换它们。在这个例子中:foo_2foo_3foo_4bar_1foo_5...我可以用gsub和一个block来做到这一点吗?如果不是,最干净的方法是什么?我真的在寻找一个优雅的解决方案,因为我总是可以暴力破解它,但我觉得有一些正则表达式技巧值得学习。 最佳答案 我(完全)不懂Ruby,但类似这样的东西应该可以工作:"foo_1foo_2".gsub(/(foo_)(\d+)/
问题1:我无法通过以下方式找到将负整数转换为二进制的方法。我应该像这样转换它。-3=>"11111111111111111111111111111101"我在下面试过:sprintf('%b',-3)=>"..101"#..appearsanddoesnotshow111111bit.-3.to_s(2)=>"-11"#Thisjustadds-tothebinaryofthepositiveinteger3.问题2:有趣的是,如果我使用在线转换器,它告诉我-3的二进制是“0010110100110011”。"11111111111111111111111111111101"和"001
我正在使用Ruby解决一些ProjectEuler问题,特别是这里我要讨论的问题25(Fibonacci数列中包含1000位数字的第一项的索引是多少?)。起初,我使用的是Ruby2.2.3,我将问题编码为:number=3a=1b=2whileb.to_s.length但后来我发现2.4.2版本有一个名为digits的方法,这正是我需要的。我转换为代码:whileb.digits.length当我比较这两种方法时,digits慢得多。时间./025/problem025.rb0.13s用户0.02s系统80%cpu0.190总计./025/problem025.rb2.19s用户0.0
我正在寻找一个用ruby演示计时器的在线示例,并发现了下面的代码。它按预期工作,但这个简单的程序使用30Mo内存(如Windows任务管理器中所示)和太多CPU有意义吗?非常感谢deftime_blockstart_time=Time.nowThread.new{yield}Time.now-start_timeenddefrepeat_every(seconds)whiletruedotime_spent=time_block{yield}#Tohandle-vesleepinteravalsleep(seconds-time_spent)iftime_spent
除了协议(protocol)定义中的斜杠('http[s]://'、'ftp://'等)之外,我想替换URL中的所有多个斜杠。我该怎么做?此代码无一异常(exception)地替换:url.gsub(/\/\/+/,'/') 最佳答案 您只需排除任何以:开头的匹配项url.gsub(/([^:])\/\//,'\1/') 关于ruby正则表达式:replacedoubleslashesinURL,我们在StackOverflow上找到一个类似的问题: http
我有一个随机大小的散列,它可能有类似"100"的值,我想将其转换为整数。我知道我可以使用value.to_iifvalue.to_i.to_s==value来做到这一点,但我不确定我将如何在我的散列中递归地做到这一点,考虑到一个值可以是一个字符串,或一个数组(哈希或字符串),或另一个哈希。 最佳答案 这是一个非常简单的递归实现(尽管必须同时处理数组和散列会增加一些技巧)。deffixnumifyobjifobj.respond_to?:to_i#IfwecancastittoaFixnum,doit.obj.to_ielsifobj