Đây là video hướng dẫn cách tạo DLL trên Delphi nhưng sử dụng trong VBA, Excel. Đây là phương pháp lập trình chuyên nghiệp nhưng không phải là khó. Giúp bạn bảo mật code của phần mềm tốt hơn.
Mình decompile code hàm TransArr ra và phát hiện chổ chậm nó rồi, cũng như hàm GetSumRange. Cách fix cũng tương tự cách fix cho hàm GetSumRange ở trên.
Nội cái dòng:
Result[i, j] := ssArr[j, i];
Delphi sinh ra code ASM như vầy đây:
Chờ mình code cho xem vd
{$DEFINE TEST}
{$IFDEF TEST}
program Test;
{$APPTYPE CONSOLE}
{$ELSE}
library Test;
{$ENDIF}
uses
System.Variants;
function FastSumRange(const arr: Variant): Integer; stdcall;
var
I, J: Integer;
LB1, LB2, UB1, UB2: Integer;
PElem: PVariant;
begin
Result := 0;
LB1 := VarArrayLowBound(arr, 1);
UB1 := VarArrayHighBound(arr, 1);
LB2 := VarArrayLowBound(arr, 2);
UB2 := VarArrayHighBound(arr, 2);
PElem := VarArrayLock(arr);
try
for I := LB1 to UB1 do
for J := LB2 to UB2 do
begin
Result := Result + PVarData(PElem).VInteger;
Inc(PElem);
end;
finally
VarArrayUnlock(arr);
end;
end;
exports
FastSumRange;
{$IFDEF TEST}
var
vArr: Variant;
I, J: Integer;
Sum: Integer;
begin
vArr := VarArrayCreate([0, 3, 0, 5], varVariant);
for I := 0 to 3 do
for J := 0 to 5 do
vArr[I, J] := I + J;
Sum := FastSumRange(vArr);
WriteLn(Sum);
ReadLn;
{$ENDIF}
end.
Đây là đoạn code mình code minh họa cho các bạn cách dùng Pointer, truy xuất trực tiếp tới từng memory của từng phần tử trong 1 array của Variant/xxx bất kỳ.
Dựa vào đây các bạn có thể độ chế lại theo yêu cầu của riêng mình.
Bao fast và vé ri vé ri nhanh nhé các bạn, vì tránh được _VarArrayGet và _VarArrayPut.
Bài đã được tự động gộp:
Mã ASM mà Delphi compiler sinh ra, sau khi qua decompiler ngược lại.
Các bạn thấy trong vòng lặp không còn _VarArrayGet và _VarArrayPut.
Chỉ là thao tác công và dịch con trỏ PLem lên sizeof(Variant)
Các bạn chú ý dòng này, điểm quan trong:
Result := Result + PVarData(PElem).VInteger;
Nếu các bạn viết:
Result := Result + PElem^
Thì Delphi compiler sẽ sinh mã gọi các hàm internal để convert 1 giá trị Variant to kiểu của biến Result. Ở đây là Integer thì sẽ là _VarAsInteger, và linh tinh nữa, sẽ kéo tốc độ xuống.
Vì Variant là kiểu union TVarData nên mình ép kiểu pointer của PElem từ con trỏ PVariant với PVarData, lấy trực tiếp field của nó luôn.
Nên tránh luôn được các hàm internal mà Delphi compiler chèn vô.
{$DEFINE TEST}
{$IFDEF TEST}
program Test;
{$APPTYPE CONSOLE}
{$ELSE}
library Test;
{$ENDIF}
uses
System.Variants;
function FastSumRange(const arr: Variant): Integer; stdcall;
var
I, J: Integer;
LB1, LB2, UB1, UB2: Integer;
PElem: PVariant;
begin
Result := 0;
LB1 := VarArrayLowBound(arr, 1);
UB1 := VarArrayHighBound(arr, 1);
LB2 := VarArrayLowBound(arr, 2);
UB2 := VarArrayHighBound(arr, 2);
PElem := VarArrayLock(arr);
try
for I := LB1 to UB1 do
for J := LB2 to UB2 do
begin
Result := Result + PVarData(PElem).VInteger;
Inc(PElem);
end;
finally
VarArrayUnlock(arr);
end;
end;
exports
FastSumRange;
{$IFDEF TEST}
var
vArr: Variant;
I, J: Integer;
Sum: Integer;
begin
vArr := VarArrayCreate([0, 3, 0, 5], varVariant);
for I := 0 to 3 do
for J := 0 to 5 do
vArr[I, J] := I + J;
Sum := FastSumRange(vArr);
WriteLn(Sum);
ReadLn;
{$ENDIF}
end.
Đây là đoạn code mình code minh họa cho các bạn cách dùng Pointer, truy xuất trực tiếp tới từng memory của từng phần tử trong 1 array của Variant/xxx bất kỳ.
Dựa vào đây các bạn có thể độ chế lại theo yêu cầu của riêng mình.
Bao fast và vé ri vé ri nhanh nhé các bạn, vì tránh được _VarArrayGet và _VarArrayPut.
Bài đã được tự động gộp:
Mã ASM mà Delphi compiler sinh ra, sau khi qua decompiler ngược lại.
Các bạn thấy trong vòng lặp không còn _VarArrayGet và _VarArrayPut.
Chỉ là thao tác công và dịch con trỏ PLem lên sizeof(Variant) View attachment 268661
Các bạn chú ý dòng này, điểm quan trong:
Result := Result + PVarData(PElem).VInteger;
Nếu các bạn viết:
Result := Result + PElem^
Thì Delphi compiler sẽ sinh mã gọi các hàm internal để convert 1 giá trị Variant to kiểu của biến Result. Ở đây là Integer thì sẽ là _VarAsInteger, và linh tinh nữa, sẽ kéo tốc độ xuống.
Vì Variant là kiểu union TVarData nên mình ép kiểu pointer của PElem từ con trỏ PVariant với PVarData, lấy trực tiếp field của nó luôn.
Nên tránh luôn được các hàm internal mà Delphi compiler chèn vô.
ÍT ngày nữa rảnh Mạnh áp dụng vào hàm chuyển mảng kia xem tình hình sao mới biết được
Thử 1 cái Array có 10 triệu dòng x 50 cột xem sao có nhanh hơn hay ko ???
KKK, tui không có nói lý thuyết suông nhe bạn Mạnh. Nhớ đó. Tui hiểu chắc, sâu cái gì tui mới nói.
Để xem bạn áp dụng vào hàm TransArr của bạn có đúng không ?
Tui đang rảnh, có thể code, sửa ngay hàm đó cho bạn. Bạn code còn bug nhiều lắm.
Nhưng không, để cho bạn tự làm, rồi bạn sẽ hiểu ra nhiều, sâu hơn.
Tốt hơn cho bạn.
Mới xem lại code của mạnh 1 năm trước thì ra có sử dụng rồi .... ít ngày nữa xử lý xong công việc ... quậy bank xác các kiểu ra xem nó ra cái gì .... mạnh cảm ơn
Bạn nào iu thích delphi có thể tham khảo thêm link sau
Mình đo thử trên dữ liệu mình thử tạo là 1 triệu dòng, 5 cột, thì đáng buồn là không nhanh hơn được bao nhiêu.
Delphi 10.4.2 64bit, Excel 2016 64bit.
Chứng tỏ code VBA được compile và execute rất tốt.
Hàm FastSumRange mình sửa lại 1 chút, từ 2 vòng for thành 1 vòng, cải tiến thêm được 1 chút tốc độ (giảm bớt số lệnh, lần CPU nhảy)
Mã:
function FastSumRange(const arr: Variant): Double; stdcall;
var
I, Count: Integer;
LB1, LB2, UB1, UB2: Integer;
PElem: PVariant;
begin
Result := 0;
LB1 := VarArrayLowBound(arr, 1);
UB1 := VarArrayHighBound(arr, 1);
LB2 := VarArrayLowBound(arr, 2);
UB2 := VarArrayhighBound(arr, 2);
Count := (UB1 - LB1 + 1) * (UB2 - LB2 + 1);
PElem := VarArrayLock(arr);
try
for I := 0 to Count - 1 do
begin
Result := Result + PVarData(PElem).VDouble;
Inc(PElem);
end;
finally
VarArrayUnlock(arr);
end;
end;
Mình đo thử trên dữ liệu mình thử tạo là 1 triệu dòng, 5 cột, thì đáng buồn là không nhanh hơn được bao nhiêu.
Delphi 10.4.2 64bit, Excel 2016 64bit.
Chứng tỏ code VBA được compile và execute rất tốt.
Hàm FastSumRange mình sửa lại 1 chút, từ 2 vòng for thành 1 vòng, cải tiến thêm được 1 chút tốc độ (giảm bớt số lần nhảy)
Mã:
function FastSumRange(const arr: Variant): Double; stdcall;
var
I, Count: Integer;
LB1, LB2, UB1, UB2: Integer;
PElem: PVariant;
begin
Result := 0;
LB1 := VarArrayLowBound(arr, 1);
UB1 := VarArrayHighBound(arr, 1);
LB2 := VarArrayLowBound(arr, 2);
UB2 := VarArrayhighBound(arr, 2);
Count := (UB1 - LB1 + 1) * (UB2 - LB2 + 1);
PElem := VarArrayLock(arr);
try
for I := 0 to Count - 1 do
begin
Result := Result + PVarData(PElem).VDouble;
Inc(PElem);
end;
finally
VarArrayUnlock(arr);
end;
end;
Em có test thử hàm anh @ThangCuAnh chia sẻ thì em thử viết dll và COM đều lỗi khi sử dụng VarArrayLowBound(arr, 1);
TH: em gán mảng từ OleVariant và mảng thuần của delphi -> chạy code được nó bị ra giá trị 0, còn bỏ qua các bước sử dụng địa chỉ bộ nhớ thì lại chậm hơn vba.
Code delphi
Mã:
function FastSumRange(arr: Variant): Integer; stdcall;
var
I, Count: Integer;
LB1, LB2, UB1, UB2: Integer;
PElem: PVariant;
begin
Result := 0;
LB1 := VarArrayLowBound(arr, 1);
UB1 := VarArrayHighBound(arr, 1);
LB2 := VarArrayLowBound(arr, 2);
UB2 := VarArrayhighBound(arr, 2);
Count := (UB1 - LB1 + 1) * (UB2 - LB2 + 1);
PElem := VarArrayLock(arr);
try
for I := 0 to Count - 1 do
begin
Result := Result + PVarData(PElem).VInteger;
Inc(PElem);
end;
finally
VarArrayUnlock(arr);
end;
end;
exports
FastSumRange;
[/ICODE]
VBA
Mã:
#If VBA7 Then
Private Declare PtrSafe Function GetTickCount Lib "kernel32" () As Long
Private Declare PtrSafe Function FastSumRange Lib "BnAddin.dll" (ByVal Rng As Variant) As Long
#Else
Private Declare Function GetTickCount Lib "kernel32" () As Long
Private Declare Function FastSumRange Lib "BnAddin.dll" (ByVal Rng As Variant) As Long
#End If
Sub testFastSumRange2()
Dim t As Double
t = GetTickCount
'Dim Com As New BnAddIn.CoBnJson
Range("C1").Value2 = FastSumRange(Range("A1:A1000000"))
Range("D1").Value2 = GetTickCount - t
End Sub
Em có test thử hàm anh @ThangCuAnh chia sẻ thì em thử viết dll và COM đều lỗi khi sử dụng VarArrayLowBound(arr, 1);
TH: em gán mảng từ OleVariant và mảng thuần của delphi -> chạy code được nó bị ra giá trị 0, còn bỏ qua các bước sử dụng địa chỉ bộ nhớ thì lại chậm hơn vba. View attachment 268704
Code delphi
Mã:
function FastSumRange(arr: Variant): Integer; stdcall;
var
I, Count: Integer;
LB1, LB2, UB1, UB2: Integer;
PElem: PVariant;
begin
Result := 0;
LB1 := VarArrayLowBound(arr, 1);
UB1 := VarArrayHighBound(arr, 1);
LB2 := VarArrayLowBound(arr, 2);
UB2 := VarArrayhighBound(arr, 2);
Count := (UB1 - LB1 + 1) * (UB2 - LB2 + 1);
PElem := VarArrayLock(arr);
try
for I := 0 to Count - 1 do
begin
Result := Result + PVarData(PElem).VInteger;
Inc(PElem);
end;
finally
VarArrayUnlock(arr);
end;
end;
exports
FastSumRange;
[/ICODE]
VBA
Mã:
#If VBA7 Then
Private Declare PtrSafe Function GetTickCount Lib "kernel32" () As Long
Private Declare PtrSafe Function FastSumRange Lib "BnAddin.dll" (ByVal Rng As Variant) As Long
#Else
Private Declare Function GetTickCount Lib "kernel32" () As Long
Private Declare Function FastSumRange Lib "BnAddin.dll" (ByVal Rng As Variant) As Long
#End If
Sub testFastSumRange2()
Dim t As Double
t = GetTickCount
'Dim Com As New BnAddIn.CoBnJson
Range("C1").Value2 = FastSumRange(Range("A1:A1000000"))
Range("D1").Value2 = GetTickCount - t
End Sub
Option Explicit
Declare PtrSafe Function FastSumRange Lib "Test.dll" (ByRef arr As Variant) As Double
Declare PtrSafe Function LoadLibraryA Lib "kernel32.dll" (ByVal DllName As String) As LongPtr
Sub VBACode()
Dim lr As Long, arr As Variant, tong As Double, i As Long, j As Long
Dim sStart As Single, sEnd As Single
sStart = Timer
With Sheet1
lr = .Range("A1000000").End(3).Row
arr = .Range("A1:K" & lr).Value
End With
For i = LBound(arr) To UBound(arr)
For j = LBound(arr, 2) To UBound(arr, 2)
tong = tong + arr(i, j)
Next j
Next i
sEnd = Timer
MsgBox tong, , sEnd - sStart
End Sub
Sub DelphiCode()
Dim sStart As Single, sEnd As Single, lr As Long, arr As Variant, tong As Double
Dim hDll As LongPtr
hDll = LoadLibraryA("test.dll")
sStart = Timer
With Sheet1
lr = .Range("A1000000").End(3).Row
arr = .Range("A1:K" & lr).Value
End With
tong = FastSumRange(arr)
sEnd = Timer
MsgBox tong, , sEnd - sStart
End Sub
Bạn Bảo Ninh thiếu keyword const trong hàm FastSumRange nhé. Không tự ý bỏ const đi được đâu.
Option Explicit
Declare PtrSafe Function FastSumRange Lib "Test.dll" (ByRef arr As Variant) As Double
Declare PtrSafe Function LoadLibraryA Lib "kernel32.dll" (ByVal DllName As String) As LongPtr
Sub VBACode()
Dim lr As Long, arr As Variant, tong As Double, i As Long, j As Long
Dim sStart As Single, sEnd As Single
sStart = Timer
With Sheet1
lr = .Range("A1000000").End(3).Row
arr = .Range("A1:K" & lr).Value
End With
For i = LBound(arr) To UBound(arr)
For j = LBound(arr, 2) To UBound(arr, 2)
tong = tong + arr(i, j)
Next j
Next i
sEnd = Timer
MsgBox tong, , sEnd - sStart
End Sub
Sub DelphiCode()
Dim sStart As Single, sEnd As Single, lr As Long, arr As Variant, tong As Double
Dim hDll As LongPtr
hDll = LoadLibraryA("test.dll")
sStart = Timer
With Sheet1
lr = .Range("A1000000").End(3).Row
arr = .Range("A1:K" & lr).Value
End With
tong = FastSumRange(arr)
sEnd = Timer
MsgBox tong, , sEnd - sStart
End Sub
Bạn Bảo Ninh thiếu keyword const trong hàm FastSumRange nhé. Không tự ý bỏ const đi được đâu.
Mạnh thử Call nó từ COM thấy lần đầu chạy có vẻ chậm hơn tí teo .... lần 2 to n là o có nghĩa nhanh hơn lần đầu
đoán là COM mất cái khúc Load 1 tí
Mã:
Sub DelphiCode()
Dim sStart As Single, sEnd As Single
Dim lr As Long, arr As Variant, tong As Double
Dim aSum As New MyLibrary.VBLib
sStart = Timer
With Sheet1
lr = .Range("A1000000").End(3).Row
arr = .Range("A1:K" & lr).Value
End With
tong = aSum.SumRange(arr)
sEnd = Timer
MsgBox tong, , sEnd - sStart
End Sub
Mạnh cảm ơn ... ngày mát trời có thêm 1 Hàm vào mục tiện Ích COM class
Thử gõ trên Cells thấy chạy cũng thế
Mã:
Function FastSumRange(ByVal DataArray As Range) As Double
Dim Arr As Variant
Dim aSum As New MyLibrary.VBLib
Arr = DataArray.Value
FastSumRange = aSum.SumRange(Arr)
End Function
Ban ngày em hơi bận nên chưa có time test, cảm ơn các anh đã chỉ dẫn, em đã test sửa lại theo hướng dẫn của anh @Nguyễn Duy Tuân và anh @ThangCuAnh :
Phạm vi: 5,242,880 cells
TH1: Code dll theo cách của anh @ThangCuAnh -> 484 ms
TH2: truyền trực tiếp Arr xử lý tính toán Arr trong delphi -> 672 ms
TH3: truyền vào range -> trong delphi gán từ rng mới gán sang mảng 532ms
TH4: VBA lỗi OverFlow
TH5: Code COM theo cách của anh @ThangCuAnh -> 250ms
TH6: Code hàm SUM Excel truyền vào Range (không phải array) -> 31 ms
TH7: Code hàm SUM Excel truyền vào Arr được lấy từ range -> 293 ms
Em thấy hiệu suất cách viết của các anh chỉ thì hàm xử lý mảng đã xử lý ở mức độ rất tốt, nếu hàm SUM của excel không phải là tính theo tọa độ mà tính theo Array thì tốc độ gần như tương đương các anh chỉ rồi!
Em muốn hỏi chút các anh là nếu ta sử dụng PElem := VarArrayLock(Rng); -> returns a pointer to the data, vậy nếu em muốn tìm tọa độ chính xác PElem tương tự như PElem[I,J] thì có cách nào không ạ
Mã:
Option Explicit
Public comBnFunction As New BnAddIn.coBNSQLFunction
Public comBnJson As New BnAddIn.CoBnJson
Private Declare PtrSafe Function GetTickCount Lib "kernel32" () As Long
Declare Function FastSumRange_TH1 Lib "BnAddin.dll" (ByRef Arr As Variant) As Double
Declare Function FastSumRange_TH2 Lib "BnAddin.dll" (ByRef Arr As Variant) As Double
Declare Function FastSumRange_TH3 Lib "BnAddin.dll" (ByRef Arr As Variant) As Double
Sub DelphiTestSum_Excel()
Dim t As Double
t = GetTickCount
Dim Arr
Arr = Range("A1:E1048576").Value2
Range("G6").Value2 = WorksheetFunction.Sum(Arr)
Range("H6").Value2 = GetTickCount - t
End Sub
Sub DelphiTestSumCOM()
Dim t As Double
t = GetTickCount
Dim Arr As Variant
Arr = Range("A1:E1048576").Value2
Range("G5").Value2 = comBnJson.BN_FastSumRange(Arr)
Range("H5").Value2 = GetTickCount - t
End Sub
Sub DelphiTestSum_TH1()
Dim t As Double
t = GetTickCount
'Dim Com As New BnAddIn.CoBnJson
Dim Arr As Variant
Arr = Range("A1:E1048576")
Range("G1").Value2 = FastSumRange_TH1(Arr)
Range("H1").Value2 = GetTickCount - t
End Sub
Sub DelphiTestSum_TH2()
Dim t As Double
t = GetTickCount
Dim Arr As Variant
Arr = Range("A1:E1048576")
Range("G2").Value2 = FastSumRange_TH2(Arr)
Range("H2").Value2 = GetTickCount - t
End Sub
Sub DelphiTestSum_TH3()
Dim t As Double
t = GetTickCount
Range("G3").Value2 = FastSumRange_TH3(Range("A1:E1048576"))
Range("H3").Value2 = GetTickCount - t
End Sub
Sub TestFastSumRangeVBA()
Dim t As Double
t = GetTickCount
Dim Arr
Arr = Range("A1:A1048576").Value2
Dim i, j As Long
Dim LB1, LB2, UB1, UB2, KQ As Long
LB1 = LBound(Arr, 1)
UB1 = UBound(Arr, 1)
LB2 = LBound(Arr, 2)
UB2 = UBound(Arr, 2)
For i = LB1 To UB1
For j = LB2 To UB2
KQ = KQ + Arr(i, j)
Next j
Next i
Range("G4").Value2 = KQ
Range("H4").Value2 = GetTickCount - t
End Sub
Bản thân code FastSumRange đã nhanh hết mức có thể rồi, chỉ còn cách dùng multithread/parallel. Nhưng overhead cho sinh thread cũng rất lớn.
Nên chỉ dùng cho các vùng data thật lớn.
Overhead ở đây là do VBA load dll và DllFunctionCall gọi hàm FastSumRange rất lớn, tốn nhiều time.
Nên ở VBA code của mình, mình đã force nó LoadLibraryA trước rồi mới bắt đầu timer.
Bạn Mạnh nên chú ý điểm này, prototype của hàm TransArr là return 1 variant array.
Tức là VBA code sẽ sinh code để copy cái array dll bạn trả về vào biến của VBA. Sẽ take time rất lớn ở đây, nên đo không chính xác.
Mã:
Dim arr1 as Variant, arr2 as Variant
.....
arr1 = TransArr(arr2)
Sẽ take time rất lớn ở phép gán arr1 =
Bạn nên sữa prototype của TransArr lại
VD: function TransArr(const arrSrc: Variant; var arrDst: Variant): Boolean;
Trans thẳng từ arrSrc vào arrDst luôn
Bản thân code FastSumRange đã nhanh hết mức có thể rồi, chỉ còn cách dùng multithread/parallel. Nhưng overhead cho sinh thread cũng rất lớn.
Nên chỉ dùng cho các vùng data thật lớn.
Overhead ở đây là do VBA load dll và DllFunctionCall gọi hàm FastSumRange rất lớn, tốn nhiều time.
Nên ở VBA code của mình, mình đã force nó LoadLibraryA trước rồi mới bắt đầu timer.
Bạn Mạnh nên chú ý điểm này, prototype của hàm TransArr là return 1 variant array.
Tức là VBA code sẽ sinh code để copy cái array dll bạn trả về vào biến của VBA. Sẽ take time rất lớn ở đây, nên đo không chính xác.
Mã:
Dim arr1 as Variant, arr2 as Variant
.....
arr1 = TransArr(arr2)
Sẽ take time rất lớn ở phép gán arr1 =
Bạn nên sữa prototype của TransArr lại
VD: function TransArr(const arrSrc: Variant; var arrDst: Variant): Boolean;
Trans thẳng từ arrSrc vào arrDst luôn
Em muốn hỏi chút các anh là nếu ta sử dụng PElem := VarArrayLock(Rng); -> returns a pointer to the data, vậy nếu em muốn tìm tọa độ chính xác PElem tương tự như PElem[I,J] thì có cách nào không ạ
Bản thân code FastSumRange đã nhanh hết mức có thể rồi, chỉ còn cách dùng multithread/parallel. Nhưng overhead cho sinh thread cũng rất lớn.
Nên chỉ dùng cho các vùng data thật lớn.
Overhead ở đây là do VBA load dll và DllFunctionCall gọi hàm FastSumRange rất lớn, tốn nhiều time.
Nên ở VBA code của mình, mình đã force nó LoadLibraryA trước rồi mới bắt đầu timer.
Bạn Mạnh nên chú ý điểm này, prototype của hàm TransArr là return 1 variant array.
Tức là VBA code sẽ sinh code để copy cái array dll bạn trả về vào biến của VBA. Sẽ take time rất lớn ở đây, nên đo không chính xác.
Mã:
Dim arr1 as Variant, arr2 as Variant
.....
arr1 = TransArr(arr2)
Sẽ take time rất lớn ở phép gán arr1 =
Bạn nên sữa prototype của TransArr lại
VD: function TransArr(const arrSrc: Variant; var arrDst: Variant): Boolean;
Trans thẳng từ arrSrc vào arrDst luôn
Rảnh code dùm cho mạnh cái hàm kia TransArr ... loay hoay nguyên tối qua tới sáng nay chưa ra
Code đó mức độ rất khó rồi ... vượt ngoài tầm hiểu + xử lý của Mạnh
Xin cảm ơn
Em muốn hỏi chút các anh là nếu ta sử dụng PElem := VarArrayLock(Rng); -> returns a pointer to the data, vậy nếu em muốn tìm tọa độ chính xác PElem tương tự như PElem[I,J] thì có cách nào không ạ
Seek con trỏ đó em. Mãng 2 chiều thôi. Hàng và cột.
Vd: mãng arr[A..B, C..D] of Variant;
PElem: Pointer;
PElem := @arr[A, C]; // tới đầu vùng nhớ của arr, phần tử đầu tiên[A, C]
Thì @arr[I, J] = @arr[A, C] + ((D - C + 1) * I + J) * sizeof(Variant)
Mạnh post file Excel và code VBA lên để mình code hàm TransArr thử xem tốc độ Delphi so với VBA lần này ra sao !!!???
Seek con trỏ đó em. Mãng 2 chiều thôi. Hàng và cột.
Vd: mãng arr[A..B, C..D] of Variant;
PElem: Pointer;
PElem := @arr[A, C]; // tới đầu vùng nhớ của arr, phần tử đầu tiên[A, C]
Thì @arr[I, J] = @arr[A, C] + ((D - C + 1) * I + J) * sizeof(Variant)
Mạnh post file Excel và code VBA lên để mình code hàm TransArr thử xem tốc độ Delphi so với VBA lần này ra sao !!!???
Private Sub Transpose_Data()
Dim tmpArray() As Variant
Dim tmpArray2() As Variant
Dim Cnn As Object, Rs As Object
Dim strCon As String
Dim ExcelPath As String
Dim srtQry As String
Rem ========== Khai bao mo ket noi
Set Cnn = CreateObject("ADODB.Connection")
Set Rs = CreateObject("ADODB.Recordset")
ExcelPath = ThisWorkbook.Path & "\Data.xlsb"
strCon = ("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & ExcelPath _
& ";Extended Properties=""Excel 12.0 Xml;HDR=Yes;"";")
Rem ========== Tuy chon Lay du lieu SQL
Rem srtQry = "Select *" & "From [" & Data_Nhap$ & "]"
srtQry = "SELECT * FROM [Data_Nhap$]"
Cnn.Open strCon
Set Rs = Cnn.Execute(srtQry)
tmpArray = Rs.GetRows
Cnn.Close
Rem ========== Thuc hien chuyen mang 2dArray len Sheet
Call Transpose_Array(tmpArray, tmpArray2) ''Chuyen Mang tmpArray Sang tmpArray2
With Sheets("ADO").Range("A2")
.Resize(UBound(tmpArray2, 1), UBound(tmpArray2, 2)).ClearContents
.Resize(UBound(tmpArray2, 1), UBound(tmpArray2, 2)).Value = tmpArray2
End With
End Sub
Private Sub Transpose_Array(ByRef InputArr() As Variant, ByRef ReturnArray() As Variant)
Rem Khai bao Dim tmpArray(), tmpArray2() As Variant ''Tang Toc 50%
Rem Su Dung Call Transpose_Array (tmpArray, tmpArray2) ''Chuyen Mang tmpArray Sang tmpArray2
Dim RowNdx As Long, ColNdx As Long
Dim LB1 As Long, LB2 As Long
Dim UB1 As Long, UB2 As Long
LB1 = LBound(InputArr, 1)
LB2 = LBound(InputArr, 2)
UB1 = UBound(InputArr, 1)
UB2 = UBound(InputArr, 2)
ReDim ReturnArray(LB2 To UB2, LB1 To UB1)
For RowNdx = LB2 To UB2
For ColNdx = LB1 To UB1
ReturnArray(RowNdx, ColNdx) = InputArr(ColNdx, RowNdx)
Next ColNdx, RowNdx ''Viet gon lai Bo Next
Erase InputArr
End Sub
Tôi code DLL trong Delphi theo hướng giải quyết khác với anh "ThangCuAnh" và kết quả DLL tôi viết trong Delphi nhanh hơn VBA. Các bạn thử nghiệm trên máy tính của các bạn rồi cho kết quả nhé. Sự so sánh có thể khác nhau giữa các Office 32 hay 64-bit.
"tuanfastcode.dll" là thư viện lập trình bằng Delphi, xuất các hàm APIs gồm
1. CopyArray: Copy hai mảng 2D với nhau
2. TransArray: Đảo chiều mảng 2D
3. FastSumArray: Tổng trong mảng 2D
Để chạy các hàm này cần copy thư viện như sau:
+ Nếu Windows 64 bit
Copy x86\tuanfastcode.dll vào C:\Windows\SysWow64\
Copy x64\tuanfastcode.dll vào C:\Windows\System32\
+ Nếu Windows 32 bit
Copy x86\tuanfastcode.dll vào C:\Windows\System32\
So sánh hàm viết trong DLL này với cách viết tương tự trong VBA.
Chạy mở file "TestTuanFastCode.xlsm", vào VBA chạy code để so sánh.
Tôi đã test với Windows 64-bit, Office365 32-bit
Tốc độ các hàm tôi viết trong Delphi đều nhanh hơn VBA, trong đó hàm FastSumArray nhanh gấp 4 lần. Các bạn có thể test để xem kết quả ra sao.