I recently had to do some performance optimizations against a sorted dictionary that yielded some interesting results...
Background: I am used to using Tuples a lot, simply because they are easy to use and normally quite efficient. Please remember that Tuples were changed from structs to classes back in .NET 4.0.
Problem: A struct decreased performance!
I had a SortedDictionary that was using a Tuple as a key, so I thought "hey, I'll just change that tuple to a struct and reduce the memory usage." ...bad news, that made performance WORSE!
Why would using a struct make performance worse? It's actually quite simple and obvious when you think about it: it was causing comparisons to repeatedly box the primitive data structure, thus allocating more memory on the heap and triggering more garbage collections.
Solution: Use a struct with an IComparer.
I then created a custom struct and used that; it was must faster, but it was still causing boxing because of the non-generic IComparable interface. So finally I added a generic IComparer and passed that into my dictionary constructor; my dictionary then ran fast and efficient, causing a total of ZERO garbage collections!
See for yourself:
The Moral of the Story
Try to be aware of what default implementations are doing, and always remember that boxing to object can add up fast. Also, pay attention to the Visual Studio Diagnostics Tools window; it can be very informative!
Here is how many lines of code it took to achieve a 5x performance increase:
private struct MyStruct
{
public MyStruct(int i, string s) { I = i; S = s; }
public readonly int I;
public readonly string S;
}
private class MyStructComparer : IComparer<MyStruct>
{
public int Compare(MyStruct x, MyStruct y)
{
var c = x.I.CompareTo(y.I);
return c != 0 ? c : StringComparer.Ordinal.Compare(x.S, y.S);
}
}
Test Program
I have written some detailed comments in the Main function about what each test is doing and how it will affect performance. Let's take a look...
public static class Program
{
public static void Main(string[] args)
{
// This is going to use the class implementation
// of Tuple that was changed in .NET 4.0. It is
// be inefficient for three reasons:
// 1) It is allocating a new class on the heap
// for each and every key.
// 2) It is using the default object comparer
// to check each item, causing inefficient
// comparisons and...
// 3) Causing boxing of primitive values.
ClassTupleAsKey();
Reset();
// DURATION: 8,479 miliseconds
// This is going to use the struct implementation
// of Tuple that was available from .NET 2.0
// until 4.0. It is even less efficiente than the
// class tuple for both reasons listed above, and
// one new reason:
// 4) The StructTuple itself will be boxed when
// being passed between comparers, causing
// even more garbage collections to occur!
StructTupleAsKey();
Reset();
// DURATION: 8,880 miliseconds
// Now we move to a custom struct implementation
// for the dictionary key, but because we are using
// a SortedDictionary we still need to implement
// IComparable, which is not generic, and thus will
// be slow for one reason:
// 1) The struct itself will be boxed when being
// passed between comparers.
MyStructAsKey();
Reset();
// DURATION: 2,896 miliseconds
// This is pretty much as fast as we can get; no
// heap allocations, no boxing, fast comparisons.
MyStructAsKeyWithCompare();
// DURATION: 1,442 miliseconds
}
private static void Reset()
{
GC.Collect();
Thread.Sleep(500);
}
private static void ClassTupleAsKey()
{
var dictionary = new SortedDictionary<ClassTuple<int, string>, int>();
var sw = Stopwatch.StartNew();
for (var i = 0; i < 1000000; i++)
{
var key = new ClassTuple<int, string>(i % 1000, i.ToString());
dictionary.Add(key, i);
}
sw.Stop();
Console.WriteLine("ClassTupleAsKey: " + sw.ElapsedMilliseconds);
}
private static void StructTupleAsKey()
{
var dictionary = new SortedDictionary<StructTuple<int, string>, int>();
var sw = Stopwatch.StartNew();
for (var i = 0; i < 1000000; i++)
{
var key = new StructTuple<int, string>(i % 1000, i.ToString());
dictionary.Add(key, i);
}
sw.Stop();
Console.WriteLine("StructTupleAsKey: " + sw.ElapsedMilliseconds);
}
private static void MyStructAsKey()
{
var dictionary = new SortedDictionary<MyStructA, int>();
var sw = Stopwatch.StartNew();
for (var i = 0; i < 1000000; i++)
{
var key = new MyStructA(i % 1000, i.ToString());
dictionary.Add(key, i);
}
sw.Stop();
Console.WriteLine("StructAsKey: " + sw.ElapsedMilliseconds);
}
private static void MyStructAsKeyWithCompare()
{
var dictionary = new SortedDictionary<MyStructB, int>(MyStructBComparer.Instance);
var sw = Stopwatch.StartNew();
for (var i = 0; i < 1000000; i++)
{
var key = new MyStructB(i % 1000, i.ToString());
dictionary.Add(key, i);
}
sw.Stop();
Console.WriteLine("StructAsKeyWithCompare: " + sw.ElapsedMilliseconds);
}
public class ClassTuple<T1, T2> : IStructuralEquatable, IStructuralComparable, IComparable
{
private readonly T1 _item1;
private readonly T2 _item2;
public T1 Item1 => _item1;
public T2 Item2 => _item2;
public ClassTuple(T1 item1, T2 item2)
{
_item1 = item1;
_item2 = item2;
}
public override bool Equals(object obj)
{
return ((IStructuralEquatable) this).Equals(obj, EqualityComparer<object>.Default);
}
bool IStructuralEquatable.Equals(object other, IEqualityComparer comparer)
{
var objTuple = other as ClassTuple<T1, T2>;
if (objTuple == null) return false;
return comparer.Equals(_item1, objTuple._item1) && comparer.Equals(_item2, objTuple._item2);
}
int IComparable.CompareTo(object obj)
{
return ((IStructuralComparable) this).CompareTo(obj, Comparer<object>.Default);
}
int IStructuralComparable.CompareTo(object other, IComparer comparer)
{
if (other == null) return 1;
var objTuple = other as ClassTuple<T1, T2>;
if (objTuple == null)
throw new ArgumentException("ArgumentException_TupleIncorrectType", nameof(other));
var c = comparer.Compare(_item1, objTuple._item1);
return c != 0 ? c : comparer.Compare(_item2, objTuple._item2);
}
public override int GetHashCode()
{
return ((IStructuralEquatable) this).GetHashCode(EqualityComparer<object>.Default);
}
int IStructuralEquatable.GetHashCode(IEqualityComparer comparer)
{
return CombineHashCodes(comparer.GetHashCode(_item1), comparer.GetHashCode(_item2));
}
private static int CombineHashCodes(int h1, int h2)
{
return ((h1 << 5) + h1) ^ h2;
}
}
public struct StructTuple<T1, T2> : IStructuralEquatable, IStructuralComparable, IComparable
{
private readonly T1 _item1;
private readonly T2 _item2;
public T1 Item1 => _item1;
public T2 Item2 => _item2;
public StructTuple(T1 item1, T2 item2)
{
_item1 = item1;
_item2 = item2;
}
public override bool Equals(object obj)
{
return ((IStructuralEquatable) this).Equals(obj, EqualityComparer<object>.Default);
}
bool IStructuralEquatable.Equals(object other, IEqualityComparer comparer)
{
if (!(other is StructTuple<T1, T2>)) return false;
var objTuple = (StructTuple<T1, T2>) other;
return comparer.Equals(_item1, objTuple._item1) && comparer.Equals(_item2, objTuple._item2);
}
int IComparable.CompareTo(object obj)
{
return ((IStructuralComparable) this).CompareTo(obj, Comparer<object>.Default);
}
int IStructuralComparable.CompareTo(object other, IComparer comparer)
{
var objTuple = (StructTuple<T1, T2>) other;
var c = comparer.Compare(_item1, objTuple._item1);
return c != 0 ? c : comparer.Compare(_item2, objTuple._item2);
}
public override int GetHashCode()
{
return ((IStructuralEquatable) this).GetHashCode(EqualityComparer<object>.Default);
}
int IStructuralEquatable.GetHashCode(IEqualityComparer comparer)
{
return CombineHashCodes(comparer.GetHashCode(_item1), comparer.GetHashCode(_item2));
}
private static int CombineHashCodes(int h1, int h2)
{
return ((h1 << 5) + h1) ^ h2;
}
}
private struct MyStructA : IComparable
{
public MyStructA(int i, string s)
{
I = i;
S = s;
}
public readonly int I;
public readonly string S;
public int CompareTo(object obj)
{
var y = (MyStructA) obj;
var c = I.CompareTo(y.I);
return c != 0 ? c : StringComparer.Ordinal.Compare(S, y.S);
}
}
private struct MyStructB
{
public MyStructB(int i, string s)
{
I = i;
S = s;
}
public readonly int I;
public readonly string S;
}
private class MyStructBComparer : IComparer<MyStructB>
{
public static readonly MyStructBComparer Instance = new MyStructBComparer();
public int Compare(MyStructB x, MyStructB y)
{
var c = x.I.CompareTo(y.I);
return c != 0 ? c : StringComparer.Ordinal.Compare(x.S, y.S);
}
}
}
Enjoy,
Tom
What happens if you implement IComparable (generic) instead of IComparable (non-generic)?
ReplyDeleteImplementing the generic IComparer makes all the difference! When it the IComparer is not generic it will have to box the struct for every comparison, leading to the performance problems described in the blog post.
DeleteThis comment has been removed by the author.
DeleteI created another test case using a struct that implemented the generic IComparable interface, i.e. (using [] braces instead of <> due to HTML stripping):
Deleteprivate struct ComparableStruct: IComparable[ComparableStruct]
{
public int CompareTo(ComparableStruct other)
{
int c = I.CompareTo(other.I);
return c != 0 ? c : StringComparer.Ordinal.Compare(S, other.S);
}
}
The results on my PC (i7 3770K @ 3.5GHz) showed that there was no boxing and it was only slightly slower than the struct with an IComparer (~1000 ms vs ~900 ms). It's also simpler to implement and easier to use since you don't have to remember to use the non-default Comparer every time you create a container...
Yes, you are absolutely correct; the IComparable will solve the problem as well! I apologize, when I read the question I thought that Mackenzie was referring to the non generic version of IComparer. Bottom line: prevent the cast to object!
DeleteIt should be generated by the compiler
ReplyDeleteThe default Comparer implementation will often provide the desired functionality, however it will come at the performance cost of boxing the values for comparison.
Deletehttps://myhrcvs.home.blog/2019/12/15/strength-medication-storage-and-disposal/
ReplyDeletehttps://mycvvshr.blogspot.com/2019/12/myhr-cvs-portal-register-login-and.html
https://sites.google.com/view/cvssmyhr/home
http://cvs-hr.jouwweb.nl/
https://cvslogin.tumblr.com/post/189680989267/cvs-health-site
customer plan individuals to fill their upkeep remedies through mail request conveyance or at a CVS Pharmacy retail location at a similar cost
I am impressed. I don't think Ive met anyone who knows as much about this subject as you do. You are truly well informed and very intelligent. You wrote something that people could understand and made the subject intriguing for everyone. Really, great blog you have got here. starslot789
ReplyDeletemuay thai equipment Offers the best quality Muay Thai equipment with Maximum satisfaction.
ReplyDeleteMy friend mentioned to me your blog, so I thought I’d read it for myself. Very interesting insights, will be back for more! Mike Tyson vs Roy Jones live stream
ReplyDeleteThis comment has been removed by the author.
ReplyDeleteThis comment has been removed by the author.
ReplyDeleteYes i am totally agreed with this article and i just want say that this article is very nice and very informative article.I will make sure to be reading your blog more. You made a good point but I can't help but wonder for 토토사이트, what about the other side? !!!!!!Thanks
ReplyDeleteI am contemplating this topic. I think you can solve my problems. My site is at "카지노사이트". I hope you can help me.
ReplyDeleteYour work is great and I value you and jumping for some more educational posts. Much obliged to you for sharing incredible data to us. 토토사이트
ReplyDeleteThank you for very good information. May I ask for more information?? This is too helpful information for me. I think you are probably interested in 먹튀검증업체 as well. The above information is on my blog, so please feel free to visit
ReplyDeleteThat’s a wonderful piece of writing. Not as good as you, but I’m wearing it like this. 토토사이트
ReplyDeletehow to write an essay for college admission 파워볼사이트
ReplyDeleteI like the theme. A debt of gratitude is in order for such an astounding your web. 안전공원
ReplyDeleteThese look great. We will be wrapping our up in about 3 weeks. 해외스포츠중계
ReplyDeleteAwesome! Exactly what I need! Can't wait to purchase the Kinder one 바카라사이트
ReplyDelete당신의 글은 정말 사랑스러워 보여요, 여기 당신이 좋아할 만한 사이트 링크가 있어요.
ReplyDelete사설토토사이트
If you are looking for more information about flat rate locksmith Las Vegas check that right away written updates
ReplyDeleteGreat post, very informative. I wonder why other experts in this field do not realize this.
ReplyDeleteFind Call Girls Escorts Service in Jaipur by Meet VIP Girls in Jaipur
JAIPUR ESCORT AND HIGH PROFILE CALL GIRL SERVICES | MALVIYA NAGAR FEMALE ESCORTS
Hire Sexy Local Area Escorts in Malviya Nagar
Independent Jaipur Escorts Are Very Awesome in Bed | BOOK THE BEST
Independent Call girls Service NCR, Jaipur, Delhi Noida, Goa
https://sites.google.com/view/cheap-escorts-in-jaipur/jaipur-escort-service
Jaipur Escort and High Profile Call Girl Services | Malviya Nagar Escorts | Vidhyadhar Nagar
How to book call girls
I read a good article. In the future, 먹튀 I will visit often and see a lot of content. I will visit often in the future^^
ReplyDeleteI do trust all of the ideas you have introduced on your post. They’re very convincing and can definitely work. Nonetheless, the posts are too brief for newbies. Could you please extend them a bit from subsequent time? Thank you for the post.
ReplyDelete카지노사이트
Hi, Neat post. There's a problem together with your site in web explorer, would check this?
ReplyDeleteIE still is the market chief and a good part of other people will miss your excellent writing
due to this problem.
오피
Hiring a pro house maintaining company commonly costs so much money it is considered being a additional expense month after month. Your home cleaning moves available your housemaid As i. e. you will want to depend on her behalf for anything relating to cleaning. Though, you may result in feeling like while driving know all sorts of things related to your residence. You may are aware that your privacy is that it is impeded once cleaning maids remain, and oftentimes, you need to deliberately continue a determine your performs and words collectors maid is just about. You may possibly face various safety inquiries when any specific outsider enters your home. Though, maintaining companies be certain their service personnel for credibility, cases in robbery always occur within clients' websites. cleaning company in dubai
ReplyDeleteThank You So Much For providing the important information. We are offering a luxurious and Delicious erotic Nuru massage and escort service. Call Girls in India.
ReplyDeletevery nice site
ReplyDeletePlay Bazaar
I finally found what I was looking for! I'm so happy. 안전한놀이터 Your article is what I've been looking for for a long time. I'm happy to find you like this. Could you visit my website if you have time? I'm sure you'll find a post of interest that you'll find interesting.
ReplyDeleteHello, I'm happy to see some great articles on your site. Would you like to come to my site later? My site also has posts, comments and communities similar to yours. Please visit and take a look 토토사이트
ReplyDeleteYour ideas inspired me very much. 메이저토토사이트모음 It's amazing. I want to learn your writing skills. In fact, I also have a website. If you are okay, please visit once and leave your opinion. Thank you.
ReplyDeleteHello, I read the post well. 메이저토토 It's a really interesting topic and it has helped me a lot. In fact, I also run a website with similar content to your posting. Please visit once
ReplyDeleteA basic yet effective way to judge any take my math class for me is to go through the review section. It says a lot about service providers. Focus on the negative reviews especially (even if they are low in numbers). This way you will be subconsciously ready for negative outcomes as well.
ReplyDeleteSuppose a service provider has more positive reviews in comparison to the negative ones, you can go ahead, but keep the negative ones in mind too. Tell them that you are not expecting a set of things you read in the reviews. And let them work. This way you can easily get assignment help online without being cheated on.
Nice.You made some good points there. I did a Google search about the topic and found most people will believe your blog 카지노사이트
ReplyDeleteYour article reflects the issues people care about. The article provides timely information that reflects multi-dimensional views from multiple perspectives. 토토사이트
ReplyDeleteI like what you guys are usually up too. This kind of clever work and coverage! Keep up the very good works guys I’ve incorporated you guys to blogroll.
ReplyDelete먹튀검증
Nice..The clearness on your post is simply spectacular and that i could assume you are a professional on this subject 룰렛
ReplyDeleteYour information was very useful. I m very pleased to read this article.카지노사이트
ReplyDeleteDid you ever listen, "take my online class for me?" Are you looking for an easy, inexpensive professional service to complete your class online, homework, exams, and other similar classes? We recognize that you have invested an enormous amount for your online classes and that in your online class and you do not want a bad grade. If you are an online student, get support from us online and get your load off. They have tutors who have attended many courses with online pupils, including their math homework, tasks, quizzes, experiments, and online exams who are looking to take my online class.
ReplyDeleteThis is a very useful and important information, it was so useful to me and other readers, thank you for always feeding the readers with interesting and useful information, your blog is indeed doing very well, weldone, meanwhile you can checkout has fce post utme form out
ReplyDeleteOne of the main reasons why students do not want to write organic chemistry assignment on their own is the lack of adequate knowledge of that particular subject, language barrier and difficulty in constructing the given content into desired assignment. Keeping the financial limitations of student in mind there are my homework done online service available to do needful for students.
ReplyDeleteHi, I do think this is a great site. I stumbledupon it ;) I may revisit yet again since i have book marked it.
ReplyDeleteMoney and freedom is the best way to change, may you
be rich and continue to help other people.
https://www.betmantoto.pro
I've been troubled for several days with this topic. 슬롯사이트, But by chance looking at your post solved my problem! I will leave my blog, so when would you like to visit it?
ReplyDeleteThanks to you for your effort...RPD Water Care Solutions is a pioneer in offering clients with high-quality goods and excellent service at low pricing. Sewage Treatment Plant
ReplyDeleteYou are busy in your life activities and thinking about Pay Someone To Do My Online Class For Me and cannot handle the online class or exam which is scheduled up? You want someone to get an A for you in your complete online course for you? No worry! We have come to the place where our experts always do the job with perfection. Your satisfaction is our ultimate goal.
ReplyDeleteit’s awesome and I found this one informative. Tanishq Adv
ReplyDeleteMy name is Alexander Barnes. GotoAssignmentHelp best CPM homework helpKeyword
ReplyDeleteservice providers in Australia countries. best services provide homework help, case study help. team paper helps assay help, any other countries Australia service provide. Assignment writing has always been a cause of concern for the students across the world. And it is same with the students of Australia. During the course of the academic career, the students of Australia face tough time to prepare assignments. GotoAssignmentHelp 24/7 hr service provide. if you are looking for an online assignment helper service. The GotoAssignmentHelp team will take care of your assignments one you book your order at our website. For more information, hire our Online Do my homework
now.
post... thanks For Sharing !!Great information for new guy like
ReplyDeleteHanuman Chalisa Lyrics
Bhagwat Geeta in Hindi PDF
sarvanam
Thank you for sharing your knowledge. I really admire your efforts and I will be waiting for your next post thank you once again. https://disqus.com/by/tanishqadv/about/
ReplyDeleteI am thankful you to visit here...RPD Water Care Solutions is a pioneer in offering clients with high-quality goods and excellent service at low pricing. Sewage treatment Plant manufacturer in Jaipur
ReplyDeletehttps://sites.google.com/view/sewage-water-treatment-plant/swimming-pool-plant ///// RPD Water treatment STP Waste Water Solution and Manufacturer Company in Jaipur. Get preeminent Sewage water treatment plant from Us to improve Your industrial processes.
ReplyDelete
ReplyDeleteNice Post Your content is very inspiring and appriciating I really like it please visit my site for
gali satta
satta result
satta king
Satta Bajar
सट्टा किंग
delhi satta king
Disawar satta king
satta matka
I wrote about a similar issue, I give you the link to my site.bank guarantee
ReplyDeleteHi! this is nice article you shared with great information. Thanks for giving such a wonderful informative information. I hope you will publish again such type of post. Also, please check out about MetaMask Log In | MetaMask | Metamask Chrome Extension
ReplyDeleteThanks for the informative article. I hope you will provide more articles like this.
ReplyDeleteMetamask wallet | Coinbase Login | Coinbase Login
Your posts are always informative for us.
ReplyDeletedisneyplus.com login/begin | showtime anytime/activate | aka.ms/remoteconnect
ReplyDeleteGreat Article. I really like your blog. Keep sharing more.
Coinbase Pro Login | Coinbase Login
From the previous definition of the database management system, we noted the essence of the use of a database management software that enables the functioning of various components of the database.
ReplyDeletetheology assignment help
assignment help
computer science assignment help
capstone project help online
article writer
content writer for hire
content writing services online
freelance writer for hire
Ghost writer for hire online
write my assignments for me
I read this post your post so nice and very informative post thanks for sharing this post!
ReplyDeletespookyswap | Quickswap
I'm looking for a lot of data on this topic. The article I've been looking for in the meantime is the perfect article. Please visit my site for more complete articles with him! แทงมวย
ReplyDeleteI have read your article, it is very informative and helpful for me.I admire the valuable information you offer in your articles. Thanks for posting it.. r1fight
ReplyDeleteI wrote about a similar issue, I give you the link to my site. single girl attitude quotes in hindi
ReplyDeleteHello my fellow friends and bloggers. It felt really great going through all your article ! Really felt the effort and hardwork you have put . if you are intrested in Satta King you can check out my website. It is quick accurate and easy to use south delhi satta king
ReplyDeleteExcellent read, Positive site, where did u come up with the information on this posting?I have read a few of the articles on your website now, and I really like your style. Thanks a million and please keep up the effective work. hanumanchalisalyrics
ReplyDeleteMarry James is a writing Expert with 15+ years of experience. Marry is also associated with MyAssignmenthelp.com, where she regularly helps students write their essays/assignments. In addition, Marry also likes to read and has read more than 100 books now. Students prefer his services as a tutor and as a counsellor and guides students on academic issues. She offers HTML assignment help to students who need help with their papers. Marry is a programmer too, so she has detailed knowledge about the subject.
ReplyDeleteOther Service
write my essay for me
Top Essay Writing Service
English Essay Writing Help
Physics homework help
college essays help
Essay Editing Service
Fraction Calculator
Persuasive Essay Help
write my research paper
plagiarism checker
Book Review Writing
Title Generator
plagiarism checker
Nursing Resume
free tools
Harvard referencing generator
Summary Generator
fnsacc311 assessment answers
hltaid004 assessment answers
I think this is an informative post and it is very useful and knowledgeable. therefore, I would like to thank you for the efforts you have made in writing this article.
ReplyDeleteSatta King Faridabad
I am understand everything about this post i hope you are write awesome articale like that.
ReplyDeleteMass Backlinks
I am really enjoying reading your well written articles. I think you spend numerous effort and time updating your blog. Bollywood Stars Biography
ReplyDeleteNice post and keep sharing Tarceva Precio Republica Dominican /
ReplyDeleteThanks for sharing your experience and valuable advice or enjoyed to reading this post. Christ University Admission Date 2022
ReplyDeletePancake Swap allows users to earn an additional yield by staking supported liquidity provider (LP) tokens in one of its numerous yield farms. By participating in a yield farm, users earn a CAKE yield on their LP tokens. This is on top of the yields generated through transaction fees.
ReplyDeleteAtomic Wallet allows users to earn an additional yield by staking supported liquidity provider (LP) tokens in one of its numerous yield farms. By participating in a yield farm, users earn a CAKE yield on their LP tokens. This is on top of the yields generated through transaction fees.
ReplyDeleteHaving an understanding of statistics enables you to utilize the appropriate procedures to collect the data, to carry out the appropriate analysis, and to present the results in an effective manner. The application of statistics is an essential step in the process of making scientific discoveries, as well as judgments and forecasts that are founded on data. The level of expertise of the tutors who assist students with their assignments is of the utmost significance to every single student who uses these services. When it comes to matters pertaining to payment, no one will ever be willing to make a concession on anything. The quality of the “my homework done online service”always meets or exceeds expectations. They are able to better control and maintain the quality standard as a direct result of the ratings.
ReplyDeleteThanks for sharing
ReplyDeleteList of tecno phones and prices at slot
post... thanks For Sharing !!Great information for new guy like Bhagwat Geeta in Hindi PDF
ReplyDeletepost... thanks For Sharing !!Great information for new guy like Bhagwat Geeta in Hindi PDF
ReplyDeleteHelpful site, helpful content.
ReplyDeletehow-to write an application letter for zambia army recruitment
Nice post
ReplyDeletemshwari loan kenya
brilliant post
ReplyDeleteHow to Check My Cash App Card Balance Without App
This is a Very well Written Post. Your Point of View Caught My Eye and It Was Very Interesting. Thanks for This Great Job
ReplyDeleteJaunpur Female Companion
Jhansi Female Companion
Kanpur Female Companion
post... thanks For Sharing !!Great information for new guy like Gujarati sexy video
ReplyDeleteThis is the ultimate desert safari experience in Dubai. On an Evening Desert Safari, you will discover the magic and charm of the Dubai desert, experiencing a world of natural wonder, indigenous wildlife, and Bedouin culture.
ReplyDelete