アラインメントの効果は本当?

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
菅沢

アラインメントの効果は本当?

#1

投稿記事 by 菅沢 » 14年前

便利さと関係なく、単純にアクセス速度を考えたいです。
理論上(一般論)として、下記の例で言えば、
アラインメントのお陰で、早くなるかどうか[実際は処理系に依存かもしれないですが]。
struct st {
int no;
char name[13];
double average;
} s;

アラインメントのお陰で、
int i = s.no;
char c = s.name[n];
double d = s.average;
は下記のケースと比べば、アクセス速度が早いのでしょうか。

char a[sizeof(int)+13+sizeof(double)];

int i = (int)a[0];
char c = a[sizeof(int)];
double d = (double)a[sizeof(int)+13];

アバター
みけCAT
記事: 6734
登録日時: 15年前
住所: 千葉県
連絡を取る:

Re: 菅沢

#2

投稿記事 by みけCAT » 14年前

アラインメントの効果は本当? さんが書きました:int i = s.no;
char c = s.name[n];
double d = s.average;
アラインメントの効果は本当? さんが書きました:char a[sizeof(int)+13+sizeof(double)];

int i = (int)a[0];
char c = a[sizeof(int)];
double d = (double)a[sizeof(int)+13];
この2個のコードは同じ働きではありません。
よって比較は意味がないのではないでしょうか?
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

菅沢

Re: 菅沢

#3

投稿記事 by 菅沢 » 14年前

ごめんなさい!!
出直します。

便利さと関係なく、単純にアクセス速度を考えたいです。
理論上(一般論)として、下記の例で言えば、
アラインメントのお陰で、早くなるかどうか[実際は処理系に依存かもしれないですが]。
struct st {
int no;
char name[13];
double average;
} s;

アラインメントのお陰で、
int i = s.no;
char c = s.name[0];
double d = s.average;
は下記のケースと比べば、アクセス速度が早いのでしょうか。

char a[sizeof(int)+13+sizeof(double)];

int i = (int)a[0];
char c = a[sizeof(int)];
double d = (double)a[sizeof(int)+13];

beatle
記事: 1281
登録日時: 14年前
住所: 埼玉
連絡を取る:

Re: 菅沢

#4

投稿記事 by beatle » 14年前

みけCATさんも言っているように,2つのコードは外面的な働きがまったく異なります.それはなぜか?

この2つを比べてみましょう

コード:

int i1 = s.no;
int i2 = (int)a[0];
i1には,s.noが表す値(4バイトの整数)が入ります.これは恐らく意図通り.(intが4バイトと仮定)
i2には,a[0]が表す値(1バイトの整数)がint型にキャストされてから入ります.

i1に入りうる最大の値はint型の最大値ですが,i2に入りうる最大の値はchar型の最大値です.

これで,2つの比較が意味をなさないのでは?というみけCATさんの疑問の真意が理解できますか?

アバター
みけCAT
記事: 6734
登録日時: 15年前
住所: 千葉県
連絡を取る:

Re: 菅沢

#5

投稿記事 by みけCAT » 14年前

最初の書き込みと「出直し」た書き込み、文章が全く同じですね。
きついことを言って申し訳ありませんが、出直した意味が全くありません。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

菅沢

再度出直します

#6

投稿記事 by 菅沢 » 14年前

再度出直します[何度も何度もほんとうに悪かったのです!]
========================================================================
便利さと関係なく、単純にアクセス速度を考えたいです。
理論上(一般論)として、下記の例で言えば、
アラインメントのお陰で、早くなるかどうか[実際は処理系に依存かもしれないですが]。
struct st {
int no;
char name[13];
double average;
} s;

アラインメントのお陰で、
int i = s.no;
char c = s.name[0];
double d = s.average;
は下記のケースと比べば、アクセス速度が早いのでしょうか。

char a[sizeof(int)+13+sizeof(double)];// さらにこの中にint data 先頭に一つ、そのつぎにchar 13個、それからdouble 一つ設定済みと仮定

int i = *(int&)a;
char c = a[sizeof(int)];
double d = *(double&)(a + sizeof(int) + 13);

beatle
記事: 1281
登録日時: 14年前
住所: 埼玉
連絡を取る:

Re: 菅沢

#7

投稿記事 by beatle » 14年前

