[go: nahoru, domu]

Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

面向过程 :面向过程性能比面向对象高?? #431

Open
ryouaki opened this issue Aug 8, 2019 · 64 comments
Open

面向过程 :面向过程性能比面向对象高?? #431

ryouaki opened this issue Aug 8, 2019 · 64 comments
Labels
discuss discuss a problem enhancement New feature or request or suggestion perfect Improve knowledge points or descriptions

Comments

@ryouaki
Copy link
ryouaki commented Aug 8, 2019

面向过程 :面向过程性能比面向对象高。 因为类调用时需要实例化,开销比较大,比较消耗资源,所以当性能是最重要的考量因素的时候,比如单片机、嵌入式开发、Linux/Unix等一般采用面向过程开发。

----》》
这个并不是根本原因,面向过程也需要分配内存,计算内存偏移量,Java性能差的主要原因并不是因为它是面向对象语言,而是Java是半编译语言,最终的执行代码并不是可以直接被CPU执行的二进制机械码。

而面向过程语言大多都是直接编译成机械码在电脑上执行,并且其它一些面向过程的脚本语言性能也并不一定比Java好。

@guang19
Copy link
Contributor
guang19 commented Aug 11, 2019

@ryouaki 感谢,学习了

@guang19
Copy link
Contributor
guang19 commented Aug 18, 2019

我觉得这个得分情况而定,c和c++就是很好的例子,两种语言执行效率差距其实并不大,但是两种语言的设计思想就不同。

@ryouaki
Copy link
Author
ryouaki commented Aug 18, 2019

@ryouaki 请教一下,是不是可以概括为:“程序的性能首先由编程语言的执行方式有关,其次才是设计范式”

设计范式和性能无关的。主要是编程语言的运行机制决定的。

@ryouaki
Copy link
Author
ryouaki commented Aug 18, 2019

我觉得这个得分情况而定,c和c++就是很好的例子,两种语言执行效率差距其实并不大,但是两种语言的设计思想就不同。

c和c++的运行机制是一样的。都是编译成机械码。

@guang19
Copy link
Contributor
guang19 commented Aug 18, 2019

感谢

@Snailclimb Snailclimb added the enhancement New feature or request or suggestion label Aug 18, 2019
@321cto
Copy link
321cto commented Sep 1, 2019

收益了,感谢

@LiuRuiXiang
Copy link

长知识了

@rockhu01
Copy link

学习了

@Lotus-Blue
Copy link

amazing gay

@konigsbergg
Copy link

good job

@codingKings
Copy link

到底是面向过程性能高还是面向对象性能高?这类问题很难回答,非要比较高低的话就必须设定一个场景,否则只能说是各有所长

@peacecoder
Copy link

另外,现在java 性能不一定低,变化很大了

@ryouaki
Copy link
Author
ryouaki commented Dec 20, 2019

另外,现在java 性能不一定低,变化很大了

上半年刚测的。

@cjjMichael
Copy link

感觉这个表述-"而是Java是半编译语言,最终的执行代码并不是可以直接被CPU执行的二进制机械码"不是很准确。最终执行代码不都是二进制码吗?(在此之前是低级语言代码,如汇编)个人觉得可以表述为编译过程的复杂程度的不同导致执行时间的不同(性能的主要度量)。第二,直接比较语言性能本来就是不恰当的。第三,现在的Java今非昔比,底层编译更加优化。第四,有限的测试不具有说服力,具体情况具体分析。

@cjjMichael
Copy link

修正一下是"中间语言代码"。

@haichuanjiang
Copy link

可以

@yongmingcode
Copy link

谢谢,学习了

@Snailclimb Snailclimb reopened this Mar 9, 2020
@zenglanyuan
Copy link

讨论比回答更精彩。哈哈哈

@makronyang
Copy link

java 是解释语言 执行的时候 比直接 编译出来的 二进制执行码多了一个步骤

@codeplayer-zzy
Copy link

学习中

@ttr5966
Copy link
ttr5966 commented Mar 30, 2020 via email

@iloooo
Copy link
Contributor
iloooo commented Apr 2, 2020

@Snailclimb
“1. 面向对象和面向过程的区别”, 根据guide中解释的内容我觉得应该是面向对象和面向过程优缺点更准确些,而它们两个区别应该讨论OOP和POP解决问题的方式不同。
面向过程解决问题方式:把解决问题的过程,拆成一个个方法,通过一个个方法的执行解决问题。
面向对象解决问题方式:先抽象出对象,然后用对象执行方法的方式解决问题。
@ryouaki
我还想补充讨论下,首先面向过程和对象是一种编程思想,就像@cjjMichael 所言“直接比较语言性能本来就是不恰当的”,语言执行性能应该从他们最终执行方式上讨论。直接编译成机器码,然后执行的语言(比如C,编译器一次性编译为平台相关的机器码),从过程复杂度上,肯定比解释执行的语言(HTML,JavaScript,浏览器的解释器,把代码一行行解释为机器码)和 Java这种中间码(编译)+虚拟机(解释成机器码)的方式,要性能高的多。

@yuanjinzhong
Copy link

惊呆了

@wangpeipei90
Copy link

同意。语言的执行方式和OOP/POP没有必然的关系。从原理上来讲,OOP的语言也可以直接编译成字节码,而POP的语言也可以解释执行。

@907739769
Copy link

个人理解:JAVA为了一次代码,处处运行(也就是可移植性),有了一层中间码也就是字节码,字节码的作用就是为了可移植性,字节码转换成机器码的工作就交给了java虚拟机,由java虚拟机解释成机器能够运行的二进制机器码。而C语言是直接编译为机器码,机器只需要直接运行二进制机器码即可,而无需和java一样在运行时解释为机器码。从这个角度看,性能与面向过程或对象是无关的。(JAVA只是为了移植性舍弃了一部分性能。)

