reliability of pin change interrupt

How reliable is pin change interrupt? Is it able to raise interrupt for frequency as high as 100Khz?

From the logic diagram in the datasheet, it looks pretty certain that the PCI hardware can detect changes that are about 4 or 5 clocks long (call it 3 MHz? I couldn't find a detailed specification.)
Note that this is a lot different than any given interrupt service routine being able to EXECUTE 3MHz, or even 100kHz. That depends on how short the ISR is. 100kHz interrupts while using typical Arduino code would be very unlikely, IMO.
If you have a signal coming in at 1MHz, you'd probably see continuous interrupts limited only by the speed of the ISR (with one non-ISR instruction executed between each interrupt.)

I saw that in datasheet that it takes 3 clock cycles to move to the ISR. So, I should be able to count frequency(100Khz) with accuracy but it is not the case. I get errors of 100Hz here.

So, I should be able to count frequency(100Khz) with accuracy but it is not the case.

If only we could see the code you're using.

sodhani:
I saw that in datasheet that it takes 3 clock cycles to move to the ISR. So, I should be able to count frequency(100Khz) with accuracy but it is not the case. I get errors of 100Hz here.

3 clock cycles to initiate the ISR for every pulse, but then how many cycles does it take to execute your isr and then move back to the regular code? And how are you calculating the time, what processor, what clock speed, and oh yeah, what's your code?

My datasheet said 4 cycles to preserve the PC and status registers to the stack (e.g. automatic, no push or pop needed). It is an old datasheet so maybe it has an error.

I think the compiler adds instructions to preserve some of the other registers that may be used. I am not sure how smart the compiler is when doing this.

avr-objdump with options -h and -S can show a listing of what the compiler did. So looking at one of the interrupt vectors this is what I see.

00001a18 <__vector_16>:
#if defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
ISR(TIM0_OVF_vect)
#else
ISR(TIMER0_OVF_vect)
#endif
{
    1a18:	1f 92       	push	r1
    1a1a:	0f 92       	push	r0
    1a1c:	0f b6       	in	r0, 0x3f	; 63
    1a1e:	0f 92       	push	r0
    1a20:	11 24       	eor	r1, r1
    1a22:	2f 93       	push	r18
    1a24:	3f 93       	push	r19
    1a26:	8f 93       	push	r24
    1a28:	9f 93       	push	r25
    1a2a:	af 93       	push	r26
    1a2c:	bf 93       	push	r27
    // copy these to local variables so they can be stored in registers
    // (volatile variables must be read from memory on every access)
    unsigned long m = timer0_millis;
    1a2e:	80 91 31 01 	lds	r24, 0x0131	; 0x800131 <timer0_millis>
    1a32:	90 91 32 01 	lds	r25, 0x0132	; 0x800132 <timer0_millis+0x1>
    1a36:	a0 91 33 01 	lds	r26, 0x0133	; 0x800133 <timer0_millis+0x2>
    1a3a:	b0 91 34 01 	lds	r27, 0x0134	; 0x800134 <timer0_millis+0x3>
    unsigned char f = timer0_fract;
    1a3e:	30 91 30 01 	lds	r19, 0x0130	; 0x800130 <timer0_fract>

    m += MILLIS_INC;
    f += FRACT_INC;
    1a42:	23 e0       	ldi	r18, 0x03	; 3
    1a44:	23 0f       	add	r18, r19
    if (f >= FRACT_MAX) {
    1a46:	2d 37       	cpi	r18, 0x7D	; 125
    1a48:	20 f4       	brcc	.+8      	; 0x1a52 <__vector_16+0x3a>
    // copy these to local variables so they can be stored in registers
    // (volatile variables must be read from memory on every access)
    unsigned long m = timer0_millis;
    unsigned char f = timer0_fract;

    m += MILLIS_INC;
    1a4a:	01 96       	adiw	r24, 0x01	; 1
    1a4c:	a1 1d       	adc	r26, r1
    1a4e:	b1 1d       	adc	r27, r1
    1a50:	05 c0       	rjmp	.+10     	; 0x1a5c <__vector_16+0x44>
    f += FRACT_INC;
    if (f >= FRACT_MAX) {
        f -= FRACT_MAX;
    1a52:	26 e8       	ldi	r18, 0x86	; 134
    1a54:	23 0f       	add	r18, r19
        m += 1;
    1a56:	02 96       	adiw	r24, 0x02	; 2
    1a58:	a1 1d       	adc	r26, r1
    1a5a:	b1 1d       	adc	r27, r1
    }

    timer0_fract = f;
    1a5c:	20 93 30 01 	sts	0x0130, r18	; 0x800130 <timer0_fract>
    timer0_millis = m;
    1a60:	80 93 31 01 	sts	0x0131, r24	; 0x800131 <timer0_millis>
    1a64:	90 93 32 01 	sts	0x0132, r25	; 0x800132 <timer0_millis+0x1>
    1a68:	a0 93 33 01 	sts	0x0133, r26	; 0x800133 <timer0_millis+0x2>
    1a6c:	b0 93 34 01 	sts	0x0134, r27	; 0x800134 <timer0_millis+0x3>
    timer0_overflow_count++;
    1a70:	80 91 35 01 	lds	r24, 0x0135	; 0x800135 <timer0_overflow_count>
    1a74:	90 91 36 01 	lds	r25, 0x0136	; 0x800136 <timer0_overflow_count+0x1>
    1a78:	a0 91 37 01 	lds	r26, 0x0137	; 0x800137 <timer0_overflow_count+0x2>
    1a7c:	b0 91 38 01 	lds	r27, 0x0138	; 0x800138 <timer0_overflow_count+0x3>
    1a80:	01 96       	adiw	r24, 0x01	; 1
    1a82:	a1 1d       	adc	r26, r1
    1a84:	b1 1d       	adc	r27, r1
    1a86:	80 93 35 01 	sts	0x0135, r24	; 0x800135 <timer0_overflow_count>
    1a8a:	90 93 36 01 	sts	0x0136, r25	; 0x800136 <timer0_overflow_count+0x1>
    1a8e:	a0 93 37 01 	sts	0x0137, r26	; 0x800137 <timer0_overflow_count+0x2>
    1a92:	b0 93 38 01 	sts	0x0138, r27	; 0x800138 <timer0_overflow_count+0x3>
}
    1a96:	bf 91       	pop	r27
    1a98:	af 91       	pop	r26
    1a9a:	9f 91       	pop	r25
    1a9c:	8f 91       	pop	r24
    1a9e:	3f 91       	pop	r19
    1aa0:	2f 91       	pop	r18
    1aa2:	0f 90       	pop	r0
    1aa4:	0f be       	out	0x3f, r0	; 63
    1aa6:	0f 90       	pop	r0
    1aa8:	1f 90       	pop	r1
    1aaa:	18 95       	reti

00001aac <millis>:

It added twelve instructions to preserve the state of the machine before any of the ISR function code runs. Then before the reti instruction a lot of pop instructions that recover the machine state.

Anyway, I would not expect much more than about 10k events per second from these 16MHz AVRs while still having some time for other tasks.

Thinking about this, if the speed is an issue (and it sounds like we're on the edge of this) - I would probably configure a timer that supported external clock to be clocked off the input signal; that way you you could set it to a target number of counts, and use the timer overflow interrupt to grab a timestamp.... That way during the counting, the arduino isn't doing anything, the timer is just ticking away...