このケースであれば,アライメントされている方が速くなる可能性が高いと思いますね.
配列aを使う後者の方は,CPUによっては例外が発生して動かない場合もあると思います.
参考:http://www5d.biglobe.ne.jp/~noocyte/Pro ... nment.html

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 15年前
住所: 東海地方
連絡を取る:

Re: 菅沢

#8

投稿記事 by softya(ソフト屋) » 14年前

三度とも内容は何も変わっていません。それとcodeタグをご利用下さい。 → http://dixq.net/board/board.html#k10

一般論としてアライメントされている方が早い可能性がありますが、今時のCPUはデータ・キャッシュされるので差がない可能性の方が大きいです。
ただ、今のコードはみなさんが書かれている通り等価な処理ではないので比較対象として不的確な上にコンパイルエラーまで出ます。

環境依存ですが、VC++の場合
#pragma pack(1)
でアライメントを変更可能ですので、そちらを使って同じ形の構造体を使って比較した方が良いと思います。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

菅沢

アラインメントの効果は本当?

#9

投稿記事 by 菅沢 » 14年前

みなさんいろいろご教授ありがとうございます。
最後の出直し[今度こそ!]----本当に申し訳ございませんでした
========================================================================
便利さと関係なく、単純にアクセス速度を考えたいです。
理論上(一般論)として、下記の例で言えば、
アラインメントのお陰で、早くなるかどうか[実際は処理系に依存かもしれないですが]。

コード:

struct st {
int no;
char name[13];
double average;
} s;
アラインメントのお陰で、
int i = s.no;
char c = s.name[0];
double d = s.average;
は下記のケースと比べば、アクセス速度が早いのでしょうか。

char a[sizeof(int)+13+sizeof(double)];// さらにこの中にint data 先頭に一つ、そのつぎにchar 13個、それからdouble 一つ設定済みと仮定

int i = *(int*)a;
char c = a[sizeof(int)];
double d = *(double*)(a + sizeof(int) + 13);

アバター
h2so5
副管理人
記事: 2212
登録日時: 15年前
住所: 東京
連絡を取る:

Re: 菅沢

#10

投稿記事 by h2so5 » 14年前

そんなに拘るのなら、ご自分で実際に計測されてはいかかでしょうか?

管沢

アラインメントの効果は本当?

#11

投稿記事 by 管沢 » 14年前

h2so5 さんが書きました:そんなに拘るのなら、ご自分で実際に計測されてはいかかでしょうか?
一般的な結論というか理論的な見地からの情報を得たいですね。
いまはCテキストを読むだけが、この後実機練習しようとしています。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 15年前
住所: 東海地方
連絡を取る:

Re: 菅沢

#12

投稿記事 by softya(ソフト屋) » 14年前

こういう物は環境を限定しないと成立しないので環境を限定します。それでもINTELのCPUだとしても型番で動作が異なる可能性があります。あるいはコンパイラ毎に機械語コードも変わります。
機械語コードが同じでもCPU種類毎に動作が異なる可能性があり論理的予想は外れる可能性があるので、論理的予想は大体の場合無意味です。なので実測に基づいた方がより建設的です。

[補足]推測を立てて実測することは有意義ですが、推測だけで延々話していても現実は違うかも知れません。ぜひ推測の上実測されることをお勧めします。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 15年前
住所: 東海地方
連絡を取る:

Re: 菅沢

#13

投稿記事 by softya(ソフト屋) » 14年前

ちなみに、このコード自体を最大最適化レベルでコンパイルすると
環境:cygwin コンパイラ:gcc 最適化:-O3

コード:

#include <stdio.h>
#include <time.h>

//	構造体を利用。
struct st {
	int no;
	char name[13];
	double average;
} s = {0};
char array[sizeof(int)+13+sizeof(double)];

void subA(int loops)
{
	int i,c,d,l;
	
	for( l=0 ; l<loops ; l++ ) {
		i = s.no;
		c = s.name[0];
		d = s.average;
	}
}


void subB(int loops)
{
	int i,c,d,l;
	//	配列を強引にアクセス
	
	for( l=0 ; l<loops ; l++ ) {
		i = *(int*)array;
		c = array[sizeof(int)];
		d = *(double*)(array + sizeof(int) + 13);
	}
}

int main(){
	int ms1,ms2;
	struct timespec ts;
	
    //	計測A
	clock_gettime(CLOCK_REALTIME, &ts);
	ms1 = ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
    subA(10000000);
	clock_gettime(CLOCK_REALTIME, &ts);
	ms2 = ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
    printf("time A: %dms\n", ms2-ms1);
    
    //	計測B
	clock_gettime(CLOCK_REALTIME, &ts);
	ms1 = ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
    subB(10000000);
	clock_gettime(CLOCK_REALTIME, &ts);
	ms2 = ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
    printf("time B: %dms\n", ms2-ms1);
    
    return 0;
}
アセンブルコードは、最適化されすぎて意味のないコードになります。

コード:

	.file	"a.c"
	.text
	.p2align 4,,15
.globl _subA
	.def	_subA;	.scl	2;	.type	32;	.endef
_subA:
	pushl	%ebp
	movl	%esp, %ebp
	popl	%ebp
	ret
	.p2align 4,,15
.globl _subB
	.def	_subB;	.scl	2;	.type	32;	.endef
_subB:
	pushl	%ebp
	movl	%esp, %ebp
	popl	%ebp
	ret
	.def	___main;	.scl	2;	.type	32;	.endef
	.section .rdata,"dr"
LC0:
	.ascii "time A: %dms\12\0"
LC1:
	.ascii "time B: %dms\12\0"
	.text
	.p2align 4,,15
.globl _main
	.def	_main;	.scl	2;	.type	32;	.endef
_main:
	leal	4(%esp), %ecx
	andl	$-16, %esp
	pushl	-4(%ecx)
	pushl	%ebp
	movl	%esp, %ebp
	subl	$56, %esp
	movl	%edi, -4(%ebp)
	leal	-24(%ebp), %edi
	movl	%ecx, -16(%ebp)
	movl	%ebx, -12(%ebp)
	movl	%esi, -8(%ebp)
	call	___main
	movl	%edi, 4(%esp)
	movl	$1, (%esp)
	call	_clock_gettime
	movl	-20(%ebp), %ebx
	movl	-24(%ebp), %esi
	movl	%edi, 4(%esp)
	movl	$1, (%esp)
	call	_clock_gettime
	movl	-20(%ebp), %eax
	imull	$1000, %esi, %esi
	movl	%eax, -36(%ebp)
	movl	$1125899907, %eax
	imull	-36(%ebp)
	imull	$1000, -24(%ebp), %eax
	sarl	$31, -36(%ebp)
	sarl	$18, %edx
	subl	-36(%ebp), %edx
	movl	$LC0, (%esp)
	leal	(%edx,%eax), %ecx
	movl	$1125899907, %eax
	imull	%ebx
	sarl	$31, %ebx
	sarl	$18, %edx
	subl	%ebx, %edx
	subl	%edx, %ecx
	subl	%esi, %ecx
	movl	%ecx, 4(%esp)
	call	_printf
	movl	%edi, 4(%esp)
	movl	$1, (%esp)
	call	_clock_gettime
	movl	-20(%ebp), %ebx
	movl	-24(%ebp), %esi
	movl	%edi, 4(%esp)
	movl	$1, (%esp)
	call	_clock_gettime
	movl	-20(%ebp), %edi
	movl	$1125899907, %eax
	imull	$1000, %esi, %esi
	imull	%edi
	imull	$1000, -24(%ebp), %eax
	sarl	$31, %edi
	movl	$LC1, (%esp)
	sarl	$18, %edx
	subl	%edi, %edx
	leal	(%edx,%eax), %ecx
	movl	$1125899907, %eax
	imull	%ebx
	subl	%esi, %ecx
	sarl	$31, %ebx
	movl	%edx, %esi
	sarl	$18, %esi
	subl	%ebx, %esi
	subl	%esi, %ecx
	movl	%ecx, 4(%esp)
	call	_printf
	movl	-16(%ebp), %ecx
	xorl	%eax, %eax
	movl	-12(%ebp), %ebx
	movl	-8(%ebp), %esi
	movl	-4(%ebp), %edi
	movl	%ebp, %esp
	popl	%ebp
	leal	-4(%ecx), %esp
	ret
.globl _s
	.bss
	.align 32
_s:
	.space 32
	.comm	_array, 25, 0
	.def	_clock_gettime;	.scl	2;	.type	32;	.endef
	.def	_printf;	.scl	2;	.type	32;	.endef
長いので一旦分けます。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 15年前
住所: 東海地方
連絡を取る:

Re: アラインメントの効果は本当?

#14

投稿記事 by softya(ソフト屋) » 14年前

じゃあ最適化しないとどうなるかと言うと、

コード:

$ ./a
time A: 163ms
time B: 159ms
優位な差は認められません。CPUのデータキャッシュが働きまくっているので意味が無いといえば意味のないコードです。
アセンブラコード自体はsubBの方がわずかに長いんですけどね。

コード:

_subA:
	pushl	%ebp
	movl	%esp, %ebp
	subl	$20, %esp
	movl	$0, -4(%ebp)
	jmp	L2
L3:
	movl	_s, %eax
	movl	%eax, -16(%ebp)
	movzbl	_s+4, %eax
	movsbl	%al,%eax
	movl	%eax, -12(%ebp)
	fldl	_s+24
	fnstcw	-18(%ebp)
	movzwl	-18(%ebp), %eax
	movb	$12, %ah
	movw	%ax, -20(%ebp)
	fldcw	-20(%ebp)
	fistpl	-8(%ebp)
	fldcw	-18(%ebp)
	addl	$1, -4(%ebp)
L2:
	movl	-4(%ebp), %eax
	cmpl	8(%ebp), %eax
	jl	L3
	leave
	ret
	

コード:

_subB:
	pushl	%ebp
	movl	%esp, %ebp
	subl	$20, %esp
	movl	$0, -4(%ebp)
	jmp	L6
L7:
	movl	$_array, %eax
	movl	(%eax), %eax
	movl	%eax, -16(%ebp)
	movzbl	_array+4, %eax
	movsbl	%al,%eax
	movl	%eax, -12(%ebp)
	movl	$_array+17, %eax
	fldl	(%eax)
	fnstcw	-18(%ebp)
	movzwl	-18(%ebp), %eax
	movb	$12, %ah
	movw	%ax, -20(%ebp)
	fldcw	-20(%ebp)
	fistpl	-8(%ebp)
	fldcw	-18(%ebp)
	addl	$1, -4(%ebp)
L6:
	movl	-4(%ebp), %eax
	cmpl	8(%ebp), %eax
	jl	L7
	leave
	ret
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

菅沢

Re: アラインメントの効果は本当?

#15

投稿記事 by 菅沢 » 14年前

softya(ソフト屋)様
いつもお世話になっております。
実験コードまで組んでいただき本当にありがとうございます。
うしろのマシンコード分りませんが、C言語の部分ではi,c,d 三つの変数を全部 int 型に定義されていますね、
これで、アラインメントの性格から生まれた両者(構造体アクセスと配列アクセス)の差は分らなくなるのではないでしょうか 。
即ち、下記のようにi,c,d をそれぞれ int, char, double 型にしなければ、この実験は無意味になるかな。?

コード:

#include <stdio.h>
#include <time.h>
 
//  構造体を利用。
struct st {
    int no;
    char name[13];
    double average;
} s = {0};
char array[sizeof(int)+13+sizeof(double)];
 
void subA(int loops)
{
    int i,c,d,l;
    
    for( l=0 ; l<loops ; l++ ) {
        int i = s.no;
        char c = s.name[0];
        double d = s.average;
    }
}
 
 
void subB(int loops)
{
    int i,c,d,l;
    //  配列を強引にアクセス
    
    for( l=0 ; l<loops ; l++ ) {
        int i = *(int*)array;
        char c = array[sizeof(int)];
        double d = *(double*)(array + sizeof(int) + 13);
    }
}
 
int main(){
    int ms1,ms2;
    struct timespec ts;
    
    //  計測A
    clock_gettime(CLOCK_REALTIME, &ts);
    ms1 = ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
    subA(10000000);
    clock_gettime(CLOCK_REALTIME, &ts);
    ms2 = ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
    printf("time A: %dms\n", ms2-ms1);
    
    //  計測B
    clock_gettime(CLOCK_REALTIME, &ts);
    ms1 = ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
    subB(10000000);
    clock_gettime(CLOCK_REALTIME, &ts);
    ms2 = ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
    printf("time B: %dms\n", ms2-ms1);
    
    return 0;
} 




beatle
記事: 1281
登録日時: 14年前
住所: 埼玉
連絡を取る:

Re: アラインメントの効果は本当?

#16

投稿記事 by beatle » 14年前

菅沢 さんが書きました: うしろのマシンコード分りませんが、C言語の部分ではi,c,d 三つの変数を全部 int 型に定義されていますね、
これで、アラインメントの性格から生まれた両者(構造体アクセスと配列アクセス)の差は分らなくなるのではないでしょうか 。
即ち、下記のようにi,c,d をそれぞれ int, char, double 型にしなければ、この実験は無意味になるかな。?
そんなことないと思います.
結局配列aにアクセスするときはアライメントされていないアドレスへアクセスするわけですから.

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 15年前
住所: 東海地方
連絡を取る:

Re: アラインメントの効果は本当?

#17

投稿記事 by softya(ソフト屋) » 14年前

[追記]申し訳ないですが、菅沢さんと言う方に心当たりがありません。名前を統一していただけると過去の話とつながるのですが・・・。
失礼しました。書き換え間違いですね。
$ ./a
time A: 26ms
time B: 32ms
差は出ましたが問題はsubAとsubBでアセンブラレベルのプログラムコードが違うということです。
subAではループ内が7命令。subBでは9命令でメモリアクセスも増えています。
これを同じにしないとアライメントの差を比較することが出来ません。

コード:

#include <stdio.h>
#include <time.h>

//	構造体を利用。
struct st {
	int no;
	char name[13];
	double average;
} s = {0};
//	配列を強引にアクセス
char array[sizeof(int)+13+sizeof(double)] = {0};

void subA(int loops)
{
	int i,l;
	char c;
	double d;
	
	//	構造体をアクセス
	for( l=0 ; l<loops ; l++ ) {
		i = s.no;
		c = s.name[0];
		d = s.average;
	}
}


void subB(int loops)
{
	int i,l;
	char c;
	double d;
	
	//	配列を強引にアクセス
	for( l=0 ; l<loops ; l++ ) {
		i = *(int*)array;
		c = array[sizeof(int)];
		d = *(double*)(array + sizeof(int) + 13);
	}
}

int main(){
	int ms1,ms2;
	struct timespec ts;
	
    //	計測A
	clock_gettime(CLOCK_REALTIME, &ts);
	ms1 = ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
    subA(10000000);
	clock_gettime(CLOCK_REALTIME, &ts);
	ms2 = ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
    printf("time A: %dms\n", ms2-ms1);
    
    //	計測B
	clock_gettime(CLOCK_REALTIME, &ts);
	ms1 = ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
    subB(10000000);
	clock_gettime(CLOCK_REALTIME, &ts);
	ms2 = ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
    printf("time B: %dms\n", ms2-ms1);
    
    return 0;
}

コード:

_subA:
	pushl	%ebp
	movl	%esp, %ebp
	subl	$32, %esp
	movl	$0, -16(%ebp)
	jmp	L2
L3:
	movl	_s, %eax
	movl	%eax, -20(%ebp)
	movzbl	_s+4, %eax
	movb	%al, -9(%ebp)
	fldl	_s+24
	fstpl	-8(%ebp)
	addl	$1, -16(%ebp)
L2:
	movl	-16(%ebp), %eax
	cmpl	8(%ebp), %eax
	jl	L3
	leave
	ret

コード:

_subB:
	pushl	%ebp
	movl	%esp, %ebp
	subl	$32, %esp
	movl	$0, -16(%ebp)
	jmp	L6
L7:
	movl	$_array, %eax
	movl	(%eax), %eax
	movl	%eax, -20(%ebp)
	movzbl	_array+4, %eax
	movb	%al, -9(%ebp)
	movl	$_array+17, %eax
	fldl	(%eax)
	fstpl	-8(%ebp)
	addl	$1, -16(%ebp)
L6:
	movl	-16(%ebp), %eax
	cmpl	8(%ebp), %eax
	jl	L7
	leave
	ret
	
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

菅沢

Re: アラインメントの効果は本当?

#18

投稿記事 by 菅沢 » 14年前

温かいご指導ありがとうございました。
これから自分が真剣に磨いて行かなければなりません。
また宜しくお願いいします。

閉鎖

“C言語何でも質問掲示板” へ戻る