Şubesiz K-anlamına gelir (ya da diğer iyileştirme)
Not: yaklaşım ve çözümler bu tür değil, çözümün kendisi için nasıl bir rehber daha memnun olurum.
Benim sistemi bir dizi belirli bağlamlarda bir profil noktasal gösteren oyuncu-çok kritik bir işlevi var. K-demek bir yineleme (zaten çok iş parçacıklı işlem için bir paralel kullanarak alt aralıklar her bir işçi olarak puan thread) ortasında.
ClusterPoint& pt = points[j];
pt.min_index = -1;
pt.min_dist = numeric_limits<float>::max();
for (int i=0; i < num_centroids; i)
{
const ClusterCentroid& cent = centroids[i];
const float dist = ...;
if (dist < pt.min_dist) // <-- #1 hotspot
{
pt.min_dist = dist;
pt.min_index = i;
}
}
Zaman kodu bu bölümde, işlem için gerekli tüm birikimleri çoğu zaman beraberinde bir sürü işe yaramaz oldum çok önemli sayar. Dışarıda denir döngü koyarak değer, örneğin, ve belirli bir ağırlık merkezi için paralel noktaları arasında yinelemek olabilir. Küme sayısı hava masasında; çarpışma sayısı binlerce yayılmış olsa milyonlarca burada yayılan puan. Algoritmanın yineleme bir avuç (genellikle 10 altında) için uygulanır. İstikrar, sadece biraz mantıklı yaklaşım./mükemmel yakınsama aramazlar
Herhangi bir fikir takdir, ama gerçekten keşfetmek için can atıyorum ne varsa bu kodu SIMD bir sürüm için izin vermesi gibi şubesiz yapılabilir. Görmedim gerçekten gelişmiş bir tür zihinsel yeteneği kolayca kavramak ne kadar şubesiz çözüm: beynime başarısız pek çok şey gibi o zaman ben ilk maruz özyineleme ilk günlerde, bu yüzden bir rehber yazmayı şubesiz Kodu ve nasıl geliştirmek için uygun bir zihniyet için de yararlı olabilir.
Kısacası, ve ipuçları ve öneriler (mutlaka çözüm) hakkında herhangi bir kılavuz mikro-optimize bu kodu arıyorum. Büyük olasılıkla algoritmik iyileştirmeler için bir oda var, ama benim her zaman kör nokta-mikro optimizasyon çözümleri (ve onları daha etkili bir şekilde denize gidiyor olmadan nasıl uygulanacağını öğrenmek için merak ediyorum) olmuştur. Zaten mantık için tıknaz paralel ile sıkıca çok iş parçacıklı, çok akıllı bir algoritma olmadan salt denemek için hızlı şeylerden biri olarak mikro-optimizasyon köşeye sıkıştım. Bellek düzeni değiştirmek için tamamen özgürüz.
Algoritmik Önerilerine Yanıt olarak
Algoritmik düzeyde mikro-optimize açıkça geliştirilebilir(knm) O bir algoritma bulmaya çalışırken, yanlış bakma, ben de buna yürekten katılıyorum. Bu biraz akademik ve pratik bir aleme bu özel soru iter. Ancak, eğer ben olabilir izin bir anekdot, ben gel bir orijinal arka plan yüksek seviyeli programlama büyük önem geniş, büyük ölçekli bakış açısı, güvenlik, ve çok az üzerinde düşük seviyeli uygulama ayrıntıları. Ben zaten son zamanlarda açık projeler için bir çok farklı tür modern aromalı ve öğreniyorum her türlü yeni hileler arkadaşlarımın önbellek verimlilik, GPGPU, şubesiz teknikleri, SIMD, özel amaçlı mem yöneticileri gerçekten daha iyi performans malloc (ama belirli senaryolar), vb.
Nerede çalışıyorum yetiş ile son performans eğilimleri ve şaşırtıcı buldum bu eski veri yapıları sık sık tercih sırasında 90'lı yılların hangi arada bağlantılı/ağaç türü yapılardır aslında oldukça geride olmaktan çok daha saf, hayvani, mikro-optimize edilmiş, parallelized kodu uygulayarak ayarlı talimatları üzerine ardışık bellek blokları. Algoritmaları makine daha şimdi uydurma ve olanaklarını bu şekilde (özellikle GPGPU ile) daralması gibi hissediyorum bu yana, aynı anda biraz hayal kırıklığı.
Çok komik bir şey mikro-optimize edilmiş, hızlı bir dizi işlem kodu bu tür daha önce kullanıyordum karmaşık algoritmalar ve veri yapıları daha korumak için daha kolay buluyorum. Bir başlangıç için, genelleme yapmak daha kolaydır. Ayrıca, arkadaşlarımın genelde bir bölgede belirli bir yavaşlama hakkında müşteri şikayeti almak, sadece paralel ve muhtemelen bir SIMD tokat ve iyi bir hız ile yapılan arama yapabilirsiniz. Algoritmik geliştirmeler olabilir sık sık teklif oldukça fazla, ama çok hızlı ve olmayan intrusiveness olan bu mikro-optimizasyon uygulanabilir beni isteyen daha fazla bilgi edinmek için bu alanı okuma kağıtları algoritmaları daha iyi bir zaman olarak gerektiren daha kapsamlı değişiklikler). Bana da öyle atlama mikro-optimizasyon çoğunluğa biraz daha son zamanlarda, belki de biraz fazla bu özel durum, ama merakımı hakkında daha fazla genişleyen aralığı Olası çözümler için herhangi bir senaryo.
Demontaj
Not: ben gerçekten, gerçekten kötü kurulda bu yüzden ben genellikle ayarlanmış şeyler daha çok bir deneme yanılma biraz şekilde geliyor biraz eğitimli tahminler hakkında neden bir sıcak nokta gösterilen vtune olabilir darboğaz daha deneyip bazı şeyleri görmek için bu kez geliştirmek varsayarak tahmin biraz ipucu gerçeği ise bu kez geliştirmek veya tamamen cevapsız işareti yok.
000007FEEE3FB8A1 jl thread_partition 70h (7FEEE3FB780h)
{
ClusterPoint& pt = points[j];
pt.min_index = -1;
pt.min_dist = numeric_limits<float>::max();
for (int i = 0; i < num_centroids; i)
000007FEEE3FB8A7 cmp ecx,r10d
000007FEEE3FB8AA jge thread_partition 1F4h (7FEEE3FB904h)
000007FEEE3FB8AC lea rax,[rbx rbx*2]
000007FEEE3FB8B0 add rax,rax
000007FEEE3FB8B3 lea r8,[rbp rax*8 8]
{
const ClusterCentroid& cent = centroids[i];
const float x = pt.pos[0] - cent.pos[0];
const float y = pt.pos[1] - cent.pos[1];
000007FEEE3FB8B8 movss xmm0,dword ptr [rdx]
const float z = pt.pos[2] - cent.pos[2];
000007FEEE3FB8BC movss xmm2,dword ptr [rdx 4]
000007FEEE3FB8C1 movss xmm1,dword ptr [rdx-4]
000007FEEE3FB8C6 subss xmm2,dword ptr [r8]
000007FEEE3FB8CB subss xmm0,dword ptr [r8-4]
000007FEEE3FB8D1 subss xmm1,dword ptr [r8-8]
const float dist = x*x y*y z*z;
000007FEEE3FB8D7 mulss xmm2,xmm2
000007FEEE3FB8DB mulss xmm0,xmm0
000007FEEE3FB8DF mulss xmm1,xmm1
000007FEEE3FB8E3 addss xmm2,xmm0
000007FEEE3FB8E7 addss xmm2,xmm1
if (dist < pt.min_dist)
// VTUNE HOTSPOT
000007FEEE3FB8EB comiss xmm2,dword ptr [rdx-8]
000007FEEE3FB8EF jae thread_partition 1E9h (7FEEE3FB8F9h)
{
pt.min_dist = dist;
000007FEEE3FB8F1 movss dword ptr [rdx-8],xmm2
pt.min_index = i;
000007FEEE3FB8F6 mov dword ptr [rdx-10h],ecx
000007FEEE3FB8F9 inc ecx
000007FEEE3FB8FB add r8,30h
000007FEEE3FB8FF cmp ecx,r10d
000007FEEE3FB902 jl thread_partition 1A8h (7FEEE3FB8B8h)
for (int j = *irange.first; j < *irange.last; j)
000007FEEE3FB904 inc edi
000007FEEE3FB906 add rdx,20h
000007FEEE3FB90A cmp edi,dword ptr [rsi 4]
000007FEEE3FB90D jl thread_partition 31h (7FEEE3FB741h)
000007FEEE3FB913 mov rbx,qword ptr [irange]
}
}
}
}
Geldik zorla içine hedefleme SSE 2 -- biraz arkasında bizim kez, ama kullanıcı tabanı aslında takıldı bir kere ne zaman farz ettiğimiz bile SSE 4 oldu Tamam bir dakika gereksinimi (kullanıcı vardı bazı prototip Intel makinesi).
Tek Başına Test ile güncelleştirmesi: ~5.6 saniye
Tüm yardımı sunuluyor, sana minnettarım! Kod ve kod karmaşık tetiklenmesi için koşullar (sistem olayları birden çok iş parçacığı üzerinde tetiklenen) oldukça geniş olduğundan, biraz deneysel değişiklikler yapmak ve onları profil için her zaman kullanışsız. Diğerleri de çalıştırmak ve böylece denemek nezaketle sunulan tüm bu çözümleri deneme olabilir bağımsız bir uygulama olarak tarafında yüzeysel bir test hazırladım.
#define _SECURE_SCL 0
#include <iostream>
#include <fstream>
#include <vector>
#include <limits>
#include <ctime>
#if defined(_MSC_VER)
#define ALIGN16 __declspec(align(16))
#else
#include <malloc.h>
#define ALIGN16 __attribute__((aligned(16)))
#endif
using namespace std;
// Aligned memory allocation (for SIMD).
static void* malloc16(size_t amount)
{
#ifdef _MSC_VER
return _aligned_malloc(amount, 16);
#else
void* mem = 0;
posix_memalign(&mem, 16, amount);
return mem;
#endif
}
template <class T>
static T* malloc16_t(size_t num_elements)
{
return static_cast<T*>(malloc16(num_elements * sizeof(T)));
}
// Aligned free.
static void free16(void* mem)
{
#ifdef _MSC_VER
return _aligned_free(mem);
#else
free(mem);
#endif
}
// Test parameters.
enum {num_centroids = 512};
enum {num_points = num_centroids * 2000};
enum {num_iterations = 5};
static const float range = 10.0f;
class Points
{
public:
Points(): data(malloc16_t<Point>(num_points))
{
for (int p=0; p < num_points; p)
{
const float xyz[3] =
{
range * static_cast<float>(rand()) / RAND_MAX,
range * static_cast<float>(rand()) / RAND_MAX,
range * static_cast<float>(rand()) / RAND_MAX
};
init(p, xyz);
}
}
~Points()
{
free16(data);
}
void init(int n, const float* xyz)
{
data[n].centroid = -1;
data[n].xyz[0] = xyz[0];
data[n].xyz[1] = xyz[1];
data[n].xyz[2] = xyz[2];
}
void associate(int n, int new_centroid)
{
data[n].centroid = new_centroid;
}
int centroid(int n) const
{
return data[n].centroid;
}
float* operator[](int n)
{
return data[n].xyz;
}
private:
Points(const Points&);
Points& operator=(const Points&);
struct Point
{
int centroid;
float xyz[3];
};
Point* data;
};
class Centroids
{
public:
Centroids(Points& points): data(malloc16_t<Centroid>(num_centroids))
{
// Naive initial selection algorithm, but outside the
// current area of interest.
for (int c=0; c < num_centroids; c)
init(c, points[c]);
}
~Centroids()
{
free16(data);
}
void init(int n, const float* xyz)
{
data[n].count = 0;
data[n].xyz[0] = xyz[0];
data[n].xyz[1] = xyz[1];
data[n].xyz[2] = xyz[2];
}
void reset(int n)
{
data[n].count = 0;
data[n].xyz[0] = 0.0f;
data[n].xyz[1] = 0.0f;
data[n].xyz[2] = 0.0f;
}
void sum(int n, const float* pt_xyz)
{
data[n].xyz[0] = pt_xyz[0];
data[n].xyz[1] = pt_xyz[1];
data[n].xyz[2] = pt_xyz[2];
data[n].count;
}
void average(int n)
{
if (data[n].count > 0)
{
const float inv_count = 1.0f / data[n].count;
data[n].xyz[0] *= inv_count;
data[n].xyz[1] *= inv_count;
data[n].xyz[2] *= inv_count;
}
}
float* operator[](int n)
{
return data[n].xyz;
}
int find_nearest(const float* pt_xyz) const
{
float min_dist_squared = numeric_limits<float>::max();
int min_centroid = -1;
for (int c=0; c < num_centroids; c)
{
const float* cen_xyz = data[c].xyz;
const float x = pt_xyz[0] - cen_xyz[0];
const float y = pt_xyz[1] - cen_xyz[1];
const float z = pt_xyz[2] - cen_xyz[2];
const float dist_squared = x*x y*y * z*z;
if (min_dist_squared > dist_squared)
{
min_dist_squared = dist_squared;
min_centroid = c;
}
}
return min_centroid;
}
private:
Centroids(const Centroids&);
Centroids& operator=(const Centroids&);
struct Centroid
{
int count;
float xyz[3];
};
Centroid* data;
};
// A high-precision real timer would be nice, but we lack C 11 and
// the coarseness of the testing here should allow this to suffice.
static double sys_time()
{
return static_cast<double>(clock()) / CLOCKS_PER_SEC;
}
static void k_means(Points& points, Centroids& centroids)
{
// Find the closest centroid for each point.
for (int p=0; p < num_points; p)
{
const float* pt_xyz = points[p];
points.associate(p, centroids.find_nearest(pt_xyz));
}
// Reset the data of each centroid.
for (int c=0; c < num_centroids; c)
centroids.reset(c);
// Compute new position sum of each centroid.
for (int p=0; p < num_points; p)
centroids.sum(points.centroid(p), points[p]);
// Compute average position of each centroid.
for (int c=0; c < num_centroids; c)
centroids.average(c);
}
int main()
{
Points points;
Centroids centroids(points);
cout << "Starting simulation..." << endl;
double start_time = sys_time();
for (int i=0; i < num_iterations; i)
k_means(points, centroids);
cout << "Time passed: " << (sys_time() - start_time) << " secs" << endl;
cout << "# Points: " << num_points << endl;
cout << "# Centroids: " << num_centroids << endl;
// Write the centroids to a file to give us some crude verification
// of consistency as we make changes.
ofstream out("centroids.txt");
for (int c=0; c < num_centroids; c)
out << "Centroid " << c << ": " << centroids[c][0] << "," << centroids[c][1] << "," << centroids[c][2] << endl;
}
Yüzeysel test tehlikelerin farkındayım, ama zaten gerçek dünya önceki oturumlarında bir sıcak nokta olarak kabul olduğu için mazur umarım. Ayrıca sadece genel teknikleri mikro-optimize tür kodu ile ilgili ilgileniyorum.
Bu profil biraz farklı sonuçlar aldım. Bu kez biraz daha eşit döngü içinde dağınık ve neden emin değilim. Belki de verileri daha küçük (üye ihmal ve min_dist
üye çekilir ve yerel bir değişken) yapılan içindir. Puan için hava masasında; çarpışma arasında tam oranı da biraz farklı, ama umarım yeterince yakın gelişmeler burada orijinal kodu çevirmek için. Ayrıca tek dişli bu yüzeysel test ve demontaj görünüyor oldukça farklı bu yüzden olabilir riske optimize bu yüzeysel test olmadan orijinal (bir riske girmeye hazırım almak için şimdi, ben daha fazla ilgi genişleyen bilgim teknikleri optimize olabilir bu gibi durumlarda yerine bir çözüm için bu kesin vaka).
Yohay Timmer Önerisi ile -- ~12.5 saniye güncelleştirin
Oh, anlayış montaj olmadan mikro-optimizasyon çok iyi sıkıntılarla karşı karşıyayım. Bunun yerine:
-if (min_dist_squared > dist_squared)
-{
- min_dist_squared = dist_squared;
- pt.centroid = c;
-}
Bu:
const bool found_closer = min_dist_squared > dist_squared;
pt.centroid = bitselect(found_closer, c, pt.centroid);
min_dist_squared = bitselect(found_closer, dist_squared, min_dist_squared);
sadece bir kez bulmak için .. ~12.5 saniye için ~5.6 saniye gelen tırmandı. Yine de, bu onun hatası değil, ne yapar elinden gelen değeri onun çözümü -- bu benim için başarısız anlamak için neler makinesi düzeyi ve bıçaklar alarak karanlıkta. Bir görünüşte cevapsız, ve görünüşe göre Ben başlangıçta düşünce olarak şube misprediction kurbanı ben değildim. Yine de, onun önerilen çözüm bu gibi durumlarda denemek için harika ve yaygın bir fonksiyonudur, ve ipuçları ve hileler benim araç eklemek için minnettarım. 2. tur için artık.
Harold SIMD Çözüm 2.496 saniye (bkz: uyarı)
Bu çözüm şaşırtıcı olabilir. Küme rep SoA dönüştürdükten sonra, bu bir ile ~2.5 saniye defa alıyorum! Ne yazık ki, bazı tür bir arıza var gibi görünüyor. Daha hafif, daha hassas farklılıklar gösteriyor son çıkış, 0 değerleri ile sonuna (arama bulunan olmadıklarını ima) yönelik bazı hava masasında; çarpışma da dahil olmak üzere çok farklı sonuçlar alıyorum. Deniyorum gitmek üzerinden SIMD mantığı ile hata ne olabilir ... olabilir sadece bir transkripsiyon hata benim, ama işte kod halinde biri olabilir nokta hata.
Eğer hata sonuçları yavaşlatmadan düzeltilmiş olabilir, bu hızlı gelişmeye hiç mikro-optimizasyon saf hayal daha fazla!
// New version of Centroids::find_nearest (from harold's solution):
int find_nearest(const float* pt_xyz) const
{
__m128i min_index = _mm_set_epi32(3, 2, 1, 0);
__m128 xdif = _mm_sub_ps(_mm_set1_ps(pt_xyz[0]), _mm_load_ps(cen_x));
__m128 ydif = _mm_sub_ps(_mm_set1_ps(pt_xyz[1]), _mm_load_ps(cen_y));
__m128 zdif = _mm_sub_ps(_mm_set1_ps(pt_xyz[2]), _mm_load_ps(cen_z));
__m128 min_dist = _mm_add_ps(_mm_add_ps(_mm_mul_ps(xdif, xdif),
_mm_mul_ps(ydif, ydif)),
_mm_mul_ps(zdif, zdif));
__m128i index = min_index;
for (int i=4; i < num_centroids; i = 4)
{
xdif = _mm_sub_ps(_mm_set1_ps(pt_xyz[0]), _mm_load_ps(cen_x i));
ydif = _mm_sub_ps(_mm_set1_ps(pt_xyz[1]), _mm_load_ps(cen_y i));
zdif = _mm_sub_ps(_mm_set1_ps(pt_xyz[2]), _mm_load_ps(cen_z i));
__m128 dist = _mm_add_ps(_mm_add_ps(_mm_mul_ps(xdif, xdif),
_mm_mul_ps(ydif, ydif)),
_mm_mul_ps(zdif, zdif));
__m128i mask = _mm_castps_si128(_mm_cmplt_ps(dist, min_dist));
min_dist = _mm_min_ps(min_dist, dist);
min_index = _mm_or_si128(_mm_and_si128(index, mask),
_mm_andnot_si128(mask, min_index));
index = _mm_add_epi32(index, _mm_set1_epi32(4));
}
ALIGN16 float mdist[4];
ALIGN16 uint32_t mindex[4];
_mm_store_ps(mdist, min_dist);
_mm_store_si128((__m128i*)mindex, min_index);
float closest = mdist[0];
int closest_i = mindex[0];
for (int i=1; i < 4; i )
{
if (mdist[i] < closest)
{
closest = mdist[i];
closest_i = mindex[i];
}
}
return closest_i;
}
Harold SIMD Çözüm (Düzeltildi) - ~2.5 saniye
Düzeltmeleri uygulamak ve test sonuçları sağlam ve doğru orijinal bulunanla benzer gelişmeler işlevi!
Bunu daha iyi anlamak için arıyordum bilgisi (şubesiz SIMD) Kutsal Kase vurur bu yana, daha fazla işlem hızını iki katına çıkarmak için bazı ekstra sahne ile çözüm ödülü vereceğim. Amacım bu noktasal azaltmak için değil sadece, ama Olası çözümler benim kişisel anlayış genişletmek için onlarla başa çıkmak için bu yana ödevimi bunu anlamak için biçilmiş kaftan.
Yine de, tüm katkıları burada çok havalı bitselect
kandırmak için algoritmik öneriler için minnettarım! Tüm cevapları kabul edebilmeyi isterdim. Bir noktada hepsi çalışıyor sonunda olabilirim, ama şimdi ödevimi olmayan aritmetik bu SIMD ops biraz anlayış çıkardım.
int find_nearest_simd(const float* pt_xyz) const
{
__m128i min_index = _mm_set_epi32(3, 2, 1, 0);
__m128 pt_xxxx = _mm_set1_ps(pt_xyz[0]);
__m128 pt_yyyy = _mm_set1_ps(pt_xyz[1]);
__m128 pt_zzzz = _mm_set1_ps(pt_xyz[2]);
__m128 xdif = _mm_sub_ps(pt_xxxx, _mm_load_ps(cen_x));
__m128 ydif = _mm_sub_ps(pt_yyyy, _mm_load_ps(cen_y));
__m128 zdif = _mm_sub_ps(pt_zzzz, _mm_load_ps(cen_z));
__m128 min_dist = _mm_add_ps(_mm_add_ps(_mm_mul_ps(xdif, xdif),
_mm_mul_ps(ydif, ydif)),
_mm_mul_ps(zdif, zdif));
__m128i index = min_index;
for (int i=4; i < num_centroids; i = 4)
{
xdif = _mm_sub_ps(pt_xxxx, _mm_load_ps(cen_x i));
ydif = _mm_sub_ps(pt_yyyy, _mm_load_ps(cen_y i));
zdif = _mm_sub_ps(pt_zzzz, _mm_load_ps(cen_z i));
__m128 dist = _mm_add_ps(_mm_add_ps(_mm_mul_ps(xdif, xdif),
_mm_mul_ps(ydif, ydif)),
_mm_mul_ps(zdif, zdif));
index = _mm_add_epi32(index, _mm_set1_epi32(4));
__m128i mask = _mm_castps_si128(_mm_cmplt_ps(dist, min_dist));
min_dist = _mm_min_ps(min_dist, dist);
min_index = _mm_or_si128(_mm_and_si128(index, mask),
_mm_andnot_si128(mask, min_index));
}
ALIGN16 float mdist[4];
ALIGN16 uint32_t mindex[4];
_mm_store_ps(mdist, min_dist);
_mm_store_si128((__m128i*)mindex, min_index);
float closest = mdist[0];
int closest_i = mindex[0];
for (int i=1; i < 4; i )
{
if (mdist[i] < closest)
{
closest = mdist[i];
closest_i = mindex[i];
}
}
return closest_i;
}
CEVAP
Şubesiz üçlü operatördür, bitselect ( koşul ? denilen bazen kullanabilirsiniz doğru : yanlış).
Sadece 2 üyeleri için, hiçbir şey yapmadan varsaymak kullanın.
Ekstra işlemleri hakkında endişelenme, eğer deyimi dallanma kıyaslanamaz.
uygulama bitselect:
inline static int bitselect(int condition, int truereturnvalue, int falsereturnvalue)
{
return (truereturnvalue & -condition) | (falsereturnvalue & ~(-condition)); //a when TRUE and b when FALSE
}
inline static float bitselect(int condition, float truereturnvalue, float falsereturnvalue)
{
//Reinterpret floats. Would work because it's just a bit select, no matter the actual value
int& at = reinterpret_cast<int&>(truereturnvalue);
int& af = reinterpret_cast<int&>(falsereturnvalue);
int res = (at & -condition) | (af & ~(-condition)); //a when TRUE and b when FALSE
return reinterpret_cast<float&>(res);
}
Ve döngü bu gibi görünmelidir:
for (int i=0; i < num_centroids; i)
{
const ClusterCentroid& cent = centroids[i];
const float dist = ...;
bool isSmaeller = dist < pt.min_dist;
//use same value if not smaller
pt.min_index = bitselect(isSmaeller, i, pt.min_index);
pt.min_dist = bitselect(isSmaeller, dist, pt.min_dist);
}
Soket seçenekleri SO_REUSEADDR ve SO_R...
git Uyarı: uzak BAŞ varolmayan hakem, ...
CSS sınıfı bir veya daha fazla diğer s...
Nasıl benim URL paylaşırken Paylaşan R...
Varlık Çerçevesi - Yeni işlem SqlExcep...