@mjiuming
Copy link
mjiuming commented Apr 19, 2020

其实感觉编程范式的侧重点并不在于性能,在于“通过限制你可能出错的细节而给你更多的自由空间”,在Robert.C.Martin的《架构简洁之道》中第三章末尾有对编程范式进行总结

结构化编程对程序控制权的直接转移进行了限制和规范

面向对象编程对程序控制权的间接转移进行了限制和规范

函数式编程对程序中的赋值进行了限制和规范

每一种编程范式都从某一方面限制和规范了程序员的能力。没有一个范式是增加新能力的。

其实也可以这么理解。通过限制goto的使用,我们可以不必在意程序控制权的跳转细节,从而给我们带来了更多的灵活性。通过封装对象的状态,我们可以安全地控制对象所处状态的有效性,从而可以使用更多设计模式。通过限制属性的赋值,我们可以写出无状态的函数与服务,这样就不逼关心函数的可见性与数据一致性,从而更方便地扩展服务。

@mjiuming
Copy link
mjiuming commented Apr 19, 2020

编程范式是与具体语言无关的,比如说下面这段代码是使用C语言实现的,这段代码是OOP的,因为它满足继承、封装、多态三个特性,但是它的所有语法使用的都是标准C99语法,执行性能与面向过程的C语言代码没有区别

#include <stdio.h>
#include <stdlib.h>

struct Object
{
    int a;
    void (*setA)(struct Object *object, int a);
    int (*getA)(struct Object *object);
};

struct ObjectExtended
{
    struct Object *__super;
    void (*setA)(struct Object *object, int a);
    int (*getA)(struct Object *object);
};

static void _setA(struct Object *object, int a)
{
    object->a = a;
}

static int _getA(struct Object *object)
{
    return object->a;
}

static void _setA_extended(struct Object *object, int a)
{
    printf("ObjectExtended[%p] set A : %d\n", object, a);
    object->a = a;
}

static int _getA_extended(struct Object *object)
{
    printf("ObjectExtended[%p] get A : %d\n", object, object->a);
    return object->a;
}

struct Object *createObject()
{
    struct Object *object = malloc(sizeof(struct Object));
    object->setA = _setA;
    object->getA = _getA;
    return object;
}

struct ObjectExtended *createObjectExtended()
{
    struct Object *object = malloc(sizeof(struct Object));
    struct ObjectExtended *objectExtended = malloc(sizeof(struct ObjectExtended));
    objectExtended->__super = object;
    objectExtended->setA = object->setA = _setA_extended;
    objectExtended->getA = object->getA = _getA_extended;
    return objectExtended;
}

int main(int argc, char **argv)
{
    struct Object *object = createObject();
    struct Object *objectExtended = createObjectExtended();

    object->setA(object, 1);
    printf("Object[%p]->getA() = %d;\n", object, object->getA(object));

    objectExtended->setA(objectExtended, 2);
    printf("Object[%p]->getA() = %d;\n", objectExtended, objectExtended->getA(objectExtended));
    
    return EXIT_SUCCESS;
}

这是运行的输出结果

Object[0x7faeb6c017c0]->getA() = 1;
ObjectExtended[0x7faeb6c01800] set A : 2
ObjectExtended[0x7faeb6c01800] get A : 2
Object[0x7faeb6c01800]->getA() = 2;

@kebukeYi
Copy link

比如来一段 是面向过程开发的代码 和面向对象开发的代码 直接对比看一下!

@mjiuming
Copy link

函数是否编译于main函数是否调用是没有关系的,比如考虑如下的C语言代码

void foo() {}
void bar() {}

int main(int argc, char **argv) {
    return 0;
}

在GCC10.2.0下使用O0优化编译出来汇编代码如下

	.file	"main.c"
	.text
	.globl	foo
	.type	foo, @function
foo:
.LFB0:
	.cfi_startproc
	pushq	%rbp
	.cfi_def_cfa_offset 16
	.cfi_offset 6, -16
	movq	%rsp, %rbp
	.cfi_def_cfa_register 6
	nop
	popq	%rbp
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc
.LFE0:
	.size	foo, .-foo
	.globl	bar
	.type	bar, @function
bar:
.LFB1:
	.cfi_startproc
	pushq	%rbp
	.cfi_def_cfa_offset 16
	.cfi_offset 6, -16
	movq	%rsp, %rbp
	.cfi_def_cfa_register 6
	nop
	popq	%rbp
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc
.LFE1:
	.size	bar, .-bar
	.globl	main
	.type	main, @function
main:
.LFB2:
	.cfi_startproc
	pushq	%rbp
	.cfi_def_cfa_offset 16
	.cfi_offset 6, -16
	movq	%rsp, %rbp
	.cfi_def_cfa_register 6
	movl	%edi, -4(%rbp)
	movq	%rsi, -16(%rbp)
	movl	$0, %eax
	popq	%rbp
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc
.LFE2:
	.size	main, .-main
	.ident	"GCC: (GNU) 10.2.0"
	.section	.note.GNU-stack,"",@progbits

实际上main入口并没有调用foo和bar,但是编译器依旧会对它进行编译,与你是否有使用过它是没有关系的

XSF notifications@github.com 于2021年1月24日周日 上午12:10写道:

别的地方我还不确定,但是面向对象的搞法内存开销一定是大过面向过程的,比如你上面的C语言面向对象的写法,假设有这么一个类,拥有很多接口,那他就需要很多个函数指针,而你只是想用其中几个接口,但是你在实例化的时候,这些指针变量都会被实例化出来的。以上是第一点,第二点,当你实例化出来你就要赋初值,也就是构造函数的工作,你需要给这些函数指针赋值,那相应地址的函数也会被编译。哪怕你本来不想用这个函数,但是构造函数把他赋值了那个函数就会被编译,同理这个对象中可能还有很多的私有或者公有变量你的程序也用不上。比如女性你只想要她的上半身的功能,但是只要你实例化个女人出来,她的下半身也会存在,在有资源的情况下你为了快速利用别人写好的类,你就直接实例化直接用就完事了多方便啊!但是没资源的情况,用面向过程,需要一个功能函数就调用一个功能函数,没被调用过的函数不会被编译,变量也是看情况声明,不会存在什么明明没用到也被声明出来的情况。linux只能给资源丰富的嵌入式设备用,哪怕是ulinux精简版,中低端单片机基本也用不起,这也是有相应的道理的。


You are receiving this because you commented.
Reply to this email directly, view it on GitHub
#431 (comment),
or unsubscribe
https://github.com/notifications/unsubscribe-auth/AKYAXFD2R7GCVMOKRZLHQC3S3LYFVANCNFSM4IKID5KQ
.

@kebukeYi
Copy link
kebukeYi commented Jan 29, 2021 via email

@mjiuming
Copy link
mjiuming commented Feb 1, 2021

即使是-O3也是同样的

	.file	"main.c"
	.text
	.p2align 4
	.globl	foo
	.type	foo, @function
foo:
.LFB0:
	.cfi_startproc
	ret
	.cfi_endproc
.LFE0:
	.size	foo, .-foo
	.p2align 4
	.globl	bar
	.type	bar, @function
bar:
.LFB4:
	.cfi_startproc
	ret
	.cfi_endproc
.LFE4:
	.size	bar, .-bar
	.section	.text.startup,"ax",@progbits
	.p2align 4
	.globl	main
	.type	main, @function
main:
.LFB2:
	.cfi_startproc
	xorl	%eax, %eax
	ret
	.cfi_endproc
.LFE2:
	.size	main, .-main
	.ident	"GCC: (GNU) 10.2.0"
	.section	.note.GNU-stack,"",@progbits

除非你对函数签名进行修饰

static void foo() {}
static void bar() {}

int main(int argc, char **argv) {
    return 0;
}

明确声明它是不会导出给外部进行调用的函数

	.file	"main.c"
	.text
	.section	.text.startup,"ax",@progbits
	.p2align 4
	.globl	main
	.type	main, @function
main:
.LFB2:
	.cfi_startproc
	xorl	%eax, %eax
	ret
	.cfi_endproc
.LFE2:
	.size	main, .-main
	.ident	"GCC: (GNU) 10.2.0"
	.section	.note.GNU-stack,"",@progbits

只要有非static的函数使用了static的函数,那么static的函数就会被重新编译(未使用O3,因为O3会将static函数内联,从而直接return

static void foo() {}
void bar() { foo(); }

int main(int argc, char **argv) {
    return 0;
}
	.file	"main.c"
	.text
	.type	foo, @function
foo:
.LFB0:
	.cfi_startproc
	pushq	%rbp
	.cfi_def_cfa_offset 16
	.cfi_offset 6, -16
	movq	%rsp, %rbp
	.cfi_def_cfa_register 6
	nop
	popq	%rbp
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc
.LFE0:
	.size	foo, .-foo
	.globl	bar
	.type	bar, @function
bar:
.LFB1:
	.cfi_startproc
	pushq	%rbp
	.cfi_def_cfa_offset 16
	.cfi_offset 6, -16
	movq	%rsp, %rbp
	.cfi_def_cfa_register 6
	movl	$0, %eax
	call	foo
	nop
	popq	%rbp
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc
.LFE1:
	.size	bar, .-bar
	.globl	main
	.type	main, @function
main:
.LFB2:
	.cfi_startproc
	pushq	%rbp
	.cfi_def_cfa_offset 16
	.cfi_offset 6, -16
	movq	%rsp, %rbp
	.cfi_def_cfa_register 6
	movl	%edi, -4(%rbp)
	movq	%rsi, -16(%rbp)
	movl	$0, %eax
	popq	%rbp
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc
.LFE2:
	.size	main, .-main
	.ident	"GCC: (GNU) 10.2.0"
	.section	.note.GNU-stack,"",@progbits

当然你可以用static修饰所有不想暴露给别人的函数,但是不觉得这样做就与拿来别人的库来用相违背吗?如果我是给别人用的库,那么我就不可能用static修饰,那么不论我用什么构造函数或不用构造函数都会编译;如果我是给别人用的库,那么我用static修饰了,并且没有使用非static的函数来调用它,很开心,他没有编译,那么这个函数存在的意义是什么呢?

@mjiuming
Copy link
mjiuming commented Feb 3, 2021

请使用阳间的方式沟通,总这么发真的没意思,
你说的这种方式无异于

int a;
int b;
int c;
int d(int){ /* do something */ return 0; }
inf e(int){ /* do something */ return 0; }
inf f(int){ /* do something */ return 0; }

既然你想比较内存优劣请先告诉我这种写法和你那种写法有什么区别,难道这么写就不会占用内存了?请不要和我说你可以不写其他的变量和函数弄成了

int a;
int d(int){ /* do something */ return 0; }

我也可以说为什么我相关的一系列操作不会使用下面的简洁方式呢?

typedef struct type {
    int a;
    int (*d)(int);
}

如果你认为你写的是4K Flash内存的上古芯片,那麻烦老老实实用汇编什么高级语言都别用,最好是没有任何过程调用没有栈帧分配。

除此之外,如果是想好好讨论问题请放在相同的问题域进行讨论,现在在讨论的是用任何语言都能实现面向对象范式,在实现相同功能的情况下大家的执行效率没有明显差异,而不是我使用了封装你非要插入无限多的无用字段然后跟我说:“看,你这个东西占用了无限大的内存,我的只需要一个变量就能完成”,在我眼里就跟有人在跟我说:“火箭这玩意得烧煤,最好还是水洗煤”。

XSF notifications@github.com 于2021年2月3日周三 下午2:41写道:

即使是-O3也是同样的

.file "main.c"
.text
.p2align 4
.globl foo
.type foo, @function
foo:
.LFB0:
.cfi_startproc
ret
.cfi_endproc
.LFE0:
.size foo, .-foo
.p2align 4
.globl bar
.type bar, @function
bar:
.LFB4:
.cfi_startproc
ret
.cfi_endproc
.LFE4:
.size bar, .-bar
.section .text.startup,"ax",@progbits
.p2align 4
.globl main
.type main, @function
main:
.LFB2:
.cfi_startproc
xorl %eax, %eax
ret
.cfi_endproc
.LFE2:
.size main, .-main
.ident "GCC: (GNU) 10.2.0"
.section .note.GNU-stack,"",@progbits

除非你对函数签名进行修饰

static void foo() {}static void bar() {}
int main(int argc, char **argv) {
return 0;
}

明确声明它是不会导出给外部进行调用的函数

.file "main.c"
.text
.section .text.startup,"ax",@progbits
.p2align 4
.globl main
.type main, @function
main:
.LFB2:
.cfi_startproc
xorl %eax, %eax
ret
.cfi_endproc
.LFE2:
.size main, .-main
.ident "GCC: (GNU) 10.2.0"
.section .note.GNU-stack,"",@progbits

只要有非static的函数使用了static的函数,那么static的函数就会被重新编译(未使用O3,因为O3会将static函数内联,从而直接
return)

static void foo() {}void bar() { foo(); }
int main(int argc, char **argv) {
return 0;
}

.file "main.c"
.text
.type foo, @function
foo:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
nop
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size foo, .-foo
.globl bar
.type bar, @function
bar:
.LFB1:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl $0, %eax
call foo
nop
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE1:
.size bar, .-bar
.globl main
.type main, @function
main:
.LFB2:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl %edi, -4(%rbp)
movq %rsi, -16(%rbp)
movl $0, %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE2:
.size main, .-main
.ident "GCC: (GNU) 10.2.0"
.section .note.GNU-stack,"",@progbits

当然你可以用static修饰所有不想暴露给别人的函数,但是不觉得这样做就与拿来别人的库来用相违背吗?如果我是给别人用的库,那么我就不可能用static修饰,那么不论我用什么构造函数或不用构造函数都会编译;如果我是给别人用的库,那么我用static修饰了,并且没有使用非
static的函数来调用它,很开心,他没有编译,那么这个函数存在的意义是什么呢?

求教一下,你定义一个结构体
struck xxx
{
int a;
int b;
int c;
int (*d)(int);
int (*e)(int);
int (*f)(int);
}
然后声明 xxx
xxx1,但是实际上你又只用了一个d函数,d函数只和a变量相关,也就是说实际你只用到了a变量和b函数。但是你却声明了xxx1,xxx1占用的内存会比你实际使用的部分大,这应该没错吧?


You are receiving this because you commented.
Reply to this email directly, view it on GitHub
#431 (comment),
or unsubscribe
https://github.com/notifications/unsubscribe-auth/AKYAXFARCLFQM6URG2WB7X3S5DVX5ANCNFSM4IKID5KQ
.

@q809935302
Copy link

请使用阳间的方式沟通,总这么发真的没意思,
你说的这种方式无异于

int a;
int b;
int c;
int d(int){ /* do something */ return 0; }
inf e(int){ /* do something */ return 0; }
inf f(int){ /* do something */ return 0; }

既然你想比较内存优劣请先告诉我这种写法和你那种写法有什么区别,难道这么写就不会占用内存了?请不要和我说你可以不写其他的变量和函数弄成了

int a;
int d(int){ /* do something */ return 0; }

我也可以说为什么我相关的一系列操作不会使用下面的简洁方式呢?

typedef struct type {
    int a;
    int (*d)(int);
}

如果你认为你写的是4K Flash内存的上古芯片,那麻烦老老实实用汇编什么高级语言都别用,最好是没有任何过程调用没有栈帧分配。

除此之外,如果是想好好讨论问题请放在相同的问题域进行讨论,现在在讨论的是用任何语言都能实现面向对象范式,在实现相同功能的情况下大家的执行效率没有明显差异,而不是我使用了封装你非要插入无限多的无用字段然后跟我说:“看,你这个东西占用了无限大的内存,我的只需要一个变量就能完成”,在我眼里就跟有人在跟我说:“火箭这玩意得烧煤,最好还是水洗煤”。

XSF notifications@github.com 于2021年2月3日周三 下午2:41写道:

即使是-O3也是同样的
.file "main.c"
.text
.p2align 4
.globl foo
.type foo, @function
foo:
.LFB0:
.cfi_startproc
ret
.cfi_endproc
.LFE0:
.size foo, .-foo
.p2align 4
.globl bar
.type bar, @function
bar:
.LFB4:
.cfi_startproc
ret
.cfi_endproc
.LFE4:
.size bar, .-bar
.section .text.startup,"ax",@progbits
.p2align 4
.globl main
.type main, @function
main:
.LFB2:
.cfi_startproc
xorl %eax, %eax
ret
.cfi_endproc
.LFE2:
.size main, .-main
.ident "GCC: (GNU) 10.2.0"
.section .note.GNU-stack,"",@progbits
除非你对函数签名进行修饰
static void foo() {}static void bar() {}
int main(int argc, char **argv) {
return 0;
}
明确声明它是不会导出给外部进行调用的函数
.file "main.c"
.text
.section .text.startup,"ax",@progbits
.p2align 4
.globl main
.type main, @function
main:
.LFB2:
.cfi_startproc
xorl %eax, %eax
ret
.cfi_endproc
.LFE2:
.size main, .-main
.ident "GCC: (GNU) 10.2.0"
.section .note.GNU-stack,"",@progbits
只要有非static的函数使用了static的函数,那么static的函数就会被重新编译(未使用O3,因为O3会将static函数内联,从而直接
return)
static void foo() {}void bar() { foo(); }
int main(int argc, char **argv) {
return 0;
}
.file "main.c"
.text
.type foo, @function
foo:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
nop
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size foo, .-foo
.globl bar
.type bar, @function
bar:
.LFB1:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl $0, %eax
call foo
nop
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE1:
.size bar, .-bar
.globl main
.type main, @function
main:
.LFB2:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl %edi, -4(%rbp)
movq %rsi, -16(%rbp)
movl $0, %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE2:
.size main, .-main
.ident "GCC: (GNU) 10.2.0"
.section .note.GNU-stack,"",@progbits
当然你可以用static修饰所有不想暴露给别人的函数,但是不觉得这样做就与拿来别人的库来用相违背吗?如果我是给别人用的库,那么我就不可能用static修饰,那么不论我用什么构造函数或不用构造函数都会编译;如果我是给别人用的库,那么我用static修饰了,并且没有使用非
static的函数来调用它,很开心,他没有编译,那么这个函数存在的意义是什么呢?
求教一下,你定义一个结构体
struck xxx
{
int a;
int b;
int c;
int (*d)(int);
int (*e)(int);
int (*f)(int);
}
然后声明 xxx
xxx1,但是实际上你又只用了一个d函数,d函数只和a变量相关,也就是说实际你只用到了a变量和b函数。但是你却声明了xxx1,xxx1占用的内存会比你实际使用的部分大,这应该没错吧?

You are receiving this because you commented.
Reply to this email directly, view it on GitHub
#431 (comment),
or unsubscribe
https://github.com/notifications/unsubscribe-auth/AKYAXFARCLFQM6URG2WB7X3S5DVX5ANCNFSM4IKID5KQ
.

请使用阳间的方式沟通,总这么发真的没意思,
你说的这种方式无异于

int a;
int b;
int c;
int d(int){ /* do something */ return 0; }
inf e(int){ /* do something */ return 0; }
inf f(int){ /* do something */ return 0; }

既然你想比较内存优劣请先告诉我这种写法和你那种写法有什么区别,难道这么写就不会占用内存了?请不要和我说你可以不写其他的变量和函数弄成了

int a;
int d(int){ /* do something */ return 0; }

我也可以说为什么我相关的一系列操作不会使用下面的简洁方式呢?

typedef struct type {
    int a;
    int (*d)(int);
}

如果你认为你写的是4K Flash内存的上古芯片,那麻烦老老实实用汇编什么高级语言都别用,最好是没有任何过程调用没有栈帧分配。

除此之外,如果是想好好讨论问题请放在相同的问题域进行讨论,现在在讨论的是用任何语言都能实现面向对象范式,在实现相同功能的情况下大家的执行效率没有明显差异,而不是我使用了封装你非要插入无限多的无用字段然后跟我说:“看,你这个东西占用了无限大的内存,我的只需要一个变量就能完成”,在我眼里就跟有人在跟我说:“火箭这玩意得烧煤,最好还是水洗煤”。

XSF notifications@github.com 于2021年2月3日周三 下午2:41写道:

即使是-O3也是同样的
.file "main.c"
.text
.p2align 4
.globl foo
.type foo, @function
foo:
.LFB0:
.cfi_startproc
ret
.cfi_endproc
.LFE0:
.size foo, .-foo
.p2align 4
.globl bar
.type bar, @function
bar:
.LFB4:
.cfi_startproc
ret
.cfi_endproc
.LFE4:
.size bar, .-bar
.section .text.startup,"ax",@progbits
.p2align 4
.globl main
.type main, @function
main:
.LFB2:
.cfi_startproc
xorl %eax, %eax
ret
.cfi_endproc
.LFE2:
.size main, .-main
.ident "GCC: (GNU) 10.2.0"
.section .note.GNU-stack,"",@progbits
除非你对函数签名进行修饰
static void foo() {}static void bar() {}
int main(int argc, char **argv) {
return 0;
}
明确声明它是不会导出给外部进行调用的函数
.file "main.c"
.text
.section .text.startup,"ax",@progbits
.p2align 4
.globl main
.type main, @function
main:
.LFB2:
.cfi_startproc
xorl %eax, %eax
ret
.cfi_endproc
.LFE2:
.size main, .-main
.ident "GCC: (GNU) 10.2.0"
.section .note.GNU-stack,"",@progbits
只要有非static的函数使用了static的函数,那么static的函数就会被重新编译(未使用O3,因为O3会将static函数内联,从而直接
return)
static void foo() {}void bar() { foo(); }
int main(int argc, char **argv) {
return 0;
}
.file "main.c"
.text
.type foo, @function
foo:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
nop
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size foo, .-foo
.globl bar
.type bar, @function
bar:
.LFB1:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl $0, %eax
call foo
nop
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE1:
.size bar, .-bar
.globl main
.type main, @function
main:
.LFB2:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl %edi, -4(%rbp)
movq %rsi, -16(%rbp)
movl $0, %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE2:
.size main, .-main
.ident "GCC: (GNU) 10.2.0"
.section .note.GNU-stack,"",@progbits
当然你可以用static修饰所有不想暴露给别人的函数,但是不觉得这样做就与拿来别人的库来用相违背吗?如果我是给别人用的库,那么我就不可能用static修饰,那么不论我用什么构造函数或不用构造函数都会编译;如果我是给别人用的库,那么我用static修饰了,并且没有使用非
static的函数来调用它,很开心,他没有编译,那么这个函数存在的意义是什么呢?
求教一下,你定义一个结构体
struck xxx
{
int a;
int b;
int c;
int (*d)(int);
int (*e)(int);
int (*f)(int);
}
然后声明 xxx
xxx1,但是实际上你又只用了一个d函数,d函数只和a变量相关,也就是说实际你只用到了a变量和b函数。但是你却声明了xxx1,xxx1占用的内存会比你实际使用的部分大,这应该没错吧?

You are receiving this because you commented.
Reply to this email directly, view it on GitHub
#431 (comment),
or unsubscribe
https://github.com/notifications/unsubscribe-auth/AKYAXFARCLFQM6URG2WB7X3S5DVX5ANCNFSM4IKID5KQ
.

不好意思哈,其实我前面发的刚写完我就都删掉了,因为我对自己回复的结论也不太确定,不知道为什么删掉了你能看见。既然你连着两次都看到了,那我这次不删了

我是这样想的,因为我们面向对象会复用别人或者自己已经写好的类,那在这种情况下,一个类中确确实实有很多你其实并不需要的部分,另外面向对象更改属性也需要通过函数,面向对象为了大型项目多人合作规矩多点,这种编程范式内存怎么想都会占用的大些吧,难道世上真有一个东西只有好处没有一点坏处???

我主要就是回复楼主,没道理两种编程范式性能一点差别都没有吧,要么面向对象性能强点,要么面向过程强点,反正不可能两个内存占用一毛一样吧

可能是我的错觉,我感觉你在不知不觉中认为我说面向过程更加优越,认为我是在贬低面向对象,其实不是这么回事,我只是实事求是的表达自己对两种语言的看法,优缺点,并没有个人喜好在里面,我看情况都会用。

你这一句也跑题了(现在在讨论的是用任何语言都能实现面向对象范式,在实现相同功能的情况下大家的执行效率没有明显差异)这楼难道不是哪个编程范式性能会高点吗?

4k flash的芯片不是上古啊,单片机中挺常见的,就算不是4k,20k也会优先面向过程吧,单片机领域真的很常见。

我回顾了下我的第一个回复,主要表述的就是内存占用确实会大些,编程范式确实会影响内存。其实这玩意不就像写作吗?写法都不同没理由长度还刚好一样吧。但是,但是,我再再再,强调一次,我没说面向过程比面向对象优越。纯粹的客观讨论,内存占用

再强调一下,大工程要我选我也用面向对象,我回复的是内存的占用吧,就算面向过程内存占用少性能高,我也不可能在大工程里用啊,我不可能给自己找麻烦吧。但是讨论问题事情总要实事求对吧,不可能因为我喜欢用面向对象,就说注重“阅读“”复用”“扩展”的面向对象的内存占用和更紧凑的面向过程也就是算法数据结构的内存占用一致是吧。

千万别跑题,我纯粹的再说内存占用,没有贬低面向对象。和你眼中的“火箭”之类的毛线关系都没有,根据工程来使用,资源少的单片机直接面向过程c,甚至迫不得已写汇编(老板特别抠就会出现这种结果),大工程面向对象。

@q809935302
Copy link

硬件场合下决定是否使用某语言的关键因素并不是性能因素,而是这个语言是否是实时性的,而不是使用了何种编程范式。

对于C语言和C++语言来说,所有内存资源的申请与释放都是手动进行的,所有的指令都是以程序员期望的顺序以相同的指令周期顺序执行。

对于Java语言和Golang语言等带自动垃圾回收语言来说,程序会在不适当的时机暂停线程来执行垃圾回收。对于某些任务来说可能期望在几个指令周期内接受所有外部信号并处理完成,在这种情况下暂停线程并执行垃圾回收就会导致信号的丢失。

至于说面向对象与面向过程的性能差异,我上面给出了使用C语言实现面向对象的示例了,而在Linux内核中也大量使用OOP范式来实现对硬件资源的抽象,所以说性能差异是来自于这个语言的执行机制,而不是这个语言采用的

硬件场合下决定是否使用某语言的关键因素并不是性能因素,而是这个语言是否是实时性的,而不是使用了何种编程范式。

对于C语言和C++语言来说,所有内存资源的申请与释放都是手动进行的,所有的指令都是以程序员期望的顺序以相同的指令周期顺序执行。

对于Java语言和Golang语言等带自动垃圾回收语言来说,程序会在不适当的时机暂停线程来执行垃圾回收。对于某些任务来说可能期望在几个指令周期内接受所有外部信号并处理完成,在这种情况下暂停线程并执行垃圾回收就会导致信号的丢失。

至于说面向对象与面向过程的性能差异,我上面给出了使用C语言实现面向对象的示例了,而在Linux内核中也大量使用OOP范式来实现对硬件资源的抽象,所以说性能差异是来自于这个语言的执行机制,而不是这个语言采用的编程范式。

//=========================================================
我之前两条确实刚写完就删了,因为发完之后觉得自己这半吊子把内存占用多些说得肯定不好。

因为不名原因删除了你还能看见,所以干脆向你请教,纯学习讨论目的

我之前说的话也是结合自己用c去仿写面向对象类,然后我自己使用的过程中的疑惑。但是我并不是很确定以自己的结论(所以删了)。如果你有好的解决方法,还希望告知,我给项目中的单片机专门写了类库,写的还挺全面的,实际使用的时候才发现声明结构体的时候一些我并不打算用的功能也被一起声明了。但是确确实实这样以对象的形式写,很方便,用的时候一个声明,一个构造函数就可以随便用了。我要是面向对象的搞法,做成功能函数,形参会特别长,还要我自己去定义各种专门的数据搭配功能

纯粹学习请教,如何解决这类问题呢?

@mjiuming
Copy link
mjiuming commented Feb 4, 2021
  1. 执行性能面向对象与面向过程基本没有区别 Link
  2. 实现相同功能集的情况下,面向过程与面向过程内存调用基本一致,不会出现面向过程不编译,面向对象就多编译了的情况Link
  3. 现在在讨论的是用任何语言都能实现面向对象范式,在实现相同功能的情况下大家的执行效率没有明显差异,大家指的就是面向对象和面向过程,因为最上面就有人用Java和C比较,所以要强调不止Java能实现面向对象,C也可以
  4. 即使是面向过程也会有使用结构体来组合多个对象的情况,面向过程的情况下是一个函数就能实现完整的功能,所以你可以为每个函数定义一个结构,就好像你都恰到好处的定义了你所需要的数据,但是即使面向过程也会出现第三方库有很多你不需要的数据的问题,以单片机引脚为例
struct {
     int _1: 1;
     int _2: 1;
     int _3: 1;
     int _4: 1;
     int _5: 1;
     int _6: 1;
     int _7: 1;
     int _8: 1;
} P;

// 单片机写法
void main() {
    P._1 = 1; // P1引脚高电平
}

依旧会出现你所说的我只需要P1引脚,但是我要创建整个实例的问题。这个例子没有说面向过程的优劣,只是说你说的这个问题是个通用的问题,不是说面向对象有,面向过程就没有了

@mjiuming
Copy link
mjiuming commented Feb 4, 2021

硬件场合下决定是否使用某语言的关键因素并不是性能因素,而是这个语言是否是实时性的,而不是使用了何种编程范式。
对于C语言和C++语言来说,所有内存资源的申请与释放都是手动进行的,所有的指令都是以程序员期望的顺序以相同的指令周期顺序执行。
对于Java语言和Golang语言等带自动垃圾回收语言来说,程序会在不适当的时机暂停线程来执行垃圾回收。对于某些任务来说可能期望在几个指令周期内接受所有外部信号并处理完成,在这种情况下暂停线程并执行垃圾回收就会导致信号的丢失。
至于说面向对象与面向过程的性能差异,我上面给出了使用C语言实现面向对象的示例了,而在Linux内核中也大量使用OOP范式来实现对硬件资源的抽象,所以说性能差异是来自于这个语言的执行机制,而不是这个语言采用的

硬件场合下决定是否使用某语言的关键因素并不是性能因素,而是这个语言是否是实时性的,而不是使用了何种编程范式。
对于C语言和C++语言来说,所有内存资源的申请与释放都是手动进行的,所有的指令都是以程序员期望的顺序以相同的指令周期顺序执行。
对于Java语言和Golang语言等带自动垃圾回收语言来说,程序会在不适当的时机暂停线程来执行垃圾回收。对于某些任务来说可能期望在几个指令周期内接受所有外部信号并处理完成,在这种情况下暂停线程并执行垃圾回收就会导致信号的丢失。
至于说面向对象与面向过程的性能差异,我上面给出了使用C语言实现面向对象的示例了,而在Linux内核中也大量使用OOP范式来实现对硬件资源的抽象,所以说性能差异是来自于这个语言的执行机制,而不是这个语言采用的编程范式。

//=========================================================
我之前两条确实刚写完就删了,因为发完之后觉得自己这半吊子把内存占用多些说得肯定不好。

因为不名原因删除了你还能看见,所以干脆向你请教,纯学习讨论目的

我之前说的话也是结合自己用c去仿写面向对象类,然后我自己使用的过程中的疑惑。但是我并不是很确定以自己的结论(所以删了)。如果你有好的解决方法,还希望告知,我给项目中的单片机专门写了类库,写的还挺全面的,实际使用的时候才发现声明结构体的时候一些我并不打算用的功能也被一起声明了。但是确确实实这样以对象的形式写,很方便,用的时候一个声明,一个构造函数就可以随便用了。我要是面向对象的搞法,做成功能函数,形参会特别长,还要我自己去定义各种专门的数据搭配功能

纯粹学习请教,如何解决这类问题呢?

这个问题的最简单回答依旧还是"It depends"。为了实现某个功能,或解决某个问题,在对象的整个生命周期内,所有的字段/属性都被用到了,那就是没有浪费,实现相同的功能与面向过程是逻辑等价的,只是数据的组织形式发生了变化。如果在对象的整个生命周期内,某些字段/属性没有被用到,那就是浪费了,使用面向对象确实是会比面向过程浪费很多的内存。
至于说对象的生命周期的概念可以考虑说二叉树数据结构,不可能说只创建二叉树而不添加,不删除,不销毁。

struct TreeNode;
typedef struct TreeNode TreeNode; 
struct TreeNode {
    TreeNode *left;
    TreeNode *right;
    int element;
};
TreeNode *binary_tree_create();
TreeNode *binary_tree_add(TreeNode *, int element);
TreeNode *binary_tree_remove(TreeNode *, int element);
void binary_tree_destroy(TreeNode *);
TreeNode *binary_search_tree_create();
TreeNode *binary_search_tree_add(TreeNode *, int element);
TreeNode *binary_search_tree_remove(TreeNode *, int element);
void binary_search_tree_destroy(TreeNode *);

int main(int argc, char **argv) {
    /* something uses create/add/remove/destroy */
}

对应的面向对象方式

struct TreeNode;
typedef struct TreeNode TreeNode; 
struct TreeNode {
    TreeNode *left;
    TreeNode *right;
    int element;
    TreeNode *(*add)(TreeNode *, int element);
    TreeNode *(*remove)(TreeNode *, int element);
};

static TreeNode *binary_tree_add(TreeNode *, int element);
static TreeNode *binary_tree_remove(TreeNode *, int element);
static void binary_tree_destroy(TreeNode *);
TreeNode *binary_tree_create() {
    TreeNode *tree_node /* = something alloc memory */;
    tree_node.add = binary_tree_add;
    tree_node.remove = binary_tree_remove;
}

static TreeNode *binary_search_tree_add(TreeNode *, int element);
static TreeNode *binary_search_tree_remove(TreeNode *, int element);
static void binary_search_tree_destroy(TreeNode *);
TreeNode *binary_search_tree_create() {
    TreeNode *tree_node /* = something alloc memory */;
    tree_node.add = binary_search_tree_add;
    tree_node.remove = binary_search_tree_remove;
}

int main(int argc, char **argv) {
    /* something uses create/add/remove/destroy */
}

@q809935302
Copy link
q809935302 commented Feb 4, 2021 via email

@q809935302
Copy link
q809935302 commented Feb 4, 2021 via email

@OceanWild
Copy link

区别在于编译过程的复杂度,像Java这种面相对象的编程语言中间多了一步编译字节码。

@mjiuming
Copy link
mjiuming commented Dec 16, 2021 via email

@smallironman666
Copy link

我觉得这个得分情况而定,c和c++就是很好的例子,两种语言执行效率差距其实并不大,但是两种语言的设计思想就不同。

c和c++的运行机制是一样的。都是编译成机械码。

这里说的应该是,C是面向过程的,C++是面向对象的吧,两种语言的都是编译型语言

@Snailclimb Snailclimb added discuss discuss a problem perfect Improve knowledge points or descriptions labels Apr 8, 2022
@Rehoni
Copy link
Rehoni commented Apr 11, 2022

所以按我的理解是,面向过程和面向对象只是思想,不存在两者之间比较效率和性能的情况,还是得拿出场景具体情况具体分析。

@Jacklijames
Copy link

学习了

@SandroDickens
Copy link

Java的性能损失有两个地方:
1.字节码不是最终可执行机器码,虽然有JIT但不是所有字节码都会被JIT
2.JVM内存模型本身的弊端,将太多数据存放到堆而不是栈
面向对象与面向过程之间的性能损失叫做抽象化损失这个概念是C++委员会提出来的,而且抽象化损失仅存在于运行时的多态,编译时的多态是不存在抽象化损失的。
C++委员会一直追求标准库的抽象化损失不能比直接用C实现差,实际上对于绝大部分程序员,他们编写的C代码性能和效率是远超C++的抽象化损失的
所以你会发现STL和Boost里面很多数据结构的性能甚至比你直接用C语言自己造轮子要好,除非你选错了数据结构

@Hij888
Copy link
Hij888 commented Jul 17, 2022

学习了

@aaaabcc2
Copy link

学习了 谢谢

@HsxDeGitHub
Copy link

学习了,学习了

@cloudtailor-cyh
Copy link

学习了, 收获很大

@jamin77777
Copy link

C和C++的编译原理是一样的,不适合拿C和C++举例,java效率慢还有一个原因就是分配内存的问题,面向过程的语言大多都是自己分配内存,而面向对象的语言通常是自动帮你分配内存,这里可能就会导致分配内存不当,导致比面向过程的语言占用更多的内存,从而影响运行效率。

@jamin77777
Copy link
jamin77777 commented Mar 19, 2023 via email

@SandroDickens
Copy link

我是看出来了,你是完全不懂JVM,或者你只是背了一些错误过时的八股文。
另外Java占用内存多也不是你说的原因。Java对象本身占用的空间就比C++大,不过Java的内存回收机制才是主要原因
没有任何人有义务去给你科普,给你科普那是情分,不给你科普那是本分,想知道正确答案的话去看看HotSpot

C和C++的编译原理是一样的,不适合拿C和C++举例,java效率慢还有一个原因就是分配内存的问题,面向过程的语言大多都是自己分配内存,而面向对象的语言通常是自动帮你分配内存,这里可能就会导致分配内存不当,导致比面向过程的语言占用更多的内存,从而影响运行效率。

@jamin77777
Copy link
jamin77777 commented Mar 19, 2023 via email

@SandroDickens
Copy link

就你这点知识量就别出来回复别人了,我都替你觉得丢脸。无知也就算了,还没教养,你是别人儿子吗?别人有义务给你科普?煞笔又不是我生的,我没必要教育它

@jamin77777
Copy link
jamin77777 commented Mar 30, 2023 via email

@ChinaFLTV
Copy link

面向过程和面向对象这两个概念是语言层面的,真正比较性能的高低,我们应该去比较其底层运行机理(这里我们比较的指标是代码运行模式:全量编译?部分编译部分解释?全量解释?)是运行机理的不同才导致的其性能的差距。
换言之,如果你面向过程采用的是全量解释,而面向对象采用的是全量编译,那么在这种情况下,面向对象的性能还优于面向过程呢

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
discuss discuss a problem enhancement New feature or request or suggestion perfect Improve knowledge points or descriptions
Projects
None yet
Development

No branches or pull